PreparedStatement 如何避免或防止 SQL 注入?
我知道PreparedStatements 可以避免/防止SQL 注入。它是如何做到的?使用PreparedStatements 构造的最终表单查询是字符串还是其他形式?
I know that PreparedStatements avoid/prevent SQL Injection. How does it do that? Will the final form query that is constructed using PreparedStatements be a string or otherwise?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(10)
考虑做同样事情的两种方法:
或者
如果“用户”来自用户输入,并且用户输入是
那么在第一种情况下,你会被淹没。在第二种情况下,您会安全,并且小鲍比桌将在您的学校注册。
Consider two ways of doing the same thing:
Or
If "user" came from user input and the user input was
Then in the first instance, you'd be hosed. In the second, you'd be safe and Little Bobby Tables would be registered for your school.
要了解PreparedStatement如何防止SQL注入,我们需要了解SQL查询执行的阶段。
1.编译阶段。
2.执行阶段。
每当SQL Server引擎收到查询时,它必须经过以下阶段,
解析和规范化阶段:
在此阶段,检查查询的语法和语义。它检查引用表和
查询中使用的列是否存在。
它还还有许多其他任务要做,但我们不详细介绍。
编译阶段:
在此阶段,查询中使用的关键字(如 select、from、where 等)将转换为格式
机器可以理解。
这是解释查询并决定要采取的相应操作的阶段。
它还还有许多其他任务要做,但我们不详细介绍。
查询优化计划:
在此阶段,创建决策树以查找执行查询的方式。
它找出可以执行查询的方式数量以及与每种方式相关的成本
执行查询。
它选择执行查询的最佳计划。
缓存:
查询优化计划中选择的最佳计划存储在缓存中,以便下次查询时
当相同的查询进来时,它不必再次经过阶段1、阶段2和阶段3。
下次查询进来时,会直接在Cache中检查并从那里取出
执行。
执行阶段:
在此阶段,执行提供的查询,并将数据作为
ResultSet
对象返回给用户。PreparedStatement API 在上述步骤中的行为
PreparedStatements 不是完整的 SQL 查询并包含占位符,
在运行时被实际用户提供的数据替换。
每当任何包含占位符的PreparedStatment被传递到SQL Server引擎时,
它经历以下阶段
以上查询将被解析、使用占位符编译作为特殊处理、优化和
被缓存。
此阶段的查询已经编译并转换为机器可理解的格式。
所以我们可以说存储在缓存中的查询是预编译的并且
仅占位符需要替换为用户提供的数据。
现在,在运行时,当用户提供的数据到来时,将从缓存中获取预编译查询,并将占位符替换为用户提供的数据。
(记住,占位符替换为用户数据后,最终的查询不是
再次编译/解释,SQL Server 引擎将用户数据视为纯数据,而不是
需要重新解析或编译的SQL;这就是PreparedStatement的美妙之处。)
如果查询不必再次经过编译阶段,那么无论替换什么数据
占位符被视为纯数据,对 SQL Server 引擎没有任何意义,它直接
执行查询。
注意:解析阶段之后的编译阶段,理解/解释查询
结构并赋予其有意义的行为。对于PreparedStatement,查询是
仅编译一次,并且始终选取缓存的编译查询来替换
由于PreparedStatement的一次性编译特性,它不会受到SQL注入的影响
攻击。
您可以在这里通过示例获得详细说明:
https://javabypatel.blogspot .com/2015/09/how-prepared-statement-in-java-prevents-sql-injection.html
To understand how PreparedStatement prevents SQL Injection, we need to understand phases of SQL Query execution.
1. Compilation Phase.
2. Execution Phase.
Whenever SQL server engine receives a query, it has to pass through below phases,
Parsing and Normalization Phase:
In this phase, Query is checked for syntax and semantics. It checks whether references table and
columns used in query exist or not.
It also has many other tasks to do, but let's not go in detail.
Compilation Phase:
In this phase, keywords used in query like select, from, where etc are converted into format
understandable by machine.
This is the phase where query is interpreted and corresponding action to be taken is decided.
It also has many other tasks to do, but let's not go in detail.
Query Optimization Plan:
In this phase, Decision Tree is created for finding the ways in which query can be executed.
It finds out the number of ways in which query can be executed and the cost associated with each way
of executing Query.
It chooses the best plan for executing a query.
Cache:
Best plan selected in Query optimization plan is stored in cache, so that whenever next
time same query comes in, it doesn't have to pass through Phase 1, Phase 2 and Phase 3 again.
When next time query come in, it will be checked directly in Cache and picked up from there
to execute.
Execution Phase:
In this phase, supplied query gets executed and data is returned to user as
ResultSet
object.Behaviour of PreparedStatement API on above steps
PreparedStatements are not complete SQL queries and contain placeholder(s),
which at run time are replaced by actual user-provided data.
Whenever any PreparedStatment containing placeholders is passed in to SQL Server engine,
It passes through below phases
Above query will get parsed, compiled with placeholders as special treatment, optimized and
get Cached.
Query at this stage is already compiled and converted in machine understandable format.
So we can say that Query stored in cache is Pre-Compiled and
only placeholders need to be replaced with user-provided data.
Now at run-time when user-provided data comes in, Pre-Compiled Query is picked up from Cache and placeholders are replaced with user-provided data.
(Remember, after place holders are replaced with user data, final query is not
compiled/interpreted again and SQL Server engine treats user data as pure data and not a
SQL that needs to be parsed or compiled again; that is the beauty of PreparedStatement.)
If the query doesn't have to go through compilation phase again, then whatever data replaced on the
placeholders are treated as pure data and has no meaning to SQL Server engine and it directly
executes the query.
Note: It is the compilation phase after parsing phase, that understands/interprets the query
structure and gives meaningful behavior to it. In case of PreparedStatement, query is
compiled only once and cached compiled query is picked up all the time to replace
user data and execute.
Due to one time compilation feature of PreparedStatement, it is free of SQL Injection
attack.
You can get detailed explanation with example here:
https://javabypatel.blogspot.com/2015/09/how-prepared-statement-in-java-prevents-sql-injection.html
SQL 注入的问题在于,用户输入被用作 SQL 语句的一部分。通过使用准备好的语句,您可以强制将用户输入作为参数的内容进行处理(而不是作为 SQL 命令的一部分)。
但是,如果您不使用用户输入作为准备好的语句的参数,而是通过将字符串连接在一起来构建 SQL 命令,那么即使使用准备好的语句,您仍然容易受到 SQL 注入的攻击。
The problem with SQL injection is, that a user input is used as part of the SQL statement. By using prepared statements you can force the user input to be handled as the content of a parameter (and not as a part of the SQL command).
But if you don't use the user input as a parameter for your prepared statement but instead build your SQL command by joining strings together, you are still vulnerable to SQL injections even when using prepared statements.
在PreparedStatement 中使用的SQL 是在驱动程序上预编译的。从那时起,参数将作为文字值而不是 SQL 的可执行部分发送到驱动程序;因此不能使用参数注入 SQL。 PreparedStatements(预编译+仅发送参数)的另一个有益的副作用是,即使参数值不同(假设驱动程序支持PreparedStatements),多次运行该语句时也会提高性能,因为驱动程序不必每次都执行 SQL 解析和编译参数改变的时间。
The SQL used in a PreparedStatement is precompiled on the driver. From that point on, the parameters are sent to the driver as literal values and not executable portions of SQL; thus no SQL can be injected using a parameter. Another beneficial side effect of PreparedStatements (precompilation + sending only parameters) is improved performance when running the statement multiple times even with different values for the parameters (assuming that the driver supports PreparedStatements) as the driver does not have to perform SQL parsing and compilation each time the parameters change.
我猜它将是一个字符串。但输入的参数会被发送到数据库&在创建实际的 SQL 语句之前将应用适当的转换/转换。
举个例子,它可能会尝试看看 CAST/转换是否有效。
如果可行的话,它可以创建一个最终声明。
尝试使用接受数字参数的 SQL 语句示例。
现在,尝试传递一个字符串变量(具有可接受作为数字参数的数字内容)。它会引发任何错误吗?
现在,尝试传递一个字符串变量(其内容不可接受作为数字参数)。看看会发生什么?
I guess it will be a string. But the input parameters will be sent to the database & appropriate cast/conversions will be applied prior to creating an actual SQL statement.
To give you an example, it might try and see if the CAST/Conversion works.
If it works, it could create a final statement out of it.
Try an example with a SQL statement accepting a numeric parameter.
Now, try passing a string variable (with numeric content that is acceptable as numeric parameter). Does it raise any error?
Now, try passing a string variable (with content that is not acceptable as numeric parameter). See what happens?
准备好的语句更安全。它将参数转换为指定类型。
例如,
stmt.setString(1, user);
会将user
参数转换为字符串。假设参数包含一个包含可执行命令的 SQL 字符串:使用准备好的语句将不允许这样做。
它添加了元字符(又名自动转换)。
这使得它更加安全。
Prepared statement is more secure. It will convert a parameter to the specified type.
For example
stmt.setString(1, user);
will convert theuser
parameter to a String.Suppose that the parameter contains a SQL string containing an executable command: using a prepared statement will not allow that.
It adds metacharacter (a.k.a. auto conversion) to that.
This makes it is more safe.
SQL注入:当用户有机会输入可能成为sql语句一部分的内容
时例如:
String query =“INSERT INTO Students VALUES('”+ user +“')”
当用户输入“Robert”时;丢掉桌子的学生; –”作为输入,会导致SQL注入,
准备好的语句如何防止这种情况发生?
字符串查询=“INSERT INTO Students VALUES('”+“:name”+“')”
parameters.addValue(“name”, user);
=>当用户再次输入“Robert”时);丢掉桌子的学生; –“,输入字符串在驱动程序上预编译为文字值,我猜它可能会被转换为:
CAST('Robert');丢掉桌子的学生; –' AS varchar(30))
因此,最后,该字符串将作为名称逐字插入到表中。
http://blog .linguiming.com/index.php/2018/01/10/why-prepared-statement-avoids-sql-injection/
SQL injection: when user has the chance to input something that could be part of the sql statement
For example:
String query = “INSERT INTO students VALUES(‘” + user + “‘)”
when user input “Robert’); DROP TABLE students; –” as the input, it causes SQL injection
How prepared statement prevents this?
String query = “INSERT INTO students VALUES(‘” + “:name” + “‘)”
parameters.addValue(“name”, user);
=> when user input again “Robert’); DROP TABLE students; –“, the input string is precompiled on the driver as literal values and I guess it may be casted like:
CAST(‘Robert’); DROP TABLE students; –‘ AS varchar(30))
So at the end, the string will be literally inserted as the name to the table.
http://blog.linguiming.com/index.php/2018/01/10/why-prepared-statement-avoids-sql-injection/
PreparedStatement:
1)SQL语句的预编译和数据库端缓存导致整体执行速度更快,并且能够批量重用相同的SQL语句。
2) 通过内置的引号和其他特殊字符转义自动防止 SQL 注入攻击。请注意,这要求您使用任何PreparedStatement setXxx() 方法来设置该值。
PreparedStatement:
1) Precompilation and DB-side caching of the SQL statement leads to overall faster execution and the ability to reuse the same SQL statement in batches.
2) Automatic prevention of SQL injection attacks by builtin escaping of quotes and other special characters. Note that this requires that you use any of the PreparedStatement setXxx() methods to set the value.
PreparedStatement<如果您仍在连接字符串,那么单独 /code>
并不能帮助您。
例如,一名恶意攻击者仍然可以执行以下操作:
不仅是 SQL,甚至是 JPQL 或 HQL如果您不使用绑定参数,则可能会受到损害。
最重要的是,在构建 SQL 语句时绝对不应该使用字符串连接。为此目的使用专用 API,例如 JPA Criteria API。
PreparedStatement
alone does not help you if you are still concatenating Strings.For instance, one rogue attacker can still do the following:
Not only SQL, but even JPQL or HQL can be compromised if you are not using bind parameters.
Bottom line, you should never use string concatenation when building SQL statements. Use a dedicated API for that purpose, like JPA Criteria API.
在准备好的语句中,用户被迫输入数据作为参数。如果用户输入一些易受攻击的语句,例如 DROP TABLE 或 SELECT * FROM USERS,则数据不会受到影响,因为这些语句将被视为 SQL 语句的参数
In Prepared Statements the user is forced to enter data as parameters . If user enters some vulnerable statements like DROP TABLE or SELECT * FROM USERS then data won't be affected as these would be considered as parameters of the SQL statement