SQL Injection Prevention Cheat Sheet

来源:互联网 发布:个体户域名备案 编辑:程序博客网 时间:2024/06/03 09:29

第一篇:SQL Injection Prevention Cheat Sheet

本文的重点是提供清晰,简单,防止SQL注入漏洞在您的应用程序可操作的指导。有如此多成功的SQL注入发生是有些遗憾的,因为在你编写的代码中SQL注入是非常容易避免的。

SQL注入漏洞是被软件开发人员在创建动态的数据库查询时引入的,这些数据库查询包含用户的输入。为了避免SQL注入缺陷是很容易的。开发人员不要写动态的数据库查询,阻止用户的输入恶意的SQL去影响查询的逻辑。

本文提供一些用于阻止SQL注入漏洞的方法去避免这两个问题。这些技术被用于任何的编程语言和数据库。这里存在其他类型的数据库,比如xml 数据库也存在也是的问题,也能使用相同的方法去避免SQL注入漏洞。

1.     主要防御方法

l 使用prepared statement(参数化查询);

l 使用存储过程;

l 控制用户的输入;

2.     其他防御方法

l 赋予最小的权限;

l 对输入使用白名单控制;

3.     不安全的例子

典型的SQL注入例子如下:下面(Java)例子是不安全的,并且能在数据库查询中运行攻击者注入的的代码。未验证过的用户输入值被附加到SQL查询中,这些查询可以是攻击者想要的任意的注入代码。不幸的是,链接操作数据库的方式是相当常见的。

  • String query="select account_balance from user_data where username = "request.getParamter("customName")";
  •  
  • try{
  •    Statement statement= connection.createStatement(...);
  •    ResultSet results= statement.executeQuery(query);
  • }

4.  主要防御方法

4.1. 防御方法1:preparedstatement()

prepared statement的用处是所有的开发人员应该知道如何去写数据库查询。他们比动态查询更加容易理解,更加容易编写。预处理过的查询要求开发人员首先定义所有的SQL代码,然后成传递参数到这个查询中。无论用户输入的是什么内容,这种编码方式数据库能够区分出来数据与代码。prepared statement确保攻击者不能够改变SQL查询的目的,即使SQL中插入的攻击者的SQL命令。下面是一个安全的例子,如果攻击者输入一个user_id为 tom' or '1' = '1,参数化的查询不是可攻击的并且查询user_id通过完整的匹配为 tom' or '1' ='1。

相关语言的处理

l  Java EE:使用preparedStatement()来绑定参数;

l  .net:使用参数化的查询,例如:用sqlCommand() 或者 oleDbCommand()绑定参数;

l  PHP:用强类型参数化查询的PDO,使用bindParam();

l  Hibernate:使用createQuery()来绑定参数;

l  SQlite:使用sqlite3_prepare()去创建一个;

在少数情况下,prepared statement 不利于性能。当面对这种情况时,最好的方法为强制验证所有的输入数据。

 

Java prepared statement

例子:下面的代码例子使用预知语句,Java参数化查询的实现结果与数据库查询结果相同。

1.1.1.1         String custname= request.getParamter("customName");

1.1.1.2         String query="selectaccount_balance from user_data where username = ?";

1.1.1.3          

1.1.1.4         PreparedStatement pstmt= connection.preparedStatement(query);

1.1.1.5         pstmt.setString(1,custname);

1.1.1.6         ResultSet results= pstmt.executeQuery();

