使用 JOOQ,我还需要什么来防止 sql 注入
Using JOOQ, what more do I need to prevent sql injections
这是怎么重复的,因为我在这里专门询问 JOOQ?
我在我的 JAVA 项目中使用 JOOQ 来处理我所有的 PostgreSQL 查询。我在 this 文章中读到 JOOQ 使用准备好的语句来执行所有查询。
假设我在执行查询时不需要担心 SQL 注入或用户输入是否安全?
我不需要担心在将用户输入交给 JOOQ 之前转义吗?
附带说明一下,我的数据库在获取用户输入方面还有哪些其他漏洞(除了那些由准备好的语句解决的漏洞),我应该注意这些漏洞?
1) 可以,只要您正确使用提供的API。仍然可以注入普通 sql 查询,但要小心。
All methods in the jOOQ API that allow for plain (unescaped, untreated) SQL contain a warning message in their relevant Javadoc
// This query will use bind values, internally.
create.fetch("SELECT * FROM BOOK WHERE ID = ? AND TITLE = ?", 5, "Animal Farm");
// This query will not use bind values, internally.
create.fetch("SELECT * FROM BOOK WHERE ID = 5 AND TITLE = 'Animal Farm'");
在此处查看 JOOQ 文档以获得更深入的解释:https://www.jooq.org/doc/3.9/manual/sql-building/bind-values/sql-injection/
2) 否,见上文。
3) 除此之外,请注意一般的数据库安全问题,例如用户 authentication/roles 和以未加密格式存储敏感数据等
无论您使用何种语言和框架,编写不安全的查询总是有可能的。
将变量连接到 SQL 的天真方式为 SQL 注入创造了机会:
String unsafeString = "O'Reilly";
create.fetch("SELECT * FROM BOOK WHERE ID = 5 AND TITLE = '"+unsafeString+"'");
// results in SQL syntax error because of unmatched ' marks
仅使用准备好的查询不会将不安全查询变成安全查询。
使用参数将动态值与 SQL 查询分开。这些在执行时在 RDBMS 中组合。参数不可能导致 SQL 注入漏洞。
String unsafeString = "O'Reilly";
create.fetch("SELECT * FROM BOOK WHERE ID = 5 AND TITLE = ?", unsafeString);
// still OK
使用参数时,不需要对变量进行任何转义。事实上,你 一定不能, 因为你最终会在数据中使用转义符号。
参数适用于将 Java 变量组合到 SQL 查询中,但只能代替 SQL 标量值。也就是说,您通常会在 SQL 中使用带引号的字符串文字、带引号的日期文字或数字文字,您可以将其替换为参数占位符。
但是在 SQL:
中你不能对其他任何东西使用参数
- Table 名字
- 列名称
- 值列表,例如
IN ( ... )
谓词——您必须为列表中的每个值使用一个 ?
占位符。
- SQL 表达式
- SQL 关键词
你可能会喜欢我的介绍SQL Injection Myths and Fallacies (video), or my book, SQL Antipatterns: Avoiding the Pitfalls of Database Programming
来自@rehas 的回复:
的确,使用准备好的语句 而不是 意味着您正在隐式使用参数。我在上面展示了一个例子(我的第一个例子),在它被发送到 prepare().
之前将一个不安全的变量连接成一个 SQL 字符串
一旦 SQL 字符串到达 RDBMS 服务器,它就无法知道字符串的哪些部分是合法的,哪些部分是由不安全变量连接而成的。它所看到的只是一个包含 SQL 语句的字符串。
使用参数的目的是将(可能不安全的)变量与 SQL 字符串分开。在 RDBMS 服务器中,SQL 字符串(仍然带有参数占位符,如 ?
)被解析。一旦它被解析,它就不会被再次解析,所以像 "O'Reilly" 这样的字符串绑定到参数占位符是安全的,而不会有导致不匹配的引号或任何东西的风险。保证在 SQL 执行中将参数视为单个值,即使参数的值包含会改变查询解析方式的字符,如果它已被包含 之前 准备()。
使用 prepare() 并不意味着您总是在使用参数。
准确地说,使用参数需要使用 prepare() 和 execute() 作为单独的步骤。但是有些框架会为您完成这两个步骤。我敢肯定,如果您阅读了 jOOQ 源代码,就会看到它。
按预期使用 jOOQ 时风险很小
当您按预期使用 jOOQ 时,您将 运行 降低 SQL 注入的风险。预期用途是:
- 使用 source code generation 为您的表/列等生成元数据
- 使用 DSL for type safe embedded SQL
正如其他人所提到的,jOOQ 将始终使用 bind variables, properly escape all inlined values (constants, literals). But again, as others have mentioned, jOOQ still allows for using plain SQL templating 来解决您需要解决缺乏功能或供应商特定功能支持的情况。在这些情况下,您必须像使用 JDBC 一样小心,并确保自己显式使用绑定变量并避免字符串连接。
使用 PlainSQLChecker
注释处理器防止事故发生
防止意外使用普通SQL模板,并确保团队中没有人未经批准使用它的一种方法是使用jOOQ's checker framework / error prone integration并默认禁止所有普通 SQL 用法。使用 Maven,您可以配置它(省略 JDK 版本特定的详细信息):
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<annotationProcessors>
<annotationProcessor>org.jooq.checker.PlainSQLChecker</annotationProcessor>
</annotationProcessors>
</configuration>
</plugin>
现在您的代码在您选择的范围内使用 DSL.query(String)
won't compile anymore, until you explicitly allow it with the @Allow.PlainSQL
注释之类的方法(方法、class、包)
这是怎么重复的,因为我在这里专门询问 JOOQ?
我在我的 JAVA 项目中使用 JOOQ 来处理我所有的 PostgreSQL 查询。我在 this 文章中读到 JOOQ 使用准备好的语句来执行所有查询。
假设我在执行查询时不需要担心 SQL 注入或用户输入是否安全?
我不需要担心在将用户输入交给 JOOQ 之前转义吗?
附带说明一下,我的数据库在获取用户输入方面还有哪些其他漏洞(除了那些由准备好的语句解决的漏洞),我应该注意这些漏洞?
1) 可以,只要您正确使用提供的API。仍然可以注入普通 sql 查询,但要小心。
All methods in the jOOQ API that allow for plain (unescaped, untreated) SQL contain a warning message in their relevant Javadoc
// This query will use bind values, internally.
create.fetch("SELECT * FROM BOOK WHERE ID = ? AND TITLE = ?", 5, "Animal Farm");
// This query will not use bind values, internally.
create.fetch("SELECT * FROM BOOK WHERE ID = 5 AND TITLE = 'Animal Farm'");
在此处查看 JOOQ 文档以获得更深入的解释:https://www.jooq.org/doc/3.9/manual/sql-building/bind-values/sql-injection/
2) 否,见上文。
3) 除此之外,请注意一般的数据库安全问题,例如用户 authentication/roles 和以未加密格式存储敏感数据等
无论您使用何种语言和框架,编写不安全的查询总是有可能的。
将变量连接到 SQL 的天真方式为 SQL 注入创造了机会:
String unsafeString = "O'Reilly";
create.fetch("SELECT * FROM BOOK WHERE ID = 5 AND TITLE = '"+unsafeString+"'");
// results in SQL syntax error because of unmatched ' marks
仅使用准备好的查询不会将不安全查询变成安全查询。
使用参数将动态值与 SQL 查询分开。这些在执行时在 RDBMS 中组合。参数不可能导致 SQL 注入漏洞。
String unsafeString = "O'Reilly";
create.fetch("SELECT * FROM BOOK WHERE ID = 5 AND TITLE = ?", unsafeString);
// still OK
使用参数时,不需要对变量进行任何转义。事实上,你 一定不能, 因为你最终会在数据中使用转义符号。
参数适用于将 Java 变量组合到 SQL 查询中,但只能代替 SQL 标量值。也就是说,您通常会在 SQL 中使用带引号的字符串文字、带引号的日期文字或数字文字,您可以将其替换为参数占位符。
但是在 SQL:
中你不能对其他任何东西使用参数- Table 名字
- 列名称
- 值列表,例如
IN ( ... )
谓词——您必须为列表中的每个值使用一个?
占位符。 - SQL 表达式
- SQL 关键词
你可能会喜欢我的介绍SQL Injection Myths and Fallacies (video), or my book, SQL Antipatterns: Avoiding the Pitfalls of Database Programming
来自@rehas 的回复:
的确,使用准备好的语句 而不是 意味着您正在隐式使用参数。我在上面展示了一个例子(我的第一个例子),在它被发送到 prepare().
之前将一个不安全的变量连接成一个 SQL 字符串一旦 SQL 字符串到达 RDBMS 服务器,它就无法知道字符串的哪些部分是合法的,哪些部分是由不安全变量连接而成的。它所看到的只是一个包含 SQL 语句的字符串。
使用参数的目的是将(可能不安全的)变量与 SQL 字符串分开。在 RDBMS 服务器中,SQL 字符串(仍然带有参数占位符,如 ?
)被解析。一旦它被解析,它就不会被再次解析,所以像 "O'Reilly" 这样的字符串绑定到参数占位符是安全的,而不会有导致不匹配的引号或任何东西的风险。保证在 SQL 执行中将参数视为单个值,即使参数的值包含会改变查询解析方式的字符,如果它已被包含 之前 准备()。
使用 prepare() 并不意味着您总是在使用参数。
准确地说,使用参数需要使用 prepare() 和 execute() 作为单独的步骤。但是有些框架会为您完成这两个步骤。我敢肯定,如果您阅读了 jOOQ 源代码,就会看到它。
按预期使用 jOOQ 时风险很小
当您按预期使用 jOOQ 时,您将 运行 降低 SQL 注入的风险。预期用途是:
- 使用 source code generation 为您的表/列等生成元数据
- 使用 DSL for type safe embedded SQL
正如其他人所提到的,jOOQ 将始终使用 bind variables, properly escape all inlined values (constants, literals). But again, as others have mentioned, jOOQ still allows for using plain SQL templating 来解决您需要解决缺乏功能或供应商特定功能支持的情况。在这些情况下,您必须像使用 JDBC 一样小心,并确保自己显式使用绑定变量并避免字符串连接。
使用 PlainSQLChecker
注释处理器防止事故发生
防止意外使用普通SQL模板,并确保团队中没有人未经批准使用它的一种方法是使用jOOQ's checker framework / error prone integration并默认禁止所有普通 SQL 用法。使用 Maven,您可以配置它(省略 JDK 版本特定的详细信息):
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<annotationProcessors>
<annotationProcessor>org.jooq.checker.PlainSQLChecker</annotationProcessor>
</annotationProcessors>
</configuration>
</plugin>
现在您的代码在您选择的范围内使用 DSL.query(String)
won't compile anymore, until you explicitly allow it with the @Allow.PlainSQL
注释之类的方法(方法、class、包)