MeuMySQL Blog is now at http://blog.mdnsolutions.com/.

Wednesday, November 23, 2011

MySQL - Checando a segurança após instalação


Olá galera, hoje nosso tópico é curto mas muito importante, vamos falar sobre algumas medidas de segurança que devemos tomar uma vez que instalamos o MySQL em nosso servidor.

Estou usando o Ubuntu 11.04 e MySQL 5.1.54 e acabei de fazer a instalação do meu serviço aqui (você pode checar como fazer isso aqui). Bem, agora temos algumas medidas de segurança para tomar de forma a proteger nossa base de dados. Inicialmente o MySQL cadastra usuarios anônimos em suas tabelas de grant, mas ele também tem ferramentas que te auxiliam no processo de aperfeiçoamento da segurança do banco.


Usuários anônimos e segurança

Podemos checar que usuários possuimos em nossa base inicialmente com o comando abaixo:

  1. mysql> SELECT user, host,  password from mysql.user;
  2. +--------+-----------+-----------------------------------+
  3. | user   | host      | password                          |
  4. +--------+-----------+-----------------------------------+
  5. | root   | localhost | *57000000000000000000000000000001 |
  6. | root   | 127.0.0.1 | *57000000000000000000000000000001 |
  7. | root   | ::1       | *57000000000000000000000000000001 |
  8. |        | localhost |                                   |
  9. +--------+-----------+-----------------------------------+
  10. 7 rows in set (0.00 sec)


Note que temos 2 usuários anônimos em nossa base. Poderiamos simplesmente apagar este usuário usando um DELETE FROM mysql.user..., mas como manda a documentação temos commandos específicos e utilitários que já fazem essa tarefa de forma limpa e segura do ponto de vista do SGDB.

Em nosso caso aqui iremos trabalhar com o utilitário mysql_secure_installation. (Lembrando que esse comando é válido apenas para o linux). Veja abaixo:

  1. shell> mysql_secure_installation
  2. NOTE: RUNNING ALL PARTS OF THIS SCRIPT IS RECOMMENDED FOR ALL MySQL       SERVERS IN PRODUCTION USE! PLEASE READ EACH STEP CAREFULLY!
  3. In order to log into MySQL to secure it, we'll need the current
  4. password for the root user.  If you've just installed MySQL, and you haven't set the root password yet, the password will be blank, so you should just press enter here.
  5. Enter current password for root (enter for none):


Uma vez digitado o password que você definiu para o seu root inicialmente (você ainda lembra né?) ele vai te guiar em um intuitivo passo a passo para tornar sua base mais segura. Entre eles te perguntar sobre a remoção dos usuário anônimos, desabilitar conexões remotas, remoção do banco de dados teste (uma vez que essa banco permite acessos por qualquer usuário) e finalmente recarregar todos os privilégios para que as alterações tenham efeito.


  1. OK, successfully used password, moving on...
  2. Setting the root password ensures that nobody can log into the MySQL
  3. root user without the proper authorisation.
  4. You already have a root password set, so you can safely answer 'n'.
  5. Change the root password[Y/n] Y
  6. Password updated successfully!
  7. Reloading privilege tables..
  8.  ... Success!
  9. Remove anonymous users? [Y/n] Y
  10.  ... Success!
  11. Disallow root login remotely? [Y/n] Y
  12.  ... skipping.
  13. Remove test database and access to it? [Y/n] Y
  14.  ... skipping.
  15. Reload privilege tables now[Y/n] Y
  16.  ... Success!
  17. Cleaning up...
  18. All done!  If you've completed all of the above steps, your MySQL
  19. installation should now be secure.
  20. Thanks for using MySQL!


Bem, após isto você ainda pode fazer algumas importantes verificações:

Tenha certeza que todas as suas contas tem senha, sua query abaixo tem que retornar vazia:

  1. mysql> SELECT Host, User FROM user WHERE Password = '';
  2. Empty set (0.00 sec)

Periodicamente procure por senhas duplicadas:

mysql> SELECT User from mysql.user GROUP BY password HAVING count(user) > 1;

Fique alerta quanto a hosts com curingas. Periodicamente faça o teste:

mysql> SELECT User, Host FROM mysql.user WHERE Host LIKE '%\%%';