4.2防御2:存储过程

         存储过程与预置语句有相同的影响在实现的时候,这是大多数存储过程实现的规范。需要开发人员去创建带有动态参数的SQL语句。存储过程与预置语句的差别是存储过程的SQL代码定义并且存储在数据库中的,然后被其他应用调用。这两种技术在预防SQL注入有相同的效果,因此选择一种最合适的方法取决于开发人员。

       注意:安全地实现意味着存储过程不应该包含任何不安全的动态的SQL。开发人员一般不会在存储过程中生成动态的SQL。如果动态的SQL不能避免,这个存储过程必须使用输入验证或者合适逃逸正如在这篇文章中所描述确保给存储过程的用户输入不能被应用到SQL注入到动态的查询中。代码审核人应该寻找sp_execute,execute和exec在存储过程的用处。

         有几种风险能够增加存储过程的风险。例如:在MS SQL Server,有三大默认规则:db_datareader,db_datawriter和db_owner。在存储过程投入使用之前,DBA会根据需求给webservervice用户db_datareader,db_datawriter的权限。然而,存储过程需要执行权限。

         用户管理的一些设置已经集中了,而且这些设置被限制在那些3个角色中,因为所有的web apps 是在db_owner的权限下运行的。

         安全的Java存储过程例子

         下面的代码例子使用CallableStatement,存储过程的接口的java实现,执行相同的数据查询。

String custname = request.getParameter("customerName"); // This should REALLY be validated
try {
        CallableStatement cs = connection.prepareCall("{call sp_getAccountBalance(?)}");
        cs.setString(1, custname);
        ResultSet results = cs.executeQuery();                
        // … result set handling 
 } catch (SQLException se) {                  
        // … logging and error handling
 }

4.3转译用户提供的输入

         第2种技术是在用户提交查询之前转译用户的输入。不过,这种技术与参数化查询相比是脆弱的并且我们不能阻止所有的SQL注入。这项技术只能谨慎的使用去改造之前代码,这样是最有效的且成本最低的方式。

这项技术是这样工作的。每种数据针对某些特定的查询支持一个或者多个字符转译模式。如果你通过数据库的合适的转译模式转译用户的所有输入,数据库对开发人员写的SQL语句不会产生困惑,从而避免任何可能的SQL注入漏洞。

为了找到针对数据库的编码器的javadoc文档,点击左边的‘codec’类。这里有很多编码器的实现。两类数据库确定的编码器是oralcecodec和mysqlcodec。

转译动态查询

使用ESAPI数据库转译是很简单的。一个oracle例子:

ESAPI.encoder().encodeForSQL( new OracleCodec(), queryparam );

因此,如果在代码中你有一个动态的SQL查询,如果是Oracle如下:

String query = "SELECT user_id FROM user_data WHERE user_name = '" + req.getParameter("userID") 
 + "' and user_password = '" + req.getParameter("pwd") +"'";
 try {
     Statement statement = connection.createStatement( … );
     ResultSet results = statement.executeQuery( query );
 }

你可以重新写第一行如下:

Codec ORACLE_CODEC = new OracleCodec();
 String query = "SELECT user_id FROM user_data WHERE user_name = '" + 
   ESAPI.encoder().encodeForSQL( ORACLE_CODEC, req.getParameter("userID")) + "' and user_password = '"
   + ESAPI.encoder().encodeForSQL( ORACLE_CODEC, req.getParameter("pwd")) +"'";

这样这个SQL能避免注入,无论提交的输入是什么。

为了获得代码的可读性,你可以构建自己的OracleEncode。

Encoder oe = new OracleEncoder();
 String query = "SELECT user_id FROM user_data WHERE user_name = '" 
   + oe.encode( req.getParameter("userID")) + "' and user_password = '" 
   + oe.encode( req.getParameter("pwd")) +"'";

通过这种方法,所有的开发人员必须包装用户的输入参数通过ESAPI.encoder().encodeForOracle( )

Like关键字允许文字搜索。在oracle中,下划线“_”只能匹配一个字符,百分号“%”用于匹配0个或者多个字符。这些字符必须被转译在like语句中,如下:

SELECT name FROM emp 
WHERE id LIKE '%/_%' ESCAPE '/';
SELECT name FROM emp 
WHERE id LIKE '%\%%' ESCAPE '\';

附加防御

         除了采取三个主要防御方法之一,我们也建议采用下面的附加方法去提供更加深度的防御。他们包括:最小权限,白名单验证。

1最小权限

         为了降低潜在的SQL注入威胁,应该降低给每个数据库账号的权限。不要赋予DBA或者amdin权限给应用程序。

2.白名单验证

0 0
原创粉丝点击