O mínimo que você deveria aprender para se defender de ataques de injeção de SQL no PostgreSQL

SQL Injection ou injeção de SQL é uma técnica de invasão de sistemas que se tornou famosa na Internet, mas pode ser utilizada em qualquer linguagem de programação. No entanto, na Internet temos uma combinação explosiva:

  • A aplicação está acessível para toda internet que possui milhares de usuários dispostos a quebrar seu sistema;
  • O uso de linguagens de script fracamente tipadas em conjunto com com tipos de dados fracamente tipados ajuda a abrir algumas brexas de segurança.
  • O protocolo HTTP tem peculiaridades que quando mal utilizadas podem tornar uma aplicação web mais vulnerável como o uso de parâmetros GET.

O exemplo clássico é o login de um usuário da aplicação. Você pede o nome e senha do usuário numa tela e depois envia um SQL com esta característica:

SELECT TRUE WHERE nome = 'telles' AND senha = 'abizi'

Na tela de login o usuário pode digitar no lugar da senha algo mais ou menos assim:

USUÁRIO: admin
SENHA: ‘ OR 1=1 –

Ao substituir as variáveis o seu SQL ficaria assim:

SELECT TRUE WHERE nome = 'admin' AND senha = '' OR 1=1 --'

Segurança contra o SQL Injection

Regra no mínimo privilégio

Veja que há uma série de problemas a serem analisados aqui. A primeira questão que qualquer sistema deve se preocupar em analisar é saber qual o potencial de destruição que o usuário vai ter se ele conseguir um ataque bem sucedido. Se o usuário que se conecta na aplicação é dono de objetos no banco de dados, como muitas aplicações gostam de fazer por uma questão de comodidade, você verá que o seu usuário terá permissões plenas sobre os seus objetos. Utilizando SQL Injection, ele poderá realizar operações como DROP, TRUNCATE, ALTER além do DELETE, INSERT, UPDATE e SELECT.

Então a primeira regra que deve SEMPRE ser seguida a risca é que:

“O USUÁRIO QUE A APLICAÇÃO UTILIZA PARA SE CONECTAR NO BANCO DE DADOS JAMAIS DEVE SER O DONO DOS OBJETOS CRIADOS NO BANCO DE DADOS”

Alguns artigos que tratam sobre SQL Injection ignoram solenemente esta recomendação. Alguns ficam desempregados inesperadamente. Então, precauções especiais devem ser tomadas em aplicações onde cada usuário da aplicação não tem seu próprio usuário criado no banco de dados – o que é comum em aplicações web com milhares de usuários que surgem sem controle do DBA. Você deve ter pelo menos 4 usuários para cada aplicação com este perfil:

  1. Um usuário que é dono dos objetos criados no banco de dados. Este usuário deve estar sob controle somente e tão somente do DBA, tanto no ambiente de testes como em produção. Lembre-se que este usuário tem poderes plenos sobre estes objetos do banco de dados.
  2. Um usuário ou grupo de usuários destinado aos desenvolvedores com poderes específicos para as suas tarefas. Pode-se determinar que no ambiente de produção ele tenha permissão de SELECT em todas tabelas e sequências e no ambiente de produção tenha poderes de SELECT, INSERT, UPDATE e DELETE.
  3. Um usuário ou grupo de usuários destinado aos administradores da aplicação, que terão poderes específicos na aplicação como deletar registros ou atualizar valores em tabelas. Este usuário deve se conectar a partir de outro executável que não o utilizado pelos usuários normais. A aplicação utilizada para fins administrativos deve ter acesso restrito ao acesso local por exemplo. Este usuário terá permissões de DELETE, UPDATE e INSERT em tabelas que um usuário normal não terá permissão. Portanto, este usuário jamais deve ser utilizado em operações de rotina.
  4. Um usuário para os usuários normais da aplicação. Este usuário deve seguir a risca a regra no menor privilégio. Ele deve apenas as permissões para acessar os objetos estritamente necessários. Privilégios de UPDATE e DELETE devem ser concedidos com muita cautela. Operações deste tipo quando executadas sem uma clausula WHERE adequada podem ser desastrosas. É este usuário que será alvo de ataques de SQL Injection. Se você for rigoroso com as permissões para este usuário, limitará muito a dimensão dos estragos que podem acontecer no caso de um ataque bem sucedido.