Com esse passo a passo seu banco de dados já está mais seguro contra invasões e usuários não autorizados. Em breve trarei mais tópicos de como melhorar sua segurança.

Viva ao meu, seu, nosso MySQL! Até mais!



Tuesday, November 1, 2011

Identificadores no MySQL

Olá Pessoal. Vamos falar nesse artigo sobre Identificadores no MySQL, algo extremanente importante em nosso cotidiano com o banco de dados, mas que por vezes não entendemos bem o funcionamento.

Quando escrevemos requisições SQL,  usamos nomes para se referir a banco de dados e outros objetos contidos no mesmo, tais como tabelas, stored routines, triggers entre outros. Alguns destes objetos tem em seus componentes nomes próprios. Por exemplo, tabelas em suas colunas e índices. Ou ainda é possível criar 'apelidos' (Alias), pelo quais atuam como sinônimos para nomes de tabelas e colunas.

Sintaxe

Identificadores podem estar ou não entre acentos grave (`). Se não estiver deve seguir algumas regras:

  • Ele pode conter qualquer caracter alfanumerico, o (_) e o sinal de dolar $;
  • O identificador pode iniciar com qualquer caracter citado acima, inclusive números. Contudo, é melhor evitar identificadores que podem ser entendidos como constantes. Por exemplo 1e3 pode ser considerado um numero de notação cientifica ou 0x1 como uma contante hexadecimal. Portanto nenhum dos dois são bons candidatos à identificadores.
  • Um indentificador não pode ser apenas números.
Um identificador estando entre acentos agudos pode conter espaços, aspas simples ou duplas. Caso o SQL mode esteja habilitado em ANSI_QUOTES você também pode usar as aspas duplas ("). Usando desta forma há algumas regras para serem seguidas:
  • Em geral, qualquer caracter pode ser usado em um identificador entre aspas. Excessões neste caso são que um identificador não pode conter um byte com um valor numero 0 ou 255, e os nomes de banco de dados e tabelas não podem conter '.', '/' ou '\'.
  • Um identificador entre aspas pode conter apenas números.
  • Um identificador pode incluir qualquer palavra, mas caso esta seja uma palavra reservada (tais como SELECT ou DESC), conter caractes especiais, ou ser apenas números ele deve ser obrigatoriamente adicionado entre aspas ou acento grave.
Caso você não esteja certo se o seu identificador é ou não uma palavra reservada coloque ele entre acentos grave.


Case Sensitivity de Identificadores

Uma caracteristica que afeta como usamos identificadores é se eles são case sensitive. Alguns podem ser e outros não. As regras que determinam essas caracteristicas dependem que tipo de identificador estamos lidando:
  • Para banco de dados e tabelas o case sensitivity dependem do sistema operacional e do sistem de arquivos do servidor. Ou ainda da variável de sistema lower_case_table_names. Bancos de dados ou tabelas são representados por diretorios e arquivos, então se o sistema operacional não é case sensitive os identificadores também não são.
    O Windows não é case sensitive, porém a maioria dos sistemas Unix são. Contudo, se a a variável de sistema lower_case_table_names estiver setada para 1 ou 2, identificadores de banco de dados e tabelas são usados no estilo case-insensitive em declarações SQL. Caso deseje usar esssa variavel, você deve seta-la antes de começar a criar seus bancos de dados e tabelas.
    Independente das propriedades do seu sistema de arquivos, identificadores de bancos de dados e tabelas devem ser escritos de forma consistente com o mesmo padrão em toda declaração SQL.
  • Identificadores para colunas, índices, stored routines e triggers não sao case sensitive.
  • 'Alias' para colunas não sao case sensitive.

Utilizando 'Nomes Qualificados'

Identificadores de colunas e tabelas podem ser escritas de forma qualificada - isto quer dizer, junto com o identificador de um nivel superior utilizando o ponto '.' como separador. Algumas vezes estes qualificadores são necessários para resolver problemas de ambiguidade. Outras vezes você pode eleger seu uso para tornar uma declaração mais simples ou precisa.

Uma tabela pode ser qualificada com o nome do banco de dados que ela pertence. Ambos os casos trazem o mesmo resultado:

SELECT * FROM tb1;
SELECT * FROM db1.tb1;

Uma coluna também pode ser qualificada com o nome da tabela à que pertence. E como a tabela também pode ser qualificada podemos ter até 3 formas diferentes de escrever a mesma query:

SELECT coluna1 FROM tb1;
SELECT tb1.coluna1 FROM tb1;
SELECT db1.tb1.coluna1 FROM db1.tb1;

Store routines e triggers também podem  ser referenciados em uma forma qualificada. No primeiro caso a qualificação é pelo banco de dados à que pertence. Um trigger é associado com uma tabela, neste caso ele deve ser associado com a mesma (table_trigger.trigger_name).

Em identificadores entre aspas em nomes qualificados use eles separados. Por exemplo: db1.tb1 como `db1`.`tb1` e não `db1.tb1`.


Usando Palavras Reservadas como Identificadores

Palavras reservadas (Reserved Words) são especiais. Por exemplo, nome de funções não podem ser usadas como identificadores de tabelas ou colunas, e caso tente mensagens de erros podem ser emitidas. A declaração abaixo é um exemplo claro do uso errôneo:

mysql> CREATE TABLE t (order INT NOT NULL UNIQUE, d DATE NOT NULL);
ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'order INT NOT NULL UNIQUE, d DATE NOT NULL)' at line 1

Igualmente, a outra query abaixo retornará a mesma situação:

mysql> SELECT 1 AS INTEGER;
ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'INTEGER' at line 1

A solução para estes problemas é colocar os identificadores entre aspas / acentos agudos. As regras desse encapsulamento do identificador dependem do seu tipo:
  •  Para utilizar uma palavra reservada como banco de dados, tabela, coluna ou indentificar um índice há um ou dois estilos para o encapsulamento, dependendo do SQL mode do servidor. Por padrão, palavras reservadas utilizando acento grave "`" podem ser usadas como identificadores:
mysql> CREATE TABLE t (`order` INT NOT NULL UNIQUE, d DATE NOT NULL);
Query OK, 0 rows affected (0.00 sec)

Se o ANSI_QUOTES no SQL mode estiver habilitado você pode escrever a declaração da usando aspas duplas:

mysql> CREATE TABLE t ("order" INT NOT NULL UNIQUE, d DATE NOT NULL);

Query OK, 0 rows affected (0.00 sec)
Se é necessário encapsular um identificador para sua criação, em todas as declarações envolvendo ele também será necessário executar o mesmo procedimento.
  • Pode-se usar uma palavra reservada como um 'Alias' usando aspas simples, duplas ou acento agudo. O SQL mode não faz diferença, uma vez que é legal utilizar qualquer uma das três formas. Portanto, para usar INTEGER como um 'alias' você pode escrever em todos os modos abaixo:

SELECT 1 AS 'INTEGER';SELECT 1 AS `INTEGER`;SELECT 1 AS "INTEGER";
É uma boa ideia evitar nomes de funções como identificadores. Normalmente, eles não são reservados, contudo há circunstancias pelo qual isto pode não ser verdade:
  • Algumas funções tem nomes que também são palavras chaves 'keywords' e portanto reservadas. CHAR() é um exemplo.
  • Por padrão, um nome de função seguido de um parenteses aberto deve ser escrito sem espaços entre eles. Isso permite ao statement parser do MySQL distinguir um nome em uma função do mesmo para outro propósito, tal como um identificador.
Contudo, se o IGNORE_SPACE SQL mode estiver habilitado, o servidor permite espaços entre o nome da função e seu parenteses de abertura "(". Rodando o servidor desse modo o nome de algumas funções passam a ser ambinguas em alguns contextos, uma vez que o statement parser não consegue mais distinguir seguramente se um nome representa uma função ou um identificador.
veja abaixo:

mysql> INSERT INTO COUNT (id) VALUES(43);

EM IGNORE_SPACE mode, esta declaração pode significar "criar uma nova linha na tabela COUNT, setando a coluna id com 43", ou pode significar uma falha de sintaxe na declaração INSERT que tem uma chamada da função COUNT onde o nome da tabela deveria estar. O parse não consegue distinguir.

Outra observação, palavras reservadas não são case sensitive. Elas pode ser escritas em maíusculo, minusculo ou ainda com ambos. Elas também não precisam ser escritas da mesma forma por toda a query. O mesmo também é verdadeiro para nome de funções.