准备好的语句如何帮助我们防止SQL注入攻击?
维基百科说:
预处理语句对SQL注入具有弹性,因为 参数值,稍后使用不同的 协议,不需要正确转义。如果原始语句 模板不能从外部输入派生,SQL注入不能 发生。
我不太明白其中的原因。如何用简单的英语和一些例子来简单地解释?
准备好的语句如何帮助我们防止SQL注入攻击?
维基百科说:
预处理语句对SQL注入具有弹性,因为 参数值,稍后使用不同的 协议,不需要正确转义。如果原始语句 模板不能从外部输入派生,SQL注入不能 发生。
我不太明白其中的原因。如何用简单的英语和一些例子来简单地解释?
当前回答
根本原因#1 -分隔符问题
Sql注入是可能的,因为我们使用引号来分隔字符串,也作为字符串的一部分,使得有时无法解释它们。如果我们有不能在字符串数据中使用的分隔符,sql注入就永远不会发生。解决分隔符问题可以消除sql注入问题。结构查询可以做到这一点。
根本原因#2 -人性,人们是狡猾的,一些狡猾的人是恶意的,所有的人都会犯错误
sql注入的另一个根本原因是人性。人,包括程序员,都会犯错误。当您在结构化查询中犯错误时,它不会使您的系统容易受到sql注入的攻击。如果不使用结构化查询,错误可能会产生sql注入漏洞。
结构化查询如何解决SQL注入的根本原因
结构化查询解决了分隔符问题,方法是将sql命令放在一个语句中,将数据放在一个单独的编程语句中。编程语句创建所需的分离。
Structured queries help prevent human error from creating critical security holes. With regard to humans making mistakes, sql injection cannot happen when structure queries are used. There are ways of preventing sql injection that don't involve structured queries, but normal human error in that approaches usually leads to at least some exposure to sql injection. Structured Queries are fail safe from sql injection. You can make all the mistakes in the world, almost, with structured queries, same as any other programming, but none that you can make can be turned into a ssstem taken over by sql injection. That is why people like to say this is the right way to prevent sql injection.
所以,你已经了解了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);
所以它不会改变我们的程序,造成任何伤害。 很简单,不是吗?
我唯一需要补充的是,每个手册中都省略了:
预准备语句只能保护数据文字,但不能与任何其他查询部分一起使用。 因此,一旦我们必须添加一个动态标识符——例如,一个字段名——预处理语句就无法帮助我们。这件事我最近已经解释过了,我就不再重复了。
根本原因#1 -分隔符问题
Sql注入是可能的,因为我们使用引号来分隔字符串,也作为字符串的一部分,使得有时无法解释它们。如果我们有不能在字符串数据中使用的分隔符,sql注入就永远不会发生。解决分隔符问题可以消除sql注入问题。结构查询可以做到这一点。
根本原因#2 -人性,人们是狡猾的,一些狡猾的人是恶意的,所有的人都会犯错误
sql注入的另一个根本原因是人性。人,包括程序员,都会犯错误。当您在结构化查询中犯错误时,它不会使您的系统容易受到sql注入的攻击。如果不使用结构化查询,错误可能会产生sql注入漏洞。
结构化查询如何解决SQL注入的根本原因
结构化查询解决了分隔符问题,方法是将sql命令放在一个语句中,将数据放在一个单独的编程语句中。编程语句创建所需的分离。
Structured queries help prevent human error from creating critical security holes. With regard to humans making mistakes, sql injection cannot happen when structure queries are used. There are ways of preventing sql injection that don't involve structured queries, but normal human error in that approaches usually leads to at least some exposure to sql injection. Structured Queries are fail safe from sql injection. You can make all the mistakes in the world, almost, with structured queries, same as any other programming, but none that you can make can be turned into a ssstem taken over by sql injection. That is why people like to say this is the right way to prevent sql injection.
所以,你已经了解了sql注入的原因,以及在使用sql注入时不可能使用sql注入的本质结构化查询。
下面是一个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=?
简单的例子:
"select * from myTable where name = " + condition;
如果用户输入是:
'123'; delete from myTable; commit;
查询将像这样执行:
select * from myTable where name = '123'; delete from myTable; commit;
ResultSet rs = statement.executeQuery("select * from foo where value = " + httpRequest.getParameter("filter");
让我们假设在Servlet中有这个。如果一个恶意的人传递了一个坏的值为'过滤器',你可能会黑你的数据库。