首页 文章

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

提问于
浏览
127

prepared statements如何帮助我们防止SQL injection攻击?

维基百科说:

准备语句对SQL注入具有弹性,因为以后使用不同协议传输的参数值无需正确转义 . 如果原始语句模板不是从外部输入派生的,则不能进行SQL注入 .

我看不清楚原因 . 简单的英语和一些例子中有什么简单的解释?

9 回答

  • 13

    这个想法很简单 - 查询和数据分别发送到数据库服务器 .
    就这样 .

    SQL注入问题的根源是 mixing of the code and the data.

    实际上,我们的SQL查询是 a legitimate program . 我们通过动态添加一些数据来动态创建这样的程序 . 因此,这些数据可能会干扰程序代码甚至改变它,因为每个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;
    

    它的工作原理是因为我们将数据直接添加到程序体中并且它成为程序的一部分,因此数据可能会改变程序,并且根据传递的数据,我们将删除常规输出或表 users .

    in case of prepared statements we don't alter our program, it remains intact
    这才是重点 .

    我们首先将程序发送到服务器

    $db->prepare("SELECT * FROM users where id=?");
    

    其中数据被称为参数或占位符的变量替换 .

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

    $db->execute($data);
    

    所以,它不能改变我们的计划并造成任何伤害 .
    很简单 - 不是吗?

    但是,值得注意的是 not every time 您正在使用占位符 it is processed as a prepared statement .

    占位符是将实际数据替换为变量以供将来处理的一般想法(例如,参见 printf() ),而预准备语句是它的唯一子集 .

    有些情况(特别是PHP中的PDO可以这样做)可以模拟准备好的语句,并且查询实际上与数据一起组成并在一个请求中发送到服务器 . 但重要的是要明白这种方法是 equally safe ,因为 every bit of data is properly formatted according to it's type 因此没有任何错误可能发生 .

    我必须添加的唯一内容总是在每个手册中省略:

    准备好的语句只能保护数据,但是 can't defend the program itself .
    所以,一旦我们必须添加一个动态的 identifier - 一个字段名称,例如,准备好的语句可以't help us. I' ve explained the matter recently,所以我不会重复自己 .

  • 0

    这是用于设置示例的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子句,以便返回所有内容,包括有关应该隐藏的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语句缓存命中,而绑定示例始终运行相同的语句) .

    这是绑定的一个例子,以避免这种注入:

    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=?
    
  • 236

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

    这是因为预准备语句首先“准备”SQL查询以找到有效的查询计划,并发送可能在以后从表单中获取的实际值 - 此时查询实际执行 .

    更多信息在这里:

    Prepared statements and SQL Injection

  • 3

    SQL Server中,使用预准备语句肯定是注入式的,因为输入参数不要形成查询 . 这意味着执行的查询不是动态查询 . SQL注入易受攻击语句的示例 .

    string sqlquery = "select * from table where username='" + inputusername +"' and password='" + pass + "'";
    

    现在,如果inoutusername变量中的值类似于'或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注入...

  • 3

    关键短语是 need not be correctly escaped . 这意味着你不要担心人们试图抛出破折号,撇号,引号等等......

    这一切都是为您处理的 .

  • 20

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

    稍后将数据绑定到查询,以便DBMS将该数据用作执行的查询参数(参数化) . DBMS不使用您绑定的数据作为已编译的SQL查询的补充;它只是数据 .

    这意味着使用预准备语句执行SQL注入根本不可能 . 准备好的语句的本质及其与DBMS的关系可以防止这种情况 .

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

    让我们假设您在Servlet中拥有它 . 如果一个恶意的人为“过滤器”传递了错误的值,那么你可能会破解你的数据库 .

  • 2

    我仔细阅读了答案,仍然觉得有必要强调说明准备陈述本质的关键点 . 考虑两种方法来查询涉及用户输入的数据库:

    Naive Approach

    一个用户输入与一些部分SQL字符串连接以生成SQL语句 . 在这种情况下,用户可以嵌入恶意SQL命令,然后将其发送到数据库以供执行 .

    String SQLString = "SELECT * FROM CUSTOMERS WHERE NAME='"+userInput+"'"
    

    例如,恶意用户输入可导致 SQLString 等于 "SELECT * FROM CUSTOMERS WHERE NAME='James';DROP TABLE CUSTOMERS;'

    由于恶意用户, SQLString 包含2个语句,其中第2个语句( "DROP TABLE CUSTOMERS" )将造成伤害 .

    Prepared Statements

    在这种情况下,由于查询和数据的分离,用户输入永远不会被视为SQL语句 and thus is never executed . 正是出于这个原因,注入的任何恶意SQL代码都不会造成任何伤害 . 因此 "DROP TABLE CUSTOMERS" 永远不会在上面的情况下执行 .

    In a nutshell, with prepared statements malicious code introduced via user input will not be executed!

  • 1

    Root Cause #1 - The Delimiter Problem

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

    Root Cause #2 - Human Nature, People are Crafty and Some Crafty People Are Malicious And All People Make Mistakes

    sql注入的另一个根本原因是人性 . 人们,包括程序员,都会犯错误 . 当您在结构化查询中出错时,它不会使您的系统容易受到SQL注入攻击 . 如果您不使用结构化查询,则错误可能会生成SQL注入漏洞 .

    How Structured Queries Resolve the Root Causes of SQL Injection

    结构化查询通过将sql命令放在一个语句中并将数据放在单独的编程语句中来解决分隔符问题 . 编程语句创建了所需的分离 .

    Structured queries help prevent human error from creating critical security holes. 关于人类犯错误,使用结构查询时不能进行SQL注入 . 有一些方法可以防止sql注入不涉及结构化查询,但是这种方法中的正常人为错误通常会导致至少一些sql注入 . 结构化查询从sql注入失败安全 . 你可以使用结构化查询,与任何其他编程一样,在世界上犯下所有错误,但是你可以做的任何事都可以变成sql注入接管的ssstem . 这就是为什么人们喜欢说这是防止sql注入的正确方法 .

    所以,你有它,sql注入的原因和自然结构化的查询,使它们在使用时不可能 .

相关问题