您的位置:首页 > 编程语言 > Java开发

java每日一学--数据校验20131008

2013-10-08 14:13 369 查看
[b]数据校验[/b]

规则1.1 禁止直接使用外部输入来拼接SQL语句以防止SQL注入

说明:如果对于外部输入的数据未经处理,直接用于拼接SQL语句,攻击者可以通过构造特殊形式的输入来改变程序中原本要执行的SQL逻辑,形成SQL注入攻击,导致系统功能异常、信息泄露、数据被非法修改等安全问题。
错误示例:

public void doPrivilegedAction(String username, char[] password) throws SQLException
{
/* … */
try
{
String pwd = hashPassword(password);
String sqlString = "SELECT * FROM db_user WHERE username = '"
+ username + "' AND password = '" + pwd + "'";
Statement stmt = connection.createStatement();
ResultSet rs = stmt.executeQuery(sqlString);
// Authenticate …
}
/* … */
}


  上面的代码使用用户输入来拼接SQL语句,但又没有对用户的输入做校验,存在SQL注入风险。当用户为username输入值:jack' OR '1' = '1
则SQL语句变成了:SELECT * FROM db_user WHERE username='jack' OR '1'='1'
AND
password=<PASSWORD>
如果jack是有效用户,这样便绕开了对口令的验证。

推荐做法1:校验输入长度,并且使用PreparedStatement来防范SQL注入。

public void doPrivilegedAction(String username, char[] password)
throws SQLException
{
/* … */
try
{
String pwd = hashPassword(password);
// Ensure that the length of user name is legitimate
if ((username.length()) > 8)
{
// Handle error
}
String sqlString = "select * from db_user where username=? and password=?";
PreparedStatement stmt = connection.prepareStatement(sqlString);
stmt.setString(1, username);
stmt.setString(2, pwd);
ResultSet rs = stmt.executeQuery();
// Authenticate …
}
/* … */
}


  使用PreparedStatement,输入的参数无法改变原始的SQL语义。上例中,即使攻击者插入类似 jack’ or ‘1’=‘1的字符串,也只会将此字符串
当做username来查询。
推荐做法2:使用存储过程

public void doPrivilegedAction(String username,
char[] password)
throws
SQLException
{
/* … */
try
{

String pwd = hashPassword(password);
// Ensure that the length of user name is
legitimate

if ((username.length()) >
8)

{

// Handle error

}

String sqlString = "select * from db_user where
username=? and password=?";

CallableStatement cs = connection.prepareCall("{call sp_getUser(?,?)}");

cs.setString(1, username);

cs.setString(2, pwd);

ResultSet rs = cs.executeQuery();
// Authenticate …

}
/* … */

}


使用存储过程的效果和使用PreparedStatement类似,其区别是存储过程需要先将SQL语句定义在数据库中。但需要注意的是,存储过程中也可能

存在注入问题,因此应该尽量避免在存储过程内使用动态的SQL语句。
推荐做法3:对外部输入进行转义

public void doPrivilegedAction(String username, char[] password)
throws SQLException
{
/* … */
try
{
String pwd = hashPassword(password);
Codec oe = new OracleEncoder();
String susername = oe.encode(username);
String spwd = oe.encode(pwd);
String sqlString = "SELECT * FROM db_user WHERE username = '"
+ susername + "' AND password = '" + spwd + "'";
Statement stmt = connection.createStatement();
ResultSet rs = stmt.executeQuery(sqlString);
// Authenticate …
}
/* … */
}


  对于PreparedStatement无法适用或者存储过程内部也存在动态构造SQL语句的情形,则可以考虑对外部输入做转义。每个DBMS都有一个字符转义机制
来告知DBMS输入的是数据而不是代码,如果将所有用户的输入都进行转义,那么DBMS就不会混淆数据和代码,也就不会出现SQL注入了。针对每种数据
的转义机制实现,可以使用现有的API工具,比如OWASP ESAPI的escaping
routines,也可以使用自己的实现。

  
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: