gpt4 book ai didi

jdbc - 在不使用 Prepared 语句的情况下摆脱 SQL 注入(inject)

转载 作者:行者123 更新时间:2023-12-05 01:01:00 25 4
gpt4 key购买 nike

我的应用程序有很多使用 Statement 编写的 JDBC 查询,因此容易受到 SQL 注入(inject)的攻击。不幸的是,这个应用程序是大约 10 年前开发的,开发人员可能不知道 Prepared statement。

我知道解决这个问题的最好方法是使用 PreparedStatement 但是在整个应用程序中转换它是非常繁琐的。此外,为 SQL 注入(inject)编写任何类型的 Patten 匹配都可能非常棘手,因为 Select、insert、union 等关键字都是英文单词,它们也可能出现在用户键入的文本字段中。

有没有更聪明的方法来避免 SQL 注入(inject)而不使用 Prepared 语句。如果这是一个重复的问题,请给我一个有很好答案的问题的链接。感谢您的帮助。

最佳答案

哈哈。不幸的是,它几乎总是取决于货币和管理决定什么是合适的,但“它非常乏味”通常不被认为是一个有效的工程问题——它只是适当重构代码的借口.

与此同时,该问题要求使用非 PreparedStatement 方法:简而言之,如果您无法将工作卸载到库中(例如准备好的语句),那么唯一的其他方法就是为每个“注入(inject)”项目自己完成。无论哪种方式,都必须执行检查输入的工作。唯一的问题是它在哪里完成,以及程序员的专业知识是什么制作了输入验证代码。

例如,考虑一个简单的 SELECT 语句:

    sql = "SELECT * FROM mytable WHERE id = " + untrustedVar;

为了完整起见,我们可以假设注入(inject)示例,其中 untrustedVar 是一个类似 1 OR 11 的字符串; DROP TABLE mytable; 显然这会导致不需要的行为,相对于返回给调用者的所有行,或者现在丢失的数据库表:

SELECT * FROM mytable WHERE id = 1;
DROP mytable;

Obligatory XKCD reference

在这种情况下,您可以让语言语义至少确保 unstrustedVar 是一个整数,也许在您的函数定义中:

String[] selectRowById ( int untrustedVar ) { ...

或者,如果它是一个字符串,您可以使用如下正则表达式执行此操作:

Pattern valid_id_re = Pattern.compile('^\d{1,10}$');  // ensure id is between 1 and 10 billion
Matcher m = valid_id_re.matcher( unstrustedVar );
if ( ! m.matches() )
return null;

但是,如果您的输入较长且没有任何语法或结构保证(例如,网络表单文本区域),那么您将需要进行较低级别的字符替换以转义潜在的错误字符。根据声明。每个变量。每个数据库风格(PostgreSQL、Oracle、MySQL、SQLite 等)。这......是一 jar 蠕虫。

当然,如果您不使用准备好的语句,并且还没有人完成其他工作来避免对您的应用程序进行 SQL 注入(inject)攻击,那么您别无选择,只能向上走。

与此同时,我敦促,敦促,敦促您重新考虑您对“我们不能使用准备好的陈述,因为某些原因”的立场。更重要的是,正如戈德·汤普森 (Gord Thompson) 在下面的评论中正确指出的那样,“在任何情况下,这都将是一项相当大的工作量,那么为什么不把它做好并完成它呢?”


编辑

写完上面的内容后,我突然想到有些人可能会认为仅仅编写准备好的语句 = 更好的安全性。实际上,它是带有绑定(bind)参数的准备好的语句,可以提高安全性。例如,可以这样写:

String sql = "SELECT * FROM mytable WHERE id = " + untrustedVar;
PreparedStatment pstmt = dbh.prepareStatement( sql );
return pstmt.executeQuery();

此时,您所做的只不过是准备了一个已被注入(inject)恶意代码的语句。相反,请考虑参数的实际绑定(bind):

String sql = "SELECT * FROM mytable WHERE id = ?";  // Raw string; never touched by "tainted" variable
PreparedStatment pstmt = dbh.prepareStatement( sql );
pstmt.setObject(1, p); // Perform the actual binding.
return pstmt.executeQuery();

后一个例子做了两件事。它首先创建一个已知的安全格式,然后将那个 发送到数据库进行准备。然后,DB 返回准备语句的句柄后,我们是否绑定(bind)变量,最后执行语句。

关于jdbc - 在不使用 Prepared 语句的情况下摆脱 SQL 注入(inject),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47272504/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com