准备好的语句如何防止SQL注入攻击?

准备好的语句如何防止SQL注入攻击?,sql,security,sql-injection,prepared-statement,Sql,Security,Sql Injection,Prepared Statement,如何帮助我们防止攻击 维基百科说: 准备好的语句能够抵抗SQL注入,因为 参数值,稍后使用不同的 协议,不需要正确转义。如果原始声明 模板不是从外部输入派生的,SQL注入无法 发生 我看不太清楚原因。什么是简单的英语解释和一些例子?关键短语是不需要正确转义。这意味着你不必担心有人试图用破折号、撇号、引号等来表示你的想法 一切都由你来处理 ResultSet rs = statement.executeQuery("select * from foo where value = " + httpR

如何帮助我们防止攻击

维基百科说:

准备好的语句能够抵抗SQL注入,因为 参数值,稍后使用不同的 协议,不需要正确转义。如果原始声明 模板不是从外部输入派生的,SQL注入无法 发生


我看不太清楚原因。什么是简单的英语解释和一些例子?

关键短语是
不需要正确转义。这意味着你不必担心有人试图用破折号、撇号、引号等来表示你的想法

一切都由你来处理

ResultSet rs = statement.executeQuery("select * from foo where value = " + httpRequest.getParameter("filter");

让我们假设您在一个Servlet中拥有它,您是对的。如果一个恶毒的人为“filter”传递了一个错误的值,您可能会入侵您的数据库。

以下是设置示例的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
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"));
        }
    }
}
您不应该使用用户输入的字符串连接来构建SQL语句。它不仅易受注入攻击,而且在服务器上也有缓存影响(语句发生变化,因此不太可能命中SQL语句缓存,而绑定示例始终运行相同的语句)

下面是一个用于避免此类注入的绑定示例:

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
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注入问题的根源在于代码和数据的混合。

事实上,我们的SQL查询是一个合法的程序。 我们正在动态地创建这样一个程序,动态地添加一些数据。因此,数据可能会干扰程序代码,甚至改变程序代码,正如每个SQL注入示例所示(所有示例都在PHP/Mysql中):

将生成一个常规查询

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;
它之所以有效,是因为我们将数据直接添加到程序体中,它将成为程序的一部分,因此数据可能会改变程序,并且根据传递的数据,我们将有一个常规输出或删除一个表
users

虽然对于准备好的语句,我们不会更改我们的程序,但它保持不变
这就是重点

我们首先向服务器发送一个程序

$db->prepare("SELECT * FROM users where id=?");
其中数据被称为参数或占位符的变量替换

请注意,将完全相同的查询发送到服务器,其中没有任何数据!然后我们在第二个请求中发送数据,基本上与查询本身分离:

$db->execute($data);
因此,它不会改变我们的程序并造成任何伤害。
很简单,不是吗

我唯一需要补充的是,在每本手册中都会遗漏:

准备好的语句只能保护数据文本,但不能与任何其他查询部分一起使用。
所以,一旦我们必须添加,比如说,一个动态的标识符——比如一个字段名——准备好的语句就帮不了我们了。我已经说过了,所以我不再重复了

在中,使用准备好的语句绝对是注入证明,因为输入参数不构成查询。这意味着执行的查询不是动态查询。 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注入…

基本上,对于准备好的语句,来自潜在黑客的数据被视为数据,并且无法与应用程序SQL混合和/或解释为SQL(当传入的数据直接放入应用程序SQL时,可能会发生这种情况)

这是因为prepared语句首先“准备”SQL查询以找到一个有效的查询计划,然后发送可能来自表单的实际值——此时查询实际执行

更多精彩信息请点击此处:


当您创建准备好的语句并将其发送到DBMS时,它将存储为SQL查询以供执行

String SQLString = "SELECT * FROM CUSTOMERS WHERE NAME='"+userInput+"'"
稍后将数据绑定到查询,以便DBMS使用该数据作为执行(参数化)的查询参数。DBMS不使用绑定的数据作为已编译SQL查询的补充;它只是数据

这意味着根本不可能使用预处理语句执行SQL注入。预处理语句的本质及其与DBMS的关系阻止了这一点。

根本原因#1-分隔符问题

Sql注入是可能的,因为我们使用引号来分隔字符串,并且也是字符串的一部分,因此有时无法解释它们。如果我们有无法在字符串数据中使用的分隔符,Sql注入就永远不会发生。解决分隔符问题可消除Sql注入问题。结构查询那样做

根本原因#2-人性,人是狡猾的,有些狡猾的人是恶意的