防范方法三:对所有用户输入进行转义
我们知道每个DBMS都有一个字符转义机制来告知DBMS输入的是数据而不是代码,如果我们将所有用户的输入都进行转义,那么DBMS就不会混淆数据和代码,也就不会出现SQL注入了。
当然,如果要采用这种方法,那么你就需要对所使用的数据库转义机制,也可以使用现存的诸如OWASP ESAPI的escaping routines。ESAPI目前是基于MySQL和Oracle的转义机制的,使用起来也很方便。一个Oracle的ESAPI的使用示例如下:
ESAPI.encoder().encodeForSQL(new OracleCodec(),queryparam);
那么,假设你有一个要访问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"))+"’";
当然,为了保证自己代码的可读性,我们也可以构建自己的OracleEncoder:
Encoder e = 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"))+"’";
除了上面所说的三种防范方法以外,我们还建议可以用以下两种附加的方法来防范SQL注入:最小权限法、输入验证白名单法。
最小权限法:
为了避免注入攻击对数据库造成的损害,我们可以把每个数据库用户的权限尽可能缩小,不要把DBA或管理员的权限赋予你应用程序账户,在给用户权限时是基于用户需要什么样的权限,而不是用户不需要什么样的权限。当一个用户只需要读的权限时,我们就只给他读的权限,当用户只需要一张表的部分数据时,我们宁愿另建一个视图让他访问。
如果你的策略是都是用存储过程的话,那么仅允许应用程序的账户执行这些查询,而不给他们直接访问数据库表的权限。诸如此类的最小权限法能够在很大程度上保证我们数据库的安全。
输入验证白名单法:
输入验证能够在数据传递到SQL查询前就察觉到输入是否正确合法,采用白名单而不是黑名单则能在更大程度上保证数据的合法性。