Upgrade to Pro — share decks privately, control downloads, hide ads and more …

MySQL melhores práticas de segurança

Sponsored · Your Podcast. Everywhere. Effortlessly. Share. Educate. Inspire. Entertain. You do you. We'll handle the rest.

MySQL melhores práticas de segurança

Palestra de melhores práticas de segurança em MySQL ministrada na universidade Feevale Novo Hamburgo - Software Freedom Day 2017

Avatar for Marcelo Altmann

Marcelo Altmann

September 16, 2017
Tweet

More Decks by Marcelo Altmann

Other Decks in Technology

Transcript

  1. © 2017 Percona 1 Marcelo Altmann MySQL: Melhores Práticas de

    Segurança Senior Support Engineer Dia da Liberdade do Software - Novo Hamburgo - RS 16/09/2017
  2. © 2017 Percona 3 Agenda ▪ Intro ▪ Segurança no

    SO ▪ Segurança na aplicação ▪ Setup Inicial ▪ SSL ▪ ACL ▪ Passwords ▪ Connection Control ▪ TDE ▪Audit Plugin ▪Proxy / Firewall
  3. © 2017 Percona 5 Marcelo Altmann ▪Engenheiro de Suporte Senior

    @ percona • MySQL DBA @ IEDR (CCTLD Irlanda) ▪Oracle ACE Associate ▪Certificaçoes •Oracle Certified Professional, MySQL 5.6 Database Administrator •Oracle Certified Professional, MySQL 5.6 Developer •Oracle Certified Professional, MySQL 5 Database Administrator •Oracle Certified Professional, MySQL 5 Developer •Oracle Certified Associate, MySQL 5.0/5.1/5.5 ▪blog.marceloaltmann.com
  4. © 2017 Percona 7 Intro ▪ Se eles querem os

    teus dados, eles vão ter. Tudo é uma questão de tempo, recursos, dinheiro e empenho ▪ ELES - Hackers, Competidores, Ex-funcinários, NSA. ▪ Segurança tem um preço •Dinheiro •Performance •Usabilidade
  5. © 2017 Percona 8 Intro - principios básicos ▪ Bloqueio

    por padrão ▪ Mínimo acesso possível ▪ Minimizar possibilidades ▪ Isolar ▪ Auditar
  6. © 2017 Percona 10 Segurança no SO ▪ Desinstalar serviços

    não utilizados ▪ Não rode compiladores ▪ Firewall - De preferencia externo ▪Desligar outbound para internet ▪Mater o sistema sempre atualizado ▪Adicionar KEY no SSH ▪Não habilitar root logar remoto
  7. © 2017 Percona 11 Segurança no SO ▪SELinux •grep mysqld

    /var/log/audit/audit.log | grep denied ▪yum install policycoreutils-python •grep mysqld /var/log/audit/audit.log | audit2allow •grep mysqld /var/log/audit/audit.log | audit2why
  8. © 2017 Percona 12 Segurança no SO ▪Alterar datadir •chcon

    -R -t mysqld_db_t /mysql/ •semanage fcontext -a -t mysqld_db_t "/mysql(/.*)?" •restorecon -Rv /mysql/ ▪Alterar porta •semanage port -a -t mysqld_port_t -p tcp 3307
  9. © 2017 Percona 14 Segurança na aplicação ▪Validar todos inputs

    ▪Não estorne informações (recuperar senha / login) ▪Não conecte como root. ▪Cada app deve ter seu usuário ▪Criptografe informações sensitivas •AES_ENCRYPT() e AES_DECRYPT() •SHA1() e MD5()
  10. © 2017 Percona 15 Segurança na aplicação ▪SQL Injection •$query

    = “SELECT * FROM users WHERE name = ‘“ . $name .”’” •$name = marcelo’ OR ‘a’=’a •SELECT * FROM users WHERE name = ‘marcelo’ OR ‘a’=’a’; ▪Prepared Statements •$stm = $db->prepare(“SELECT * FROM users WHERE name = ?”) •$stm->bind_param(“s”, $name) •$stm->execute()
  11. © 2017 Percona 17 Setup inicial ▪Conta anônima e test

    database ▪Conectar sem usuário/senha ▪Todos os privilégios no banco test
  12. © 2017 Percona 18 Setup inicial mysql> CREATE TABLE tb1

    ( ID INT AUTO_INCREMENT PRIMARY KEY, a bigint); Query OK, 0 rows affected (0.05 sec) mysql> INSERT INTO tb1 VALUES (NULL, RAND(99999)); mysql> INSERT INTO tb1 SELECT NULL, RAND(99999) FROM tb1; mysql> INSERT INTO tb1 SELECT NULL, RAND(99999) FROM tb1; mysql> INSERT INTO tb1 SELECT NULL, RAND(99999) FROM tb1; mysql> INSERT INTO tb1 SELECT NULL, RAND(99999) FROM tb1; mysql> INSERT INTO tb1 SELECT NULL, RAND(99999) FROM tb1;
  13. © 2017 Percona 19 Setup inicial ▪Conta anônima / uso

    de memória mysql> use information_schema; mysql> SELECT * FROM COLLATIONS a, COLLATIONS b, COLLATIONS c, COLLATIONS d, COLLATIONS e, COLLATIONS f, COLLATIONS g; Killed > dmesg [ 7392.378967] Out of memory: Kill process 30278 (mysql) score 702 or sacrifice child [ 7392.380223] Killed process 30278 (mysql) total-vm:1662332kB, anon- rss:424576kB, file-rss:0kB, shmem-rss:0kB
  14. © 2017 Percona 20 Setup inicial ▪mysql_secure_installation •DROP DATABASE test;

    •DROP USER ''@'localhost'; •DROP USER ''@'HOSTNAME'; • SET PASSWORD FOR root@IP
  15. © 2017 Percona 22 SSL ▪Por padrão informações trafegam abertas

    ▪SSL criptografa os dados trafegados ▪Padrão 5.7 ▪Gerar certificados •5.7 - mysql_ssl_rsa_setup •5.6 - openssl - https://goo.gl/5T7zqF
  16. © 2017 Percona 23 SSL ▪Instalação [mysqld] ssl-ca=ca.pem ssl-cert=server-cert.pem ssl-key=server-key.pem

    ▪Conectar mysql --ssl-ca=ca.pem \ --ssl-cert=client-cert.pem \ --ssl-key=client-key.pem
  17. © 2017 Percona 24 SSL ▪Forcar usuário a usar SSL

    CREATE USER 'marcelo'@'localhost' REQUIRE SSL; ▪Forçar todos os usuários a usarem uma conexão segura •Socket / namedpipe •SSL [mysqld] require_secure_transport
  18. © 2017 Percona 26 ACL ▪GRANT [privilégio] ON [banco].[tabela] TO

    [usuario]@[host] •GRANT SELECT(campo) ON [banco].[tabela] TO [usuario]@[host] ▪Não adicione GRANT ALL ON *.* ▪Nao adicione % como host ▪Limite os usuários com WITH GRANT OPTION ▪Cuidado com SUPER e PROCESS •SUPER - max_allowed_connections + 1 •PROCESS - SHOW PROCESSLIST
  19. © 2017 Percona 28 Password ▪Crie passwords fortes •Letras Maiusculas

    e Minusculas •Mínimo de 8 characters •Possuir dígitos •Possuir acentos • Não utilize “dictionary” passwords
  20. © 2017 Percona 29 Password ▪Password Validation Plugin 5.6+ [mysqld]

    plugin-load-add=validate_password.so validate-password=FORCE_PLUS_PERMANENT ▪validate_password_length (Default 8) ▪validate_password_mixed_case_count (Default 1) ▪validate_password_number_count (Default 1) ▪validate_password_special_char_count (Default 1) ▪validate_password_dictionary_file ▪validate_password_policy (Default 1)
  21. © 2017 Percona 30 Password ▪Mysql_native_password ▪Hash de 41 characters

    ▪Mesmo hash todos mysql ▪Pode ser hackeado via Rainbow table ▪Hash é enviado aberto pela rede
  22. © 2017 Percona 31 Password ▪Sha256_password (built-in 5.6+) ▪Hash é

    calculado com um salt ▪Mesmo password gera um hash diferente toda vez ▪Password é enviado ou por SSL ou criptografado(RSA)
  23. © 2017 Percona 32 Password ▪Configurar cd /var/lib/mysql openssl genrsa

    -out private_key.pem 2048 openssl rsa -in private_key.pem \ -pubout -out public_key.pem chmod 400 private_key.pem; chmod 444 public_key.pem
  24. © 2017 Percona 33 Password ▪Reiniciar MySQL SHOW STATUS LIKE

    'Rsa_public_key'\G *************************** 1. row *************************** Variable_name: Rsa_public_key Value: -----BEGIN PUBLIC KEY----- MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAt3ioXKWcNMUfBmHrc4JC WxXBhi/c+UXXRlV5lZcq2rr2fDIRPNhJy/ctOKvVSW99MF2Y92TUlxNN8WK9J96P X1UjGM3zRuqpd9DH70yYIzte50HHQQ0KYSRdNIiGxXIT4t80bqC3dIZdlRfEiUk5 swJcREzIy96W5ewmhiaOnIh/WPAKKS+a9GgPj+Gb0BDRewLaROiRaZkq1d7yGbJ3 xxUvGHSDChOjF1a5ZOX+4ATBfhhJ8VBrCHG6aXZLuTTk46XAbylB9XDOG4+kKTcT jbUUY41XcXonKAMthhJWoZU406Tahnntof6AO/ZF5BQXv34pv7eBNSLy50cF7cTw VQIDAQAB -----END PUBLIC KEY-----
  25. © 2017 Percona 34 Password ▪Criar usuario CREATE USER 'sha256user'@'localhost'

    IDENTIFIED WITH sha256_password; SET old_passwords = 2; SET PASSWORD FOR 'sha256user'@'localhost' = PASSWORD('Sh@256Pa33');
  26. © 2017 Percona 35 Password ▪Forçar usuário a trocar password

    •MySQL 5.6 ALTER USER 'myuser'@'localhost' PASSWORD EXPIRE; •MySQL 5.7 [mysqld] default_password_lifetime=180 CREATE USER 'myuser'@'localhost' PASSWORD EXPIRE INTERVAL 90 DAY;
  27. © 2017 Percona 36 Password mysql> SELECT 1; ERROR 1820

    (HY000): You must SET PASSWORD before executing this statement mysql> ALTER USER USER() IDENTIFIED BY 'new_password'; Query OK, 0 rows affected (0.01 sec) mysql> SELECT 1; +---+ | 1 | +---+ | 1 | +---+ 1 row in set (0.00 sec)
  28. © 2017 Percona 38 Connection Control ▪MySQL 5.7.17+ ▪Adicionar delay

    na autenticação após X erros ▪Instalação [mysqld] plugin-load-add=connection_control.so INSTALL PLUGIN CONNECTION_CONTROL SONAME 'connection_control.so'; INSTALL PLUGIN CONNECTION_CONTROL_FAILED_LOGIN_ATTEMPTS SONAME 'connection_control.so';
  29. © 2017 Percona 40 Connection Control time mysql -u msandbox

    -pmsandbox2 -P 57182 --protocol=TCP ERROR 1045 (28000): Access denied for user 'msandbox'@'localhost' (using password: YES) real 0m0.014s user 0m0.007s sys 0m0.006s time mysql -u msandbox -pmsandbox2 -P 57182 --protocol=TCP ERROR 1045 (28000): Access denied for user 'msandbox'@'localhost' (using password: YES) real 0m0.014s user 0m0.004s sys 0m0.009s time mysql -u msandbox -pmsandbox2 -P 57182 --protocol=TCP ERROR 1045 (28000): Access denied for user 'msandbox'@'localhost' (using password: YES) real 0m1.513s user 0m0.009s sys 0m0.004s
  30. © 2017 Percona 42 TDE ▪InnoDB MySQL 5.7 ▪Criptografa os

    dados armazenados no disco ▪Cada tabela tem sua própria chave ▪Chave da tabela é armazenada no header do arquivo ▪Instalação [mysqld] early-plugin-load=keyring_file.so keyring_file_data=/usr/local/mysql/mysql-keyring/keyring
  31. © 2017 Percona 43 TDE ▪Verificar se o plugin está

    instalado mysql> SELECT PLUGIN_NAME, PLUGIN_STATUS FROM INFORMATION_SCHEMA.PLUGINS WHERE PLUGIN_NAME LIKE 'keyring%'; +--------------+---------------+ | PLUGIN_NAME | PLUGIN_STATUS | +--------------+---------------+ | keyring_file | ACTIVE | +--------------+---------------+
  32. © 2017 Percona 44 TDE ▪Criptografar tabelas mysql> CREATE TABLE

    t1 (c1 INT) ENCRYPTION='Y'; mysql> ALTER TABLE t1 ENCRYPTION='Y';
  33. © 2017 Percona 46 Audit Plugin ▪MySQL Enterprise - Pago

    ▪Macfee - Grátis ▪Percona Server - Grátis ▪Loga atividades do banco de dados ▪Diferente do general log ▪Filtros por command / usuário / banco
  34. © 2017 Percona 47 Audit Plugin ▪Formatos - OLD <AUDIT_RECORD

    "NAME"="Query" "RECORD"="2_2014-04-28T09:29:40" "TIMESTAMP"="2014-04-28T09:29:40 UTC" "COMMAND_CLASS"="install_plugin" "CONNECTION_ID"="47" "STATUS"="0" "SQLTEXT"="INSTALL PLUGIN audit_log SONAME 'audit_log.so'" "USER"="root[root] @ localhost []" "HOST"="localhost" "OS_USER"="" "IP"="" />
  35. © 2017 Percona 48 Audit Plugin ▪Formatos - New <AUDIT_RECORD>

    <NAME>Quit</NAME> <RECORD>10902_2014-04-28T11:02:54</RECORD> <TIMESTAMP>2014-04-28T11:02:59 UTC</TIMESTAMP> <CONNECTION_ID>36</CONNECTION_ID> <STATUS>0</STATUS> <USER></USER> <PRIV_USER></PRIV_USER> <OS_LOGIN></OS_LOGIN> <PROXY_USER></PROXY_USER> <HOST></HOST> <IP></IP> <DB></DB> </AUDIT_RECORD>
  36. © 2017 Percona 49 Audit Plugin ▪Formatos - JSON {

    "audit_record":{ "name":"Query", "record":"4707_2014-08-27T10:43:52", "timestamp":"2014-08-27T10:44:19 UTC", "command_class":"show_databases", "connection_id":"37", "status":0, "sqltext":"show databases", "user":"root[root] @ localhost []", "host":"localhost", "os_user":"", "ip":"" } }
  37. © 2017 Percona 50 Audit Plugin ▪Formatos - CSV "Query","49284_2014-08-27T10:47:11","2014-08-

    27T10:47:23 UTC","show_databases","37",0,"show databases","root[root] @ localhost []","localhost","",""
  38. © 2017 Percona 51 Audit Plugin ▪Mysqlauditgrep - buscar informações

    nos logs ▪Data ▪Tipo de evento ▪Usuário
  39. © 2017 Percona 53 Firewall ▪MySQL Enterprise Firewall - Pago

    ▪ProxySQL - Grátis ▪https://goo.gl/n4VZzH
  40. © 2017 Percona 54 Firewall ▪Bloquear queries - SQL INJECTION!

    •Original query: SELECT Name FROM world.city WHERE Name = ‘?’ •SQL Injection: ? = São Paulo' OR ID > 0; -- •SELECT Name FROM world.city WHERE Name = 'São Paulo' OR ID > 0; --' Admin> SELECT username, digest, digest_text FROM stats_mysql_query_digest WHERE digest_text LIKE '% OR ID %' ORDER BY first_seen DESC LIMIT 1; +-------------+--------------------+------------------------------------------------------+ | username | digest | digest_text | +-------------+--------------------+------------------------------------------------------+ | application | 0xD8AF41BF32707ABD | SELECT Name FROM world.city WHERE Name = ? OR ID > ? | +-------------+--------------------+------------------------------------------------------+ 1 row in set (0.00 sec)
  41. © 2017 Percona 55 Firewall Admin> INSERT INTO mysql_query_rules (rule_id,

    active, digest, error_msg, apply) VALUES (4,1,'0xD8AF41BF32707ABD','Suspeita de SQL Injection',1); Query OK, 1 row affected (0.00 sec) [root@localhost ~]# mysql -u application -papp -e "SELECT Name FROM world.city WHERE Name = 'São Paulo' OR ID > 0; --' " ERROR 1148 (42000) at line 1: Suspeita de SQL Injection
  42. DATABASE PERFORMANCE MATTERS Database Performance Matters Database Performance Matters Database

    Performance Matters Database Performance Matters Database Performance Matters Obrigado!