准备好的语句如何帮助我们防止SQL注入攻击?
维基百科说:
预处理语句对SQL注入具有弹性,因为 参数值,稍后使用不同的 协议,不需要正确转义。如果原始语句 模板不能从外部输入派生,SQL注入不能 发生。
我不太明白其中的原因。如何用简单的英语和一些例子来简单地解释?
准备好的语句如何帮助我们防止SQL注入攻击?
维基百科说:
预处理语句对SQL注入具有弹性,因为 参数值,稍后使用不同的 协议,不需要正确转义。如果原始语句 模板不能从外部输入派生,SQL注入不能 发生。
我不太明白其中的原因。如何用简单的英语和一些例子来简单地解释?
当前回答
基本上,通过准备好的语句,来自潜在黑客的数据被视为数据——它不可能与应用程序SQL混合和/或被解释为SQL(当传入的数据直接放入应用程序SQL时可能发生)。
这是因为预处理语句首先“准备”SQL查询,以找到一个有效的查询计划,然后发送可能来自表单的实际值——在查询实际执行的时候。
更多信息请点击这里:
准备好的语句和SQL注入
其他回答
其思想非常简单——查询和数据分别发送到数据库服务器。 这是所有。
SQL注入问题的根源在于代码和数据的混合。
事实上,我们的SQL查询是一个合法的程序。 我们正在动态地创建这样一个程序,动态地添加一些数据。因此,数据可能会干扰程序代码,甚至改变它,正如每个SQL注入示例所显示的那样(所有PHP/Mysql中的示例):
$expected_data = 1;
$query = "SELECT * FROM users where id=$expected_data";
会产生一个常规查询吗
SELECT * FROM users where id=1
而这段代码
$spoiled_data = "1; DROP TABLE users;"
$query = "SELECT * FROM users where id=$spoiled_data";
会产生恶意序列吗
SELECT * FROM users where id=1; DROP TABLE users;
它可以工作,因为我们直接将数据添加到程序主体中,它成为程序的一部分,因此数据可能会改变程序,并且根据传递的数据,我们将有一个常规输出或用户删除的表。
而对于准备好的语句,我们不改变我们的程序,它保持不变 这就是重点。
我们首先向服务器发送一个程序
$db->prepare("SELECT * FROM users where id=?");
其中数据被一些称为参数或占位符的变量所取代。
注意,完全相同的查询被发送到服务器,其中没有任何数据!然后我们将数据与第二个请求一起发送,本质上与查询本身分离:
$db->execute($data);
所以它不会改变我们的程序,造成任何伤害。 很简单,不是吗?
我唯一需要补充的是,每个手册中都省略了:
预准备语句只能保护数据文字,但不能与任何其他查询部分一起使用。 因此,一旦我们必须添加一个动态标识符——例如,一个字段名——预处理语句就无法帮助我们。这件事我最近已经解释过了,我就不再重复了。
关键短语是不需要正确转义。这意味着你不需要担心人们试图插入破折号、撇号、引号等等……
一切都为你安排好了。
当您创建准备好的语句并将其发送到DBMS时,它将存储为SQL查询以供执行。
稍后将数据绑定到查询,以便DBMS使用该数据作为执行(参数化)的查询参数。DBMS不会使用你绑定的数据作为已经编译的SQL查询的补充;只是数据而已。
这意味着使用准备好的语句执行SQL注入从根本上是不可能的。预处理语句的本质及其与DBMS的关系阻止了这一点。
ResultSet rs = statement.executeQuery("select * from foo where value = " + httpRequest.getParameter("filter");
让我们假设在Servlet中有这个。如果一个恶意的人传递了一个坏的值为'过滤器',你可能会黑你的数据库。
下面是一个SQL语句来建立一个例子:
CREATE TABLE employee(name varchar, paymentType varchar, amount bigint);
INSERT INTO employee VALUES('Aaron', 'salary', 100);
INSERT INTO employee VALUES('Aaron', 'bonus', 50);
INSERT INTO employee VALUES('Bob', 'salary', 50);
INSERT INTO employee VALUES('Bob', 'bonus', 0);
Inject类容易受到SQL注入的攻击。查询与用户输入动态地粘贴在一起。查询的目的是显示关于Bob的信息。工资或奖金,根据用户输入。但是恶意用户操纵输入,在where子句后面附加一个“or true”,从而破坏了查询,从而返回了所有内容,包括本应隐藏的Aaron的信息。
import java.sql.*;
public class Inject {
public static void main(String[] args) throws SQLException {
String url = "jdbc:postgresql://localhost/postgres?user=user&password=pwd";
Connection conn = DriverManager.getConnection(url);
Statement stmt = conn.createStatement();
String sql = "SELECT paymentType, amount FROM employee WHERE name = 'bob' AND paymentType='" + args[0] + "'";
System.out.println(sql);
ResultSet rs = stmt.executeQuery(sql);
while (rs.next()) {
System.out.println(rs.getString("paymentType") + " " + rs.getLong("amount"));
}
}
}
运行此命令,第一种情况是正常使用,第二种情况是恶意注入:
c:\temp>java Inject salary
SELECT paymentType, amount FROM employee WHERE name = 'bob' AND paymentType='salary'
salary 50
c:\temp>java Inject "salary' OR 'a'!='b"
SELECT paymentType, amount FROM employee WHERE name = 'bob' AND paymentType='salary' OR 'a'!='b'
salary 100
bonus 50
salary 50
bonus 0
您不应该使用用户输入的字符串连接来构建SQL语句。它不仅容易受到注入的攻击,而且还会对服务器产生缓存影响(语句更改,因此SQL语句缓存命中的可能性较小,而绑定示例总是运行相同的语句)。
下面是一个避免这种注入的Binding的例子:
import java.sql.*;
public class Bind {
public static void main(String[] args) throws SQLException {
String url = "jdbc:postgresql://localhost/postgres?user=postgres&password=postgres";
Connection conn = DriverManager.getConnection(url);
String sql = "SELECT paymentType, amount FROM employee WHERE name = 'bob' AND paymentType=?";
System.out.println(sql);
PreparedStatement stmt = conn.prepareStatement(sql);
stmt.setString(1, args[0]);
ResultSet rs = stmt.executeQuery();
while (rs.next()) {
System.out.println(rs.getString("paymentType") + " " + rs.getLong("amount"));
}
}
}
使用与前面示例相同的输入运行此代码,显示恶意代码不起作用,因为没有匹配该字符串的paymentType:
c:\temp>java Bind salary
SELECT paymentType, amount FROM employee WHERE name = 'bob' AND paymentType=?
salary 50
c:\temp>java Bind "salary' OR 'a'!='b"
SELECT paymentType, amount FROM employee WHERE name = 'bob' AND paymentType=?