23 de abril de 2010

CakePHP com conexão segura ao MySQL

Postado em: Desenvolvimento por Jonas Alves e herbertt

No projeto de um de nossos clientes, surgiu a necessidade de configurar uma conexão segura a um servidor MySQL para garantir a segurança dos dados, pois a conexão seria através da internet.

Consideramos algumas opções, entre elas:

  • Túnel SSH
  • VPN
  • Criptografia nativa do MySQL via SSL

Após alguns testes, concluímos que a criptografia nativa do MySQL via SSL é simples de ser configurada e garante a segurança dos dados, além de não precisar de um utilitário externo (SSH, OpenVPN, etc).

No PHP, os dois drivers nativos para MySQL – mysql e mysqli – suportam a conexão criptografada. Basta usar o método Mysqli::ssl_set:

$connection->ssl_set($key,$cert,$ca,$capath,$cipher);

Porém, descobrimos que os conectores para MySQL do CakePHP (1.2, 1.3 e 2.0) não tem essa funcionalidade. Decidimos então implementar um novo conector baseado no original.

Adaptando o conector MySQL do CakePHP

Criamos o conector DboMysqliSsl, que extende DboMysqli, em cake/libs/model/datasources/dbo/dbo_mysqli_ssl.php e sobrescrevemos o método connect, responsável por estabelecer a conexão. O trecho destacado é a parte que difere da superclasse:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
<?php
App::import('Core', 'DboMysqli');
class DboMysqliSsl extends DboMysqli {
  function connect() {
    $config = $this->config;
    $this->connected = false;

    if (is_numeric($config['port'])) {
      $config['socket'] = null;
    } else {
      $config['socket'] = $config['port'];
      $config['port'] = null;
    }

    $this->connection = new mysqli();
    $this->connection->init();

    if (isset($config['ssl'])) {
      @$key = $config['ssl']['key'];
      @$cert = $config['ssl']['cert'];
      @$ca = $config['ssl']['ca'];
      @$capath = $config['ssl']['capath'];
      @$cipher = $config['ssl']['cipher'];
     
      $this->connection->ssl_set($key,$cert,$ca,$capath,$cipher);
    }
   
    @$flags = $config['flags'];
    $this->connected = $this->connection->real_connect($config['host'], $config['login'],$config['password'], $config['database'], $config['port'], $config['socket'], $flags);
   
    $this->_useAlias = (bool)version_compare(mysqli_get_server_info($this->connection), "4.1", ">=");

    if (!empty($config['encoding'])) {
      $this->setEncoding($config['encoding']);
    }
    return $this->connected;
  }
}
?>

Configurando sua conexão (database.php)

Agora, para que sua aplicação CakePHP consiga se conectar a um servidor MySQL com SSL, basta adicionar as seguintes configurações na sua conexão:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
array(
  'driver' => 'mysqli_ssl',
  'persistent' => false,
  'host' => '127.0.0.1',
  'login' => 'userssl',
  'password' => 'password',
  'database' => 'meu_sistema',
  'prefix' => '',
  'encoding'=>'utf8',
  'flags' => MYSQL_CLIENT_SSL,
  'ssl' => array(
    'key'    => '/path/to/meu_sistema/app/config/mysql_ssl/server-key.pem',
    'cert'   => '/path/to/meu_sistema/app/config/mysql_ssl/server-cert.pem',
    'ca'     => '/path/to/meu_sistema/app/config/mysql_ssl/ca-cert.pem',
    'capath' => null,
    'cipher' => null
  )
)

Como a configuração sugere, salve os arquivos indicados em um lugar acessível pela sua aplicação – no exemplo usamos /path/to/meu_sistema/app/config/mysql_ssl.

Configurando o MySQL para aceitar (ou exigir) conexões criptografadas

Para seu MySQL aceitar conexões criptografadas você terá que, primeiro, gerar os certificados conforme o tópico “Example 1: Creating SSL files from the command line on Unix” desta página do manual do MySQL.

Em seguida, você deve indicar esses certificados no arquivo de configuração do mysql (/etc/mysql/my.cnf no Debian e Ubuntu). Essa configuração já existe por volta da linha 118. Descomente essas linhas e indique o caminho dos seus certificados:

ssl-ca=/etc/mysql/cacert.pem
ssl-cert=/etc/mysql/server-cert.pem
ssl-key=/etc/mysql/server-key.pem

Reinicie seu MySQL:

sudo /etc/init.d/mysql restart

Agora, se você executar a seguinte query no MySQL:

mysql> SHOW VARIABLES LIKE '%ssl%';

Você verá que o SSL está ativado.

Se você quiser exigir o uso do certificado para que um usuários consiga se conectar no seu servidor MySQL, você deve alterar a permissão do usuário, adicionando a opção REQUIRE SSL, conforme o exemplo:

GRANT ALL PRIVILEGES ON *.* TO 'userssl'@'%' IDENTIFIED BY 'password' REQUIRE SSL;

Pronto, agora você tem uma conexão segura entre sua aplicação CakePHP e seu banco de dados MySQL.

Para acessar seu MySQL com SSL via linha de comando, use os seguintes parâmetros:

mysql -h127.0.0.1 -uuserssl -p --ssl-ca=ca-cert.pem --ssl-cert=client-cert.pem --ssl-key=client-key.pem

Open Source

Essa modificação foi sugerida para ser incorporada ao framework no Lighthouse do CakePHP.

2 Comentários

  1. Clodoaldo Bragato Lopes disse:

    Muito legal o post de vocês. Também achei legal a iniciativa de extender o framework para atender a necessidade do cliente. Isso demonstra que não vale a pena ficar parado diante de um problema, mas sim deve-se atacá-lo com o conhecimento necessário.
    Parabéns

  2. [...] This post was mentioned on Twitter by Jonas Alves and Flavio Logullo, webgoal. webgoal said: Novo Post no Blog da WebGoal – CakePHP com conexão segura ao MySQL por Jonas Alves e Herbertt Bamonde. http://bit.ly/bj3OsN [...]

Deixe seu comentário