Use criptografia, visões e funções

Feito isso, você já terá um ambiente muito mais seguro, com certeza. A próxima parte do nosso trabalho será realizar um tratamento dedicado às informações mais sensíveis do seu sistema. Identifique estes objetos com cuidado. Existem dados sigilosos que podem ser alvo de criptografia. Senhas jamais devem ser armazenados sem criptografia. Use uma função como MD5 para isso. Lembre-se que você não poderá saber qual é a senha armazenada no campo, apenas poderá verificar se uma senha é igual a que está armazenada. No entanto, se apenas um grupo específico precisa ter acesso a estas informações você deve restringir o acesso a elas. Quando você quer restringir o acesso a apenas uma coluna ou grupo de colunas de uma tabela a melhor alternativa é criar uma visão com apenas as colunas que o usuário vai precisar. De permissão ao usuário acessar esta visão não permita que ele leia a tabela diretamente.

Há casos em que a pessoa precisa alterar ou ler registros, mas você não quer que limitar a possibilidade de que o usuário altere todos os registros da tabela. Para isso, você pode criar funções que encapsulem toda a operação. Você passa para a função os parâmetros a serem alterados, a função checa a validade destes dados, faz todas as alterações em todas tabelas necessárias e retorna se realizou a operação com sucesso ou não, ou ainda retorna um valor desejado como se fosse um SELECT. O resultado disso é que você só precisa dar permissão ao usuário para executar a função e não precisa dar nenhum acesso a qualquer um dos objetos envolvidos na transação. Isto significa que o usuário só poderá realizar aquela transação especificada pela função, que se for bem codificada, evitará definitivamente qualquer chance de acesso indevido.

Procedimentos preparados

A terceira etapa é utilizar os procedimentos preparados ou “prepared statements’. Este procedimentos fazem com que você passe como parâmetros os valores a serem substituídos num comando SQL qualquer. As pessoas evitam utilizar este procedimento pois ele requer que você o execute em duas etapas: preparar o procedimento e executar o procedimento. Quando executado com freqüência, os procedimentos preparados não só são mais seguros, como também apresentam um ganho de performance.

Dollar Quoting

A quarta etapa que você pode utilizar é peculiar ao PostgreSQL, e chama-se “dollar quoting“. Ao invés colocar strings de caractere entre aspas simples em expressões SQL, você pode usar o $$ como em:

SELECT TRUE FROM users WHERE name = $$$$ AND senha = $$abizi$$;

Você ainda pode fazer coisas como:

SELECT TRUE FROM users WHERE name = $name$telles$name$ AND senha = $senha$abizi$senha$;

Note que você pode colocar suas strings entre $$, $nome$, $senha$ ou qualquer outra coisa, tendo somente que começar e terminar com a mesma marca. Usar este tipo de notação no código SQL pode parecer um tanto grotesco inicialmente, mas quando você for escrever funções, ela pode tornar a sua vida muito mais simples, pois você não terá situações bizarras como um grupo de várias aspas simples ou contra barras. Com o dollar quoting, você não precisa se preocupar com as aspas simples.

Checar o tipo de dados

A próxima barreira de defesa na luta contra a injeção de SQL é checar o tipo de dados utilizado. Um tipo comum de ataque é por exemplo injetar código em parâmetros GET do protocolo http. Se você espera um número, tenha certeza de que ele é um número antes de substituir a sua variável no seu código SQL. Uma forma eficiente de fazer isso é utilizar a função ‘printf’. Quase toda linguagem de programação possui um comando semelhante ao printf da linguagem C. A nossa string SQL utilizando printf ficaria alguma coisa assim:

SQL = printf('SELECT TRUE FROM users WHERE nome = %s AND senha = %s', nome, senha)

A questão aqui é que a função printf vai forçar o uso do tipo de dados correto na função SQL. Não é uma solução perfeita, mas pode evitar alguns problemas além de evitar a conversão implícita de tipos de dados, fonte de muitas dores de cabeça no banco de dados.

Escapar as strings

