准备好的语句如何帮助我们防止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注入的本质结构化查询。
其他回答
当您创建准备好的语句并将其发送到DBMS时,它将存储为SQL查询以供执行。
稍后将数据绑定到查询,以便DBMS使用该数据作为执行(参数化)的查询参数。DBMS不会使用你绑定的数据作为已经编译的SQL查询的补充;只是数据而已。
这意味着使用准备好的语句执行SQL注入从根本上是不可能的。预处理语句的本质及其与DBMS的关系阻止了这一点。
我把答案通读了一遍,仍然觉得有必要强调阐明事先陈述的本质的关键点。考虑两种查询数据库的方法,其中涉及用户输入:
天真的方法
其中一个将用户输入与部分SQL字符串连接起来以生成SQL语句。在这种情况下,用户可以嵌入恶意SQL命令,然后将这些命令发送到数据库执行。
String SQLString = "SELECT * FROM CUSTOMERS WHERE NAME='"+userInput+"'"
例如,恶意用户输入可能导致SQLString等于"SELECT * FROM CUSTOMERS WHERE NAME='James';DROP TABLE CUSTOMERS;' "
由于恶意用户的存在,SQLString包含2条语句,其中第二条语句(“DROP TABLE CUSTOMERS”)将造成损害。
准备好的语句
在这种情况下,由于查询和数据分离,用户输入永远不会被视为SQL语句,因此永远不会被执行。正是由于这个原因,注入的任何恶意SQL代码都不会造成损害。所以“DROP TABLE CUSTOMERS”在上面的例子中永远不会被执行。
简而言之,通过用户输入引入的恶意代码将不会被执行!
关键短语是不需要正确转义。这意味着你不需要担心人们试图插入破折号、撇号、引号等等……
一切都为你安排好了。
下面是一个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=?
在SQL Server中,使用准备好的语句绝对是防注入的,因为输入参数不构成查询。这意味着执行的查询不是动态查询。 SQL注入易受攻击语句的示例。
string sqlquery = "select * from table where username='" + inputusername +"' and password='" + pass + "'";
现在,如果inoutusername变量的值是类似于a'或1=1——,这个查询现在变成:
select * from table where username='a' or 1=1 -- and password=asda
其余部分在——之后被注释,所以它永远不会被执行和绕过,就像使用下面的预处理语句示例一样。
Sqlcommand command = new sqlcommand("select * from table where username = @userinput and password=@pass");
command.Parameters.Add(new SqlParameter("@userinput", 100));
command.Parameters.Add(new SqlParameter("@pass", 100));
command.prepare();
所以实际上你不能发送另一个参数,从而避免SQL注入…