Esta classe de ataques, muito comum em ambientes web, consiste na inserção de comandos nos dados passados como entrada para a aplicação de forma que estes comandos sejam executados pela ou através da aplicação. As subclasses mais comuns são SQL injection e Shell Command Injection. Injeção de SQL, ou SQL Injection, consiste em inserir comandos SQL nos dados enviados para a aplicação de modo a acessar o banco de dados subjacente. Por exemplo, suponhamos que uma aplicação web esteja vulnerável e contenha uma sentença SQL: "SELECT Login, Nome FROM Usuario WHERE Login = '" + sParam + "'". Se o adversário passar para esta aplicação a string "zé' OR '2'='2'", o resultado será: "SELECT Login, Nome FROM Usuario WHERE Login = 'zé' OR '2'='2'". Esta sentença SQL retornará a lista de todos os usuários da aplicação, que poderá ser usada posteriormente como subsídio para outros ataques. O adversário poderia também inserir comandos visando alterar ou apagar dados do banco, causando estragos ainda maiores. A injeção de comandos shell acontece de forma similar se a aplicação acessa o sistema operacional através de um interpretador de comandos.
A maneira mais simples de proteger uma aplicação contra injeção de comandos é evitar o uso de interpretadores externos, como um interpretador SQL ou um shell. Para evitar o uso de comandos de shell, devemos usar as bibliotecas do sistema que realizem as funções específicas necessárias. Nos casos em que não há alternativa, tais como as chamadas a interpretadores SQL de bancos de dados, devemos validar criteriosamente os dados recebidos e certificar que estes dados não incluam nenhum comando ou conteúdo potencialmente nocivo. Devemos também estruturar as requisições de maneira que os parâmetros recebidos sejam efetivamente tratados como dados e não como comandos. No caso de Java, pode-se empregar também stored procedures e prepared statements (representados em Java pelas classes CallableStatement, quando são usadas stored procedures, e PreparedStatement, quando se usa SQL) para aumentar o nível de proteção à informação. O exemplo abaixo ilustra o uso incorreto de PreparedStatement, pois insere os dados recebidos pela aplicação diretamente no comando SQL que será enviado para o banco de dados. String name = request.getParameter("name"); PreparedStatement pstmt = conn.prepareStatement("insert into EMP (ENAME) values ('" + name + "')"); pstmt.execute(); pstmt.close(); A maneira correta seria: PreparedStatement pstmt = conn.prepareStatement ("insert into EMP (ENAME) values (?)"); String name = request.getParameter("name"); pstmt.setString (1, name); pstmt.execute(); pstmt.close(); No segundo exemplo, o parâmetro name é tratado exclusivamente como dado (mais especificamente uma string), o que protege a aplicação contra a tentativa de injeção de código SQL. Abaixo, temos um trecho de código onde o CallableStatement é usado de forma incorreta: String name = request.getParameter("name"); String sql = "begin ? := GetPostalCode('" + name + "'); end;"; CallableStatement cs = conn.prepareCall(sql); cs.registerOutParameter(1, Types.CHAR); cs.executeUpdate(); String result = cs.getString(1); cs.close(); No código acima, há a possibilidade que o código SQL enviado ao servidor seja (o texto em vermelho corresponde ao suposto conteúdo do parâmetro name): begin ? := GetPostalCode(''); delete from users; commit; dummy(''); end; A maneira correta de usar o CallableStatement está mostrada abaixo. É possível perceber que há o registro do parâmetro name, como dado contendo caracteres. String name = request.getParameter("name"); CallableStatement cs = conn.prepareCall ("begin ? := cs.registerOutParameter(1,Types.CHAR); cs.setString(2, name); cs.executeUpdate(); String result = cs.getString(1); cs.close(); Nos casos em que é usado o framework Hibernate, as manipulações de dados mais simples ou corriqueiras são tratadas diretamente pelo framework. No entanto, o Hibernate disponibiliza a linguagem HQL (ou Hibernate Query Language) para que os programadores possam definir consultas ou manipulações complexas dos dados da aplicação. Embora o HQL apresente algumas restrições no conteúdo dos comandos que diminuam o risco de ataques de injeção de comandos, os riscos não são totalmente eliminados. Assim, são necessários os mesmos cuidados que no caso do uso direto de SQL para acesso à base de dados. O exemplo a seguir apresenta um comando HQL passível de ser atacado por injeção de código, através do parâmetro paymentIds: Payment payment = (Payment) session.find("from com.example.Payment as payment where payment.id = " + paymentIds.get(i)); O mesmo trecho pode ser reescrito usando outras versões da função session.find que permitem definir explicitamente os tipos dos dados a serem passados para a base de dados. Desta forma, o risco de ataques por injeção de comandos é bastante reduzido, conforme demonstra o exemplo abaixo: String pId = paymentIds.get(i); TsPayment payment = (TsPayment) session.find("from com.example.Payment as payment where payment.id = ?", pId, StringType); |
Início >