A barreira mais conhecida na proteção contra a injeção de SQL é o uso de funções que escapem as strings. Hoje a maioria das linguagens de programação possuem funções para escapar caracteres como aspas simples em strings. Veja que uma aspa simples pode fazer parte de uma string como em “database’s security”. Para que uma string deste tipo seja aceita, é preciso escrevê-la desta forma numa expressão SQL:

INSERT INTO titulo VALUES ('database''s security');

Note que existem duas aspas simples e não uma aspas dupla. Esta é a única forma de se adicionar aspas simples numa string em banco de dados. Você deve esperar que o seu usuário por qualquer motivo digite uma aspa simples em qualquer string. A não ser que você remova explicitamente as aspas simples da sua string antes de construir o seu comando SQL, você deverá sempre escapa-las. Se a sua linguagem de programação possuir uma função de escape de strings específica para o seu banco de dados, utilize-a no lugar de uma função genérica.

Conclusão

Os problemas de segurança em geral são criados por profissionais mal informados ou por desenvolvedores que acreditam que segurança é um fator supérfluo e que implementar todas as barreiras necessárias é muito trabalhoso. O descuido com a segurança chega num ponto onde muitos servidores web exibem suas mensagens de erro diretamente na tela do usuário, no ambiente de produção. O uso de técnicas de SQL Injection em ambiente que não implantaram todas as barreiras citadas e ainda oferecem de bandeja os erros da aplicação na tela é absolutamente devastador. O usuário consegue descobrir o nome de todas as tabelas e colunas da sua aplicação, alterar qualquer registro ou mesmo apagar todas as informações de todas tabelas. Não é incomum encontrarmos aplicações de grande porte com furos homéricos de segurança. Não é incomum sites famosos e grandes corporações sofrerem com o vazamento de informações, fraudes e perda de dados. O prejuízo é sempre maior do que o custo de implementar a segurança de forma correta na aplicação.

Quando pensar novamente em segurança, lembre-se que a tarefa é de todos. Os ataques de SQL injections são muito simples de executar, a pondo de criarem a expressão “Script kiddie” para designar pessoas com pouco conhecimento técnico que utilizam receitas simples e eficientes para invadir sistemas. Não adiante a rede, o servidor e o banco de dados adotarem condutas rigorosas de segurança se você lança mão de aplicações (desenvolvidas por você ou por terceiros) que não tomam este tipo de precaução.

Alterando Tablespaces de tabelas e índices no Oracle

A tarefa é trivial, mas não é algo que você faz todo dia. Então resolvi documentar aqui para facilitar a minha vida. Há várias formas diferentes de se rearranjar tablespaces. Com a popularização dos RAIDs, não é mais tão comum ficar dividindo tablespaces através de discos isolados, mas ainda assim, há bons motivos para você criar todos os objetos em apenas um tablespace:

  • O backup on-line pode ser feito um tablespace por vez, diminuindo a quantidade de logs gerados durante o backup;
  • Você pode transportar tablespaces entre bases (teste e produção por exemplo) sem ter que exportar e importar todos os dados;
  • Você pode utilizar diferentes parâmetros de storage, particionamento, etc;
  • Fica mais fácil monitorar o crescimento da base com várias aplicações se cada aplicação possuir suas próprias tablespaces;
  • Separar índices de tabelas ainda é uma boa política, especialmente porquê os índices podem ser reconstruídos e as tabelas não;
  • Objetos especiais como LOBs e dados estáticos são bons candidatos a terem seu próprio tablespace;

Assim sendo, é comum você pegar uma tabela que cresceu muito e alocar um tablespace só para ela e coisas do tipo. Particularmente, quando os desenvolvedores tem a liberdade de criar objetos no ambiente de testes (sim, isso é polêmico e fonte para outra conversa), é comum ter que ajustar os parâmetros de storage antes de colocar os objetos no ambiente de homologação ou produção. Seja qual for o motivo da movimentação, você terá que fazer a migração em 3 etapas:

  • Migrar tabelas com o comando:
ALTER TABLE nome_da_tabela MOVE TABLESPACE nome_do_novo_tablespace
;
  • Migrar índices com o comando:
ALTER INDEX nome_do_indice REBUILD TABLESPACE nome_do_novo_tablespace
;
  • Migrar LOBs com o comando:
ALTER TABLE nome_da_tabela MOVE LOB(nome_da_coluna_lob)
    STORE AS (nome_do_novo_tablespace);

Note tabelas que contem LOBs, possuem um índice que aparece na tabela DBA_INDEXES com data_type do tipo LOB. Se você tentar reconstruir estes índices em outro tablespace você terá um erro do tipo: “ORA-02327: cannot create index on expression with datatype LOB”. Por isso é importante a etapa de migração dos LOBs.

Segue aqui um script para fazer isso rapidamente num para todos objetos de um determinado esquema:

SELECT 'ALTER TABLE nome_do_esquema.' || TABLE_NAME || ' MOVE TABLESPACE nome_do_novo_tablespace;'
  FROM dba_tables
  WHERE
    owner = 'nome_do_esquema';
 
SELECT 'ALTER INDEX nome_do_esquema.' || index_name || ' REBUILD TABLESPACE nome_do_novo_tablespace;'
  FROM dba_indexes
  WHERE
    owner = 'nome_do_esquema' AND
    index_type != 'LOB';
 
SELECT
  'ALTER TABLE nome_do_esquema.' || TABLE_NAME ||
  ' MOVE LOB( ' || COLUMN_NAME ||
  ' ) STORE AS (TABLESPACE nome_do_novo_tablespace);'
  FROM dba_tab_columns
  WHERE
    owner = 'nome_do_esquema' AND
    data_type LIKE '%LOB';

12 Dicas para o DBA lidar com o Desenvolvedor

Resolvi fazer eu mesmo uma réplica ao post anterior antes que os desenvolvedores comecem a me jogar pedras…

  1. Se você não quer que as pessoas fiquem ligando para você o tempo todo para saber se o banco de dados está no ar, disponibilize uma ferramenta (de preferência web) para que eles possam checar o status do servidor.
  2. Ajude seus desenvolvedores a homologar a solução. Facilite o teste com dados reais ou utilize ferramentas para injetar dados na aplicação. Crie métricas para testes e discuta elas com seus desenvolvedores antes de homologar a solução.
  3. Procure conhecer a regra de negócios da aplicação que está sendo desenvolvida. Procure conhecer o modelo de dados da aplicação durante o desenvolvimento da mesma. Se você encontrou um problema grave, não reclame apenas, mostre alternativas e se possível faça testes comparativos para mostrar a diferença de performance das diferentes abordagens.
  4. Sempre que você puder consertar um problema de desempenho alterando o modelo físico sem alterar o modelo lógico, faça-o. Existem muitas ferramentas disponíveis para um DBA experiente contornar alguns problemas.
  5. Desenvolvedores vivem num mundo dinâmico com exigências que mudam o tempo todo. Procure entender a dinâmica dos negócios da empresa. Estabeleça regras mínimas para serem seguidas pelos desenvolvedores e saiba quebra-las em momentos de crise. Se precisar quebrar uma regra, dê um prazo para o desenvolvedor se adequar depois que o sistema entrou em produção. Se você nunca acompanhar o rítimo dos negócios da empresa, pode haver um momento que não haja mais empresa com dados para cuidar!
  6. Ser cauteloso não pode significar ser covarde. Um dia você vai ter de migrar de versão do seu SGDB. Seus desenvolvedores esperam por uma série de funcionalidades disponíveis nas novas versões. Considere que 2 anos é um tempo razoável para uma nova versão se estabilizar. Ao invés de se opor às mudanças, crie um calendário de testes bastante generoso.
  7. Não fazer alterações direto na base de produção não significa que não é possível realizar alterações num prazo mais curto. Se a alteração for urgente, dê prioridade para a alteração e peça ajuda do desenvolvedor para os testes. Se isto for comum, crie alternativas para agilizar os testes como ter bases de dados com dados atualizados com uma certa frequência.
  8. Não se proponha a colocar parte da inteligência da aplicação dentro do banco de dados se você não tiver fôlego para manter isso. Saiba balancear bem isso e não torne a aplicação totalmente dependente de um fornecedor de SGDB desnecessariamente.
  9. Procure conhecer um pouco da linguagem de programação utilizada pela aplicação. Foque o seu estudo na forma como a linguagem se conecta no banco de dados e nas funções adjacentes. Isto ajudará muito no processo de orientação ao desenvolvedor.
  10. Crie algum tipo de documentação para orientar os desenvolvedores a criarem aplicações que não criem problemas sérios de performance. Crie metas de desempenho como tempo máximo para uma consulta, regras para transações em lote, etc. Dê pleno suporte para aqueles que se empenharem em seguir as suas recomendações.
  11. Não espere que o desenvolvedor crie toda a documentação que você precisará. Você deve no mínimo documentar os requisitos de performance, atributos do modelo físico e testes comparativos de performance.
  12. Procure não se portar apenas como alguém que presta suporte ao desenvolvedor e sim como alguém que faz parte da equipe de desenvolvimento. Dê sugestões, se comprometa com o projeto e com o core business da empresa.

