在Java应用程序中,与数据库交互通常使用JDBC(Java Database Connectivity)。在JDBC中,有两种执行SQL语句的方式,分别是Statement和PreparedStatement。
准备
数据库
<code>CREATE DATABASE IF NOT EXISTS testsqli; CREATE TABLE IF NOT EXISTS `user` ( `id` int unsigned AUTO_INCREMENT, `username` varchar(100) NOT NULL, `password` varchar(100) NOT NULL, PRIMARY KEY (`id`) ) CHARSET=utf8; INSERT INTO user (id, username, password) VALUES (1, "Bob", "12345678"); INSERT INTO user (id, username, password) VALUES (2, "Alice", "1qaz@WSX"); INSERT INTO user (id, username, password) VALUES (3, "admin", "admin123");</code>
pom.xml
<code><dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.13</version> </dependency></code>
Statement
SQL语句为:
<code>String sql = "SELECT * FROM user WHERE username = '" + username + "' AND password = '" + password + "'";</code>
当传入的数值为正常数值时,如:username = admin, password = admin123,若存在当前用户名及密码的账户,则会返回数据。
然而,当我们传入admin' OR 1=1#
时,改变了SQL语义,1=1
为恒真,并注释掉SQL语句后面的内容,因此表达式会返回所有结果。这意味着在知道用户名存在的情况下,即可用admin' and 1=1#
进行登录。
显然,JDBC使用Statement的方式进行数据库操作是很危险的,因此一般开发人员会使用PreparedStatement做预编译。
PreparedStatement
与Statement的区别在于,PreparedStatement会对SQL语句进行预编译。无论最后输入的是什么,预编译的语句只是作为字符串来执行。因此,在正确使用PreparedStatement的前提下,解决了注入问题。
测试代码
<code>public static void select(String username, String password) throws Exception { Connection connection = JdbcSqli.connector(); String sql = "SELECT * FROM user WHERE username = ? AND password = ?"; PreparedStatement preparedStatement = connection.prepareStatement(sql); preparedStatement.setString(1, username); preparedStatement.setString(2, password); ResultSet resultSet = preparedStatement.executeQuery(); while (resultSet.next()) { System.out.println("id=" + resultSet.getObject("id") + ", username=" + resultSet.getObject("username") + ", password=" + resultSet.getObject("password")); } preparedStatement.close(); connection.close(); }</code>
在测试代码中,通过使用PreparedStatement的方式进行数据库操作,有效地防止了SQL注入。PreparedStatement的防御原理在于使用?
作为占位符,预编译后的语句已经确定整个SQL语句的结构,因此即使攻击者传入SQL语句也不会被数据库解析。
PreparedStatement 防御SQL原理
通过查看PreparedStatement的源代码,我们可以了解其防御SQL注入的原理。
PreparedStatement在设置参数时,会对传入的字符串进行处理,将特殊字符进行转义。这样一来,即使用户输入的字符串中包含了SQL语句的关键字符,也不会对SQL语句造成破坏。
总结
虽然使用PreparedStatement可以有效防御大部分SQL注入攻击,但在某些情况下仍然存在注入的风险。例如,当开发人员拼接用户输入、使用In语句、Like语句或Order by语句时,都有可能导致SQL注入漏洞的产生。因此,在编写代码时,务必谨慎处理用户输入,避免造成安全隐患。
暂无评论内容