我知道PreparedStatements可以避免/防止SQL注入。它是如何做到的?使用PreparedStatements构造的最终表单查询是否为字符串?
我知道PreparedStatements可以避免/防止SQL注入。它是如何做到的?使用PreparedStatements构造的最终表单查询是否为字符串?
Answers:
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();
如果“用户”来自用户输入,并且用户输入为
Robert'); DROP TABLE students; --
然后,首先,您将被迫接管。第二,为了安全起见,Little Bobby Tables将为您的学校注册。
要了解PreparedStatement如何防止SQL注入,我们需要了解SQL查询执行的各个阶段。
1.编译阶段。2.执行阶段。
每当SQL Server引擎收到查询时,它都必须经过以下阶段,
解析和规范化阶段: 在此阶段,将检查查询的语法和语义。它检查查询中使用的引用表和列是否存在。它还有许多其他任务要做,但让我们不再详细介绍。
编译阶段: 在此阶段,将查询中使用的关键字(例如select,from,where等)转换为机器可理解的格式。这是解释查询并决定要采取的措施的阶段。它还有许多其他任务要做,但让我们不再详细介绍。
查询优化计划: 在此阶段,创建决策树以查找执行查询的方式。它找出执行查询的方式数量以及与执行查询的每种方式相关的成本。它选择执行查询的最佳计划。
高速缓存: 在查询优化计划中选择的最佳计划存储在高速缓存中,这样,每当下一次出现相同查询时,就不必再次通过阶段1,阶段2和阶段3。下次查询到来时,将直接在Cache中对其进行检查并从那里进行查询。
执行阶段:
在此阶段,将执行提供的查询,并将数据作为ResultSet
对象返回给用户。
PreparedStatement不是完整的SQL查询,并且包含占位符,这些占位符在运行时被用户提供的实际数据替换。
每当任何包含占位符的PreparedStatment传递到SQL Server引擎时,它都会通过以下阶段
UPDATE用户集username =?和密码=?id =?
上面的查询将被解析,使用占位符作为特殊处理进行编译,优化并进行缓存。此阶段的查询已经以机器可理解的格式进行编译和转换。因此,可以说存储在缓存中的查询是预编译的,仅占位符需要替换为用户提供的数据。
现在,在运行时,当提供用户提供的数据时,将从缓存中提取预编译查询,并将占位符替换为用户提供的数据。
(请记住,在用用户数据替换占位符之后,不会再次编译/解释最终查询,并且SQL Server引擎将用户数据视为纯数据,而不是需要再次分析或编译的SQL;这就是PreparedStatement的美。 )
如果查询不必再次经历编译阶段,则在占位符上替换的所有数据都将被视为纯数据,并且对SQL Server引擎没有任何意义,它将直接执行查询。
注意:是在解析阶段之后的编译阶段,它了解/解释查询结构并为其赋予有意义的行为。在PreparedStatement的情况下,查询仅被编译一次,并且缓存的已编译查询始终被拾取以替换用户数据并执行。
由于PreparedStatement具有一次编译功能,因此它不受SQL注入攻击。
您可以在此处通过示例获取详细说明:https : //javabypatel.blogspot.com/2015/09/how-prepared-statement-in-java-prevents-sql-injection.html
在PreparedStatement中使用的SQL已在驱动程序上预编译。从那时起,参数作为文字值发送到驱动程序,而不是SQL的可执行部分。因此,无法使用参数来注入SQL。PreparedStatements的另一个有益的副作用(预编译+仅发送参数)是多次运行语句时提高了性能,即使参数的值不同(假设驱动程序支持PreparedStatements),因为该驱动程序不必分别执行SQL解析和编译时间改变。
我想这将是一个字符串。但是输入参数将被发送到数据库,并且在创建实际的SQL语句之前将应用适当的转换/转换。
举一个例子,它可能会尝试查看CAST / Conversion是否起作用。
如果可行,可以从中创建最终声明。
SELECT * From MyTable WHERE param = CAST('10; DROP TABLE Other' AS varchar(30))
尝试使用一个接受数字参数的SQL语句的示例。
现在,尝试传递字符串变量(具有可接受的数字内容作为数字参数)。它会引发任何错误吗?
现在,尝试传递一个字符串变量(其内容不可接受作为数字参数)。走着瞧吧?
SQL注入:当用户有机会输入可能属于sql语句的内容时
例如:
字符串查询=“将学生插入VALUES('” +用户+“')”
当用户输入“ Robert”时);DROP TABLE学生;–”作为输入,它将导致SQL注入
准备好的语句如何防止这种情况?
字符串查询=“向学生插入VALUES('” +“:name” +“')”
parameters.addValue(“ name”,用户);
=>当用户再次输入“ Robert”)时;DROP TABLE学生;–”,输入字符串在驱动程序上作为文字值进行了预编译,我想它可能会像这样强制转换:
CAST('Robert'); DROP TABLE学生;–'AS varchar(30))
因此,最后,该字符串将作为表的名称逐字插入。
http://blog.linguiming.com/index.php/2018/01/10/why-prepared-statement-avoids-sql-injection/
CAST(‘Robert’);
from的那部分CAST(‘Robert’); DROP TABLE students; –‘ AS varchar(30))
会中断,那么如果是这样的话,它将继续删除该表。它确实停止了注入,因此我认为该示例还不够完整,无法解释该情况。
PreparedStatement:
1)SQL语句的预编译和数据库侧缓存可提高整体执行速度,并能够批量重用同一SQL语句。
2)通过对引号和其他特殊字符进行内置转义,自动防止SQL注入攻击。请注意,这要求您使用任何PreparedStatement setXxx()方法来设置值。
正如在解释这个帖子中,PreparedStatement
本身并不能帮助你,如果你仍然在连接字符串。
例如,一个恶意攻击者仍然可以执行以下操作:
如果不使用绑定参数,则不仅会损害SQL,甚至会损害JPQL或HQL。
最重要的是,在构建SQL语句时,绝对不要使用字符串连接。为此使用专用的API:
在“ Prepared Statements”中,用户被迫输入数据作为参数。如果用户输入一些易受攻击的语句(例如DROP TABLE或SELECT * FROM USERS),则数据不会受到影响,因为这些将被视为SQL语句的参数