在Java程序中防止SQL注入攻击

15

我需要在我的Java程序中添加一条语句来更新数据库表:

String insert =
    "INSERT INTO customer(name,address,email) VALUES('" + name + "','" + addre + "','" + email + "');";

我听说这个可以通过SQL注入来利用,例如:

DROP TABLE customer; 

我的程序有一个Java GUI,所有的姓名、地址和电子邮件值都是从 Jtextfields 中获取的。我想知道如何通过黑客向我的插入语句中添加以下代码 (DROP TABLE customer;),以及如何防止这种情况发生。


10
必看XKCD漫画 - DNA
1
可能是Java - 转义字符串以防止SQL注入的重复问题。 - Nate
9个回答

26

你需要使用 PreparedStatement

String insert = "INSERT INTO customer(name,address,email) VALUES(?, ?, ?);";
PreparedStatement ps = connection.prepareStatement(insert);
ps.setString(1, name);
ps.setString(2, addre);
ps.setString(3, email);

ResultSet rs = ps.executeQuery();

这将防止注入攻击。

黑客的做法是,如果你要插入的字符串来自某个输入 - 例如网页上的输入字段,应用程序中的表单输入字段或类似的输入位置。


这确实可以在一定程度上防止 SQL 注入攻击,因为它将代码与数据分离。以下是一个简短的教程,介绍如何使用 Java JDBC 的预处理语句。 - drorw

15

我想知道黑客如何将这段代码(“DROP TABLE customer;”)添加到我的插入语句中。

例如:

name = "'); DROP TABLE customer; --"

将会产生这个值插入到insert中:

INSERT INTO customer(name,address,email)     VALUES(''); DROP TABLE customer; --"','"+addre+"','"+email+"');

我特别想知道如何防止这种情况发生

使用预处理语句和 SQL 参数(下面的示例是从 Matt Fellows “窃取”的):

String insert = "INSERT INTO customer(name,address,email) VALUES(?, ?, ?);";
PreparedStament ps = connection.prepareStatment(insert);

还要解析你所拥有的变量的值,并确保它们不包含任何非允许字符(例如名称中的“;”)。


我正在使用GUI。用户只能通过文本字段输入值。他们如何将此代码片段添加到我的语句中,例如: INSERT INTO customer(name,address,email)VALUES(''; DROP TABLE customer; -“','"+addre+"','"+email+"); - Grant
2
为什么你不会在文本字段中输入 '*'); DROP TABLE customer; --*?反正,一个恶意用户可以黑掉那些文本字段的限制(例如,直接修改 RAM、注入伪造的网络数据包等等……)。 - m0skit0
1
我不想自己重写它 :D - m0skit0
1
@m0skit0 很抱歉回复晚了。一个理想的攻击向量可能是一个受限制的系统,用户没有管理员权限甚至无法逃脱到主操作系统。拥有一个具有 SQL 注入可能性和键盘输入的文本框可以允许窃取凭据和存储在数据库中的其他信息。您如何在咖啡店客户计算机上编辑 RAM,即使您可以,也无法从程序配置中获取授权凭据并对服务器造成破坏吗? - hegez

7
您可以查看文章了解相关信息! :)

我推荐使用参数化查询:

String selectStatement = "SELECT * FROM User WHERE userId = ? ";
PreparedStatement prepStmt = con.prepareStatement(selectStatement);
prepStmt.setString(1, userId);
ResultSet rs = prepStmt.executeQuery();

5

攻击者只需在email字段中输入类似于'foo@example.com"); DROP TABLE customer;的内容,你的系统就会受到攻击。

你可以通过使用JDBC语句的适当转义来防止这种情况发生。


2
这就是为什么在字符串语句中应该使用问号的原因:
 PreparedStatement pstmt = con.prepareStatement("UPDATE EMPLOYEES
                                     SET SALARY = ? WHERE ID = ?");
   pstmt.setBigDecimal(1, 153833.00)
   pstmt.setInt(2, 110592)

quoted from here


2

正如这篇文章所解释的那样,如果您仍在连接字符串,PreparedStatement本身并不能帮助您。

例如,一个恶意攻击者仍然可以执行以下操作:

  • 调用休眠函数以使所有数据库连接都处于繁忙状态,从而导致应用程序不可用
  • 从DB中提取敏感数据
  • 绕过用户身份验证

而且不仅是 SQL,如果您没有使用绑定参数,JPQL 和 HQL 也可能受到威胁:

PreparedStatement ps = connection.prepareStatement(
    INSERT INTO customer(name,address,email) VALUES(?, ?, ?)
);
int index = 0;
ps.setString(++index, name);
ps.setString(++index, address);
ps.setString(++index, email);

ResultSet rs = ps.executeQuery();

总之,在构建SQL语句时,不应该使用字符串拼接。应该使用专门的API:


1

您还应该尽可能地限制访问数据库的帐户的权限。例如,对于搜索,帐户只需要读取所需的表和列即可。这将防止任何有害的SQL注入并限制对敏感数据的访问。


1

使用PreparedStatement的好处:

SQL语句的预编译和数据库缓存使得执行速度更快,同时还能在批处理中重复使用同一条SQL语句。

通过内置引号和其他特殊字符的转义,自动防止SQL注入攻击。需要注意的是,这要求您使用PreparedStatement的任何setXxx()方法来设置值。


0

尽管其他答案告诉你如何在Java中修复SQL注入,Mukesh Kumar的回答实际上告诉你谁实际上防止了这种攻击。请注意,实际上是DB服务器通过提供使用参数化查询的建议来防止SQL注入攻击,只要您作为程序员遵循他们的建议。

参考此处-预防SQL注入漏洞

对于Java程序员来说,无法对每个输入字符串进行消毒处理,因此DB供应商为我们提供了准备语句的选项,并告诉我们通过使用该选项准备和执行查询,其余的事情将由DB供应商处理。

DROP TABLE customer;这样的极端情况可能不会发生,但是SQL注入的基本前提是没有人能够通过提供无效输入(有意或无意)来破坏您的查询。

OWASP - SQL注入预防备忘单


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接