什么是SQL注入
当使用字符串拼接或者不正确的参数绑定方式时,就会导致SQL注入攻击。
举个例子,当我们根据用户名和密码查找用户时,查询语句如下:
SELECT * FROM users WHERE username = '{username}' AND password = '{password}'
如果攻击者使用以下值来伪造用户名和密码参数:
username = ' OR 1=1 --
password = ' OR 1=1 --
那查询语句就变成了:
SELECT * FROM users WHERE username = '' OR 1=1 --' AND password = '' OR 1=1 --'
因为OR 1=1"部分总是为真,"–"是注释符号,将查询语句的余下部分注释掉,从而使密码验证的部分无效。这个查询便返回所有用户。
如何避免SQL注入攻击
防止SQL注入攻击的基本思路是将用户输入的数据当作数据参数,而不是拼接到SQL语句中。
以下是几个常用的防止SQL注入攻击的方法:
1. 使用参数化查询
使用SQL语句时,将SQL语句中的参数用占位符?
或者命名参数:paramName
的方式代替,然后将实际参数绑定到语句中,避免直接将用户输入拼接到SQL语句中,从而防止SQL注入。例如使用占位符:
String sql = "SELECT * FROM users WHERE username = ? AND password = ?";
PreparedStatement statement = connection.prepareStatement(sql);
statement.setString(1, username);//将username的值设置到SQL语句的第1个占位符的位置
statement.setString(2, password);
ResultSet rs = statement.executeQuery();
或者使用命名参数:
String sql = "SELECT * FROM users WHERE username = :userName AND password = :passWord";
PreparedStatement statement = connection.prepareStatement(sql);
statement.setString("userName", username);//将username的值设置到SQL语句的命名参数:userName的位置
statement.setString("passWord", password);
ResultSet resultSet = statement.executeQuery();
username是在代码中已经定义并赋值的变量,这里将其作为参数传递给statement.setString()方法,以设置SQL语句中的参数值。
2. 过滤特殊字符
使用一些过滤器或者正则表达式将用户输入的一些特殊字符进行过滤或者替换,例如:
String sql = "SELECT * FROM users WHERE username = '" + username.replace("'", "''") + "' AND password = '" + password.replace("'", "''") + "'";
这里使用replace()方法将用户输入的单引号替换成两个单引号。在SQL语句中,单引号通常用来表示字符串常量,两个连续的单引号表示一个单引号字符。举个例子,如果用户输入的username值为JoJo's
,那如果不替换,SQL语句SELECT * FROM users WHERE username = 'JoJo's'
,显然是会有SQL语法错误的,但是替换之后变成SELECT * FROM users WHERE username = 'JoJo''s'
,这样就没有问题了,不会因为这个用户输入包含了SQL语句中的特殊字符而破坏SQL语句的语法结构。
3. 使用框架或者ORM工具
在MyBatis框架中,使用#{}
占位符和<![CDATA[]]>
标签来处理参数和SQL语句的拼接。
<![CDATA[ ]]>
是XML提供的一种特殊标记,用来指定XML文档中的某个区块不应被解析器解析。因为这个标记中的内容不会被解析器处理,所以可以在其中使用任意的字符,包括特殊字符和标记等。
${}
语法中的参数值会被直接拼接进SQL语句中,因此容易受到SQL注入攻击。攻击者可以通过构造恶意的输入参数,来改变SQL语句的含义,导致安全风险。
而使用#{}
语法,会将参数值转义并作为预编译语句的参数,而不是将参数值直接拼接到SQL语句中,从而避免了SQL注入攻击。
4. 对输入进行验证
可以使用一些验证框架或者正则表达式来验证用户输入的数据是否符合预期,例如使用Spring的表单验证框架来验证数据是否合法。