我知道 PreparedStatement 可以避免/阻止 SQL 注入。它是怎么做到的?使用 PreparedStatments 构造的最终表单查询是否为字符串?
SQL 注入的问题在于,用户输入被用作 SQL 语句的一部分。通过使用准备好的语句,可以强制将用户输入作为参数的内容(而不是作为 SQL 命令的一部分)进行处理。
但是,如果不使用用户输入作为准备语句的参数,而是通过将字符串连接在一起来构建 SQL 命令,那么即使使用准备语句,也是 仍然容易受到 SQL 注入的影响。
想想做同样事情的两种方法:
PreparedStatement stmt = conn.createStatement("INSERT INTO students VALUES('" + user + "')"); stmt.execute();
或者
PreparedStatement stmt = conn.prepareStatement("INSERT INTO student VALUES(?)"); stmt.setString(1, user); stmt.execute();
如果“ user”来自用户输入,而用户输入是
Robert'); DROP TABLE students; --
那么一开始,你就会被淘汰。第二,你就安全了“小鲍比桌子”会在你的学校注册。
它将是一个字符串。但是输入参数将被发送到数据库,并且在创建一个实际的 SQL 语句之前应用适当的强制转换/转换。
为了给您一个示例,它可能会尝试查看 CAST/Conversion 是否有效。 如果成功了,它可以创建一个最终的声明。
SELECT * From MyTable WHERE param = CAST('10; DROP TABLE Other' AS varchar(30))
尝试使用 SQL 语句接受数值参数的示例。 现在,尝试传递一个字符串变量(其中包含可以作为数值参数接受的数值内容)?
现在,尝试传递一个字符串变量(包含不能作为数值参数接受的内容)?
PreparedStatement 中使用的 SQL 是在驱动程序上预编译的。从那时起,参数作为文本值而不是 SQL 的可执行部分发送给驱动程序; 因此,不能使用参数注入 SQL。PreparedStatments (预编译 + 仅发送参数)的另一个有益的副作用是,当多次运行语句时,即使参数值不同(假设驱动程序支持 PreparedStatments) ,性能也会提高,因为驱动程序不必在每次参数变化时执行 SQL 解析和编译。
语句更安全。它将参数转换为指定的类型。
例如,stmt.setString(1, user);将 user参数转换为 String。
stmt.setString(1, user);
user
假设使用准备语句的参数 包含包含可执行命令的 SQL 字符串: 不允许这样做。
它添加了元字符(也称为自动转换)。
这样更安全。
准备声明:
1) SQL 语句的预编译和 DB 端缓存导致总体上更快的执行速度,并且能够批量重用相同的 SQL 语句。
2)通过引号和其他特殊字符的内置转义自动防止 SQL 注入攻击。注意,这需要使用任何 PreparedStatement setXxx ()方法来设置值。
为了理解 PreparedStatement 如何防止 SQL 注入,我们需要理解 SQL 查询执行的各个阶段。
1. 编制阶段。 2. 执行阶段 每当 SQL 服务器引擎接收到一个查询,它必须通过以下阶段, 解析和规范化阶段: 在这个阶段,查询被检查语法和语义。它检查是否引用表和 查询中使用的列是否存在。 它还有许多其他任务要完成,但我们不要详细讨论 编制阶段: 在这个阶段,查询中使用的关键字如 select、 from、 where 等被转换成格式 机器所能理解的。 这是解释查询并确定要采取的相应操作的阶段。 它还有许多其他任务要完成,但我们不要详细讨论 查询优化计划: 在这个阶段,创建决策树是为了找到执行查询的方法。 它找出可以执行查询的方法的数量以及每种方法的相关成本 执行 Query。 它选择执行查询的最佳计划 缓存: 在查询优化计划中选择的最佳计划存储在缓存中,以便下次 当相同的查询进入时,它不必再次通过第1阶段、第2阶段和第3阶段。 当下次查询进入时,它将直接在 Cache 中检查并从那里提取 执行 执行阶段: 在这个阶段,执行提供的查询并将数据作为 ResultSet对象返回给用户。 PreparedStatement API 在上述步骤中的行为 PreparedStatement 不是完整的 SQL 查询,并且包含占位符, 它在运行时被实际的用户提供的数据所替代。 每当将包含占位符的任何 PreparedStatement 传递到 SQLServer 引擎时, 它通过下面的阶段 解析和规范化阶段 编制阶段 查询优化计划 缓存(带占位符的编译查询存储在缓存中。) UPDATE 用户集 username = ? 和 password = ? WHERE id = ? 以上查询将得到解析,编译占位符作为特殊处理,优化和 得到缓存。 此阶段的查询已经以机器可理解的格式进行编译和转换。 所以我们可以说,存储在缓存中的查询是预编译的,并且 只有占位符需要替换为用户提供的数据 现在,在运行时,当用户提供的数据进入时,从 Cache 拾取预编译查询,并用用户提供的数据替换占位符。 (记住,占位符被用户数据替换后,最终的查询不是 再次编译/解释,并且 SQLServer 引擎将用户数据视为纯数据而不是 需要重新解析或编译的 SQL; 这就是 PreparedStatement 的美妙之处。) 如果查询不必再次经历编译阶段,那么在 占位符被视为纯数据,对 SQLServer 引擎和它直接没有意义 执行查询。 注意: 解析阶段之后是编译阶段,它理解/解释查询 结构,并为其提供有意义的行为 只编译一次,缓存的已编译查询将一直被拾取以替换 用户数据并执行 由于 PreparedStatement 具有一次性编译特性,因此不需要 SQL 注入 攻击。 你可以通过这里的例子得到详细的解释: Https://javabypatel.blogspot.com/2015/09/how-prepared-statement-in-java-prevents-sql-injection.html
每当 SQL 服务器引擎接收到一个查询,它必须通过以下阶段,
解析和规范化阶段: 在这个阶段,查询被检查语法和语义。它检查是否引用表和 查询中使用的列是否存在。 它还有许多其他任务要完成,但我们不要详细讨论
编制阶段: 在这个阶段,查询中使用的关键字如 select、 from、 where 等被转换成格式 机器所能理解的。 这是解释查询并确定要采取的相应操作的阶段。 它还有许多其他任务要完成,但我们不要详细讨论
查询优化计划: 在这个阶段,创建决策树是为了找到执行查询的方法。 它找出可以执行查询的方法的数量以及每种方法的相关成本 执行 Query。 它选择执行查询的最佳计划
缓存: 在查询优化计划中选择的最佳计划存储在缓存中,以便下次 当相同的查询进入时,它不必再次通过第1阶段、第2阶段和第3阶段。 当下次查询进入时,它将直接在 Cache 中检查并从那里提取 执行
执行阶段: 在这个阶段,执行提供的查询并将数据作为 ResultSet对象返回给用户。
ResultSet
PreparedStatement 不是完整的 SQL 查询,并且包含占位符, 它在运行时被实际的用户提供的数据所替代。
每当将包含占位符的任何 PreparedStatement 传递到 SQLServer 引擎时, 它通过下面的阶段
UPDATE 用户集 username = ? 和 password = ? WHERE id = ?
以上查询将得到解析,编译占位符作为特殊处理,优化和 得到缓存。 此阶段的查询已经以机器可理解的格式进行编译和转换。 所以我们可以说,存储在缓存中的查询是预编译的,并且 只有占位符需要替换为用户提供的数据
现在,在运行时,当用户提供的数据进入时,从 Cache 拾取预编译查询,并用用户提供的数据替换占位符。
(记住,占位符被用户数据替换后,最终的查询不是 再次编译/解释,并且 SQLServer 引擎将用户数据视为纯数据而不是 需要重新解析或编译的 SQL; 这就是 PreparedStatement 的美妙之处。)
如果查询不必再次经历编译阶段,那么在 占位符被视为纯数据,对 SQLServer 引擎和它直接没有意义 执行查询。
注意: 解析阶段之后是编译阶段,它理解/解释查询 结构,并为其提供有意义的行为 只编译一次,缓存的已编译查询将一直被拾取以替换 用户数据并执行 由于 PreparedStatement 具有一次性编译特性,因此不需要 SQL 注入 攻击。 你可以通过这里的例子得到详细的解释: Https://javabypatel.blogspot.com/2015/09/how-prepared-statement-in-java-prevents-sql-injection.html
由于 PreparedStatement 具有一次性编译特性,因此不需要 SQL 注入 攻击。
你可以通过这里的例子得到详细的解释: Https://javabypatel.blogspot.com/2015/09/how-prepared-statement-in-java-prevents-sql-injection.html
如果您仍然在连接字符串,仅凭 PreparedStatement 无法帮助您。
PreparedStatement
例如,一个流氓攻击者仍然可以做以下事情:
如果不使用绑定参数,不仅 SQL,甚至 JPQL 或 HQL 都可能受到损害。
底线是,在构建 SQL 语句时不应使用字符串串联。为此使用专用的 API,如 JPA Criteria API。
SQL 注入: 当用户有机会输入可能是 SQL 语句一部分的内容时
例如:
字符串查询 = “ INSERT INTO Student VALUES (‘”+ user + “’)”
当用户输入“ Robert”; DROPTABLE Student;-”作为输入时,它将导致 SQL 注入
准备语句如何防止这种情况?
字符串查询 = “ INSERT INTO Student VALUES (‘”+ “ : name”+ “’)”
AddValue (“ name”,user) ;
当用户再次输入“ Robert”时,输入字符串在驱动程序上被预先编译为文本值,我猜它可能是这样转换的:
CAST (‘ Robert’) ; 放下表格学生;-‘ AS varchar (30))
所以在最后,字符串将作为名称插入到表中。
Http://blog.linguiming.com/index.php/2018/01/10/why-prepared-statement-avoids-sql-injection/
在“准备好的语句”中,用户必须将数据作为参数输入。如果用户输入一些易受攻击的语句,如 DROP TABLE 或 SELECT * FROM USERS,那么数据不会受到影响,因为这些将被视为 SQL 语句的参数