Sobrevivendo como DBA

Algumas pessoas já sabem e outras andaram estranhando. O fato é que voltei a trabalhar com Oracle. Passei um tempo com desenvolvimento Web, mas o fato é que eu realmente gosto de trabalhar com Banco de Dados. Procuro há algum tempo uma vaga para DBA PostgreSQL no Brasil, e não encontro. Se você entrar no APInfo, por exemplo e fizer uma busca, você vai encontrar algumas vagas para desenvolvedores que tenham algum conhecimento de PostgreSQL, mas não vagas para DBA PostgreSQL.

O fato é que o PostgreSQL está crescendo no mercado. Ainda não representa uma fatia tão expressiva quando o Firefox no mercado de navegadores, mas vem crescendo. O que eu vejo são empresas com aplicações pequenas onde não existem um DBA alocado exclusivamente para tomar conta do SGDB ou empresas com aplicações médias onde um outro DBA, como um DBA Oracle ou MS SQL Server acaba assumindo o trabalho. Existem ainda poucas empresas especializadas em dar suporte para o PostgreSQL no Brasil, e poucas aplicações de grande porte utilizando o PostgreSQL, pelo menos é o que tenho notícias. Sei que muitas implementações não são divulgadas, até mesmo por questões de segurança interna. Mas já tem muita gente testando e se convencendo que o PostgreSQL já está pronto para aguentar cargas mais pesadas. O lançamento da versão 8.3 vai confirmar isso novamente.

Estive lendo um artigo interessante, onde o autor demonstra que em seus testes de migração do Oracle 8 para o 10, não houve uma melhoria muito grande no aumento de performance do Oracle. O que mudou então? Muita coisa melhorou, mas o que eu tenho sentido nos meus testes pessoais é uma melhoria enorme nas interfaces administrativas. Uma quantidade enorme de ferramentas foram colocadas a disposição para automatizar dezenas de tarefas. É claro que em algumas implementações críticas ou muito grandes, um ou mais bons DBAs conseguirão fazer um gerenciamento do Banco de Dados melhor que as ferramentas automáticas. Mas as implementações pequenas e médias precisam de cada vez menos intervenções. Muita coisa que eu fazia na mão ou via scripts no Oracle 9i, podem ser feitas automaticamente pelo Oracle 10g. E em breve teremos a versão 11 por aí.

Isto significa que a demanda por DBAs tende diminuir um pouco, mas significa também que o PostgreSQL ainda tem muito terreno para ganhar. A cada nova versão, o desempenho do PostgreSQL tem sofrido melhorias constantes e significativas. Ainda não existem funcionalidades e ferramentas tão sofisticadas quanto as da Oracle, mas iniciativas como o CEDRUS estão aparecendo e muita gente está começando a se preocupar com isso. A meu ver, teremos um aumento de demanda crescente para bons DBAs para PostgreSQL, de preferência, aqueles que tiverem experiência com ambientes maiores. Isto significa que o que se aprende com Oracle (se não aprender apenas a arrastar o mouse, claro…) não deverá se perder, e sim se agregar para diversas situações onde o ambiente é mais exigente.

Com isso, digo que estou um pouco afastado do trabalho com PostgreSQL, mas continuo acompanhando de perto, com o prazer de trabalhar ao lado de dois bons DBAs com quem estou aprendendo muito, ironicamente, um é um DBA DB2, e ou outro é um DBA PostgreSQL! Acredito que haverá espaço para os fãns do elefante em breve,enquanto isso, se aprimorar um pouco em ambientes mais complexos, não me parece tão ruim assim. Tenho aprendido muita coisa e comparado como as coisas funcionam em SGDBs diferentes, tenho até aprendido algumas coisas sobre DB2 em mainframes, que é muito interessante. Espero que quando as oportunidades surgirem eu esteja bem preparado para assumir novos desafios.

12 Dicas para o desenvolvedor lidar com o DBA

Este é um post inspirada no post do Insano Mundo de Jack que é um complemento do post no br-net.org que é uma tradução do post do Geeks are Sexy.

DBA’s são pessoas que trabalham entre os desenvolvedores (locais ou externos) e os adminstradores de sistema. Na minha curta experiência, os DBA’s costumam estabelecer relações mais amistosas com os Administradores de Sistema (que são poucos) e ter atritos com os desenvolvedores (que são muitos).

Então, aqui vão algumas dicas para o desenvolvedor (e em alguns casos usuários também) manterem uma relação produtiva com o seu DBA:

  1. Se a sua aplicação parou de funcionar de repente, isto não significa que o banco de dados está fora do ar. A rede costuma ser o problema número um. Verifique se a rede está funcionando normalmente antes de por a culpa no banco de dados;
  2. Entenda que o fato de uma aplicação ter desempenho adequado nos testes ou nos primeiros meses de produção, não significa que ela terá desempenho bom sempre. Os problemas de desempenho crescem geometricamente a partir de um certo volume de dados;
  3. Por mais que você se considere um mestre em modelagem de dados, discuta o modelo com o seu DBA antes de sair implementando ele. O modelo de dados lógico nunca é igual ao modelo físico implementado pelo DBA;
  4. Antes de sair desnormalizando o seu modelo de dados, tente seguir as formas normais e discuta os casos em que você acha conveniente fugir do modelo relacional com o seu DBA. Muitas veses isto não é vantajoso ou pode ser feito de forma transparente para a aplicação, ou só é vantajoso em alguns SGDBs.
  5. DBAs não gostam de mudanças rápidas. Eles são resistentes a mudanças por natureza. Isto tem haver com a função deles. Linguagens de programação vem e vão e muitas vezes os bancos de dados continuam o mesmo. Veja que apesar dos modismos, a teoria relacional de bancos de dados continua firme há mais de 20 anos;
  6. Nunca peça para realizar alterações no modelo de dados com pressa. Uma simples mudança de um campo em uma única tabela pode demandar semanas de planejamento cuidadoso. Se você apressar seu DBA para realizar as alterações, descobrirá que um erro cometido pelo DBA pode acarretar prejuízos incalculáveis para uma empresa.
  7. Nunca, repito NUNCA solicite alteração em dados diretamente no ambiente de produção. Por favor, não insista!
  8. Não peça permissões no ambiente de produção se você não está preparado para se responsabilizar pela perda daquelas informações.
  9. Por mais que você seja um programador habilidoso, entenda que há operações que rodam de forma mais eficiente dentro do banco de dados. Aprenda a utilizar as linguagens procedurais do banco de dados e aprenda a utilizar subconsultas. É melhor perguntar para o seu DBA se você não souber, do que tentar resolver o problema sozinho.
  10. Entenda que orientação a objetos pode ser a palavra de órdem entre as linguagens de programação, mas em materia de banco de dados suportando aplicações transacionais, isto não funciona, por mais que alguns vendedores tentem lhe convencer o contrário. Por mais que isto lhe pareça atraente, os SGDBs orientados a objeto não conseguiram provar sua robustez em ambientes de produção.
  11. Não, diagramas UML não substituem as documentações clássicas utilizadas em banco de dados como o diagrama entidade relacionamento e dicionários de dados.
  12. Veja seu DBA também como um desenvolvedor e não apenas como um administrador de sistemas. Ele é uma peça fundamental na homologação dos sistemas, mas se estiver presente no começo do projeto, muito tempo poderá ser economizado, a não ser que você tenha um Administrador de Dados realmente experiente na equipe.