看看:

(来源:https://xkcd.com/327/)

这个SQL做什么:

Robert'); DROP TABLE STUDENTS; --

我知道'和——都是用来评论的,但是DROP这个词不是也会被评论吗?因为它是同一行的一部分。


当前回答

假设你天真地写了一个学生创建方法,像这样:

void createStudent(String name) {
    database.execute("INSERT INTO students (name) VALUES ('" + name + "')");
}

有人输入名字罗伯特’);降表学生;--

在数据库上运行的是这个查询:

INSERT INTO students (name) VALUES ('Robert'); DROP TABLE STUDENTS --')

分号结束插入命令并开始另一个;——注释掉了该行的其余部分。DROP TABLE命令被执行…

这就是为什么绑定参数是一个好东西。

其他回答

数据库的作者可能做了一个

sql = "SELECT * FROM STUDENTS WHERE (STUDENT_NAME = '" + student_name + "') AND other stuff";
execute(sql);

如果给出的是student_name,则使用名称“Robert”进行选择,然后删除表。“——”部分将给定查询的其余部分更改为注释。

它删除了学生表。

学校程序的原始代码可能看起来像这样

q = "INSERT INTO Students VALUES ('" + FNMName.Text + "', '" + LName.Text + "')";

这是向查询中添加文本输入的最简单的方法,非常糟糕,正如您将看到的那样。

值从第一个名字,中间名文本框FNMName。文本(是罗伯特的);降表学生;——)和姓氏文本框LName。文本(让我们称之为Derper)连接到查询的其余部分,结果实际上是两个查询由语句结束符(分号)分开。第二个查询已注入到第一个查询中。当代码对数据库执行此查询时,它将如下所示

INSERT INTO Students VALUES ('Robert'); DROP TABLE Students; --', 'Derper')

简单地说,就是这两个问题:

在student表中添加一条Name值为'Robert'的新记录

and

删除Students表

第二个查询之后的所有内容都被标记为注释:——','Derper')

学生名字中的'不是注释,而是结束字符串分隔符。因为学生的名字是一个字符串,所以在语法上需要它来完成假设的查询。只有当注入的SQL查询结果为有效SQL时,注入攻击才有效。

根据dan04的评论再次编辑

');结束查询,不开始注释。然后它删除students表,并注释本该执行的其余查询。

博士TL;

-- The application accepts input, in this case 'Nancy', without attempting to
-- sanitize the input, such as by escaping special characters
school=> INSERT INTO students VALUES ('Nancy');
INSERT 0 1

-- SQL injection occurs when input into a database command is manipulated to
-- cause the database server to execute arbitrary SQL
school=> INSERT INTO students VALUES ('Robert'); DROP TABLE students; --');
INSERT 0 1
DROP TABLE

-- The student records are now gone - it could have been even worse!
school=> SELECT * FROM students;
ERROR:  relation "students" does not exist
LINE 1: SELECT * FROM students;
                      ^

这将删除student表。

(这个回答中的所有代码示例都是在PostgreSQL 9.1.2数据库服务器上运行的。)

为了弄清楚发生了什么,让我们尝试一个只包含name字段的简单表,并添加一行:

school=> CREATE TABLE students (name TEXT PRIMARY KEY);
NOTICE:  CREATE TABLE / PRIMARY KEY will create implicit index "students_pkey" for table "students"
CREATE TABLE
school=> INSERT INTO students VALUES ('John');
INSERT 0 1

让我们假设应用程序使用以下SQL将数据插入到表中:

INSERT INTO students VALUES ('foobar');

将foobar替换为学生的实际姓名。一个正常的插入操作是这样的:

--                            Input:   Nancy
school=> INSERT INTO students VALUES ('Nancy');
INSERT 0 1

当我们查询这个表时,我们得到:

school=> SELECT * FROM students;
 name
-------
 John
 Nancy
(2 rows)

当我们将Little Bobby Tables的名字插入到表中时会发生什么?

--                            Input:   Robert'); DROP TABLE students; --
school=> INSERT INTO students VALUES ('Robert'); DROP TABLE students; --');
INSERT 0 1
DROP TABLE

这里的SQL注入是学生的名字终止语句并包含一个单独的DROP TABLE命令的结果;输入末尾的两个破折号用于注释掉任何可能导致错误的剩余代码。输出的最后一行确认数据库服务器已经删除了该表。

重要的是要注意,在INSERT操作期间,应用程序不会检查输入是否有任何特殊字符,因此允许在SQL命令中输入任意输入。这意味着恶意用户可以在通常供用户输入的字段中插入特殊符号(如引号)以及任意SQL代码,从而导致数据库系统执行该符号,即SQL注入。

结果呢?

school=> SELECT * FROM students;
ERROR:  relation "students" does not exist
LINE 1: SELECT * FROM students;
                      ^

SQL注入在数据库中相当于操作系统或应用程序中的远程任意代码执行漏洞。一个成功的SQL注入攻击的潜在影响不可低估——取决于数据库系统和应用程序配置,攻击者可以使用它来导致数据丢失(如本例中所示),获得对数据的未经授权访问,甚至在主机上执行任意代码。

正如XKCD漫画所指出的,防止SQL注入攻击的一种方法是净化数据库输入,例如通过转义特殊字符,这样它们就不能修改底层SQL命令,因此也就不会导致任意SQL代码的执行。这可以在应用程序级别上完成,一些参数化查询的实现通过清除输入来操作。

但是,在应用程序级别上清除输入可能不会阻止更高级的SQL注入技术。例如,有一些方法可以绕过mysql_real_escape_string PHP函数。为了增加保护,许多数据库系统支持预处理语句。如果在后端正确地实现,准备语句可以通过将数据输入从语义上与命令的其余部分分开来使SQL注入成为不可能。

正如其他人已经指出的那样,the ');关闭原来的语句,然后第二个语句紧随其后。到目前为止,大多数框架(包括PHP等语言)都有默认的安全设置,不允许在一个SQL字符串中包含多条语句。例如,在PHP中,您只能使用mysqli_multi_query函数在一个SQL字符串中运行多条语句。

但是,您可以通过SQL注入操作现有的SQL语句,而无需添加第二条语句。假设你有一个登录系统,它检查用户名和密码,简单的选择:

$query="SELECT * FROM users WHERE username='" . $_REQUEST['user'] . "' and (password='".$_REQUEST['pass']."')";
$result=mysql_query($query);

如果你提供peter作为用户名,secret作为密码,得到的SQL字符串将如下所示:

SELECT * FROM users WHERE username='peter' and (password='secret')

一切都很好。现在,假设您提供以下字符串作为密码:

' OR '1'='1

然后得到的SQL字符串将是这样的:

SELECT * FROM users WHERE username='peter' and (password='' OR '1'='1')

这将使您可以在不知道密码的情况下登录任何帐户。因此,为了使用SQL注入,您不需要能够使用两条语句,尽管如果您能够提供多条语句,您可能会做更多破坏性的事情。