题 如何从“Bobby Tables”XKCD漫画中注入SQL?


只看:

XKCD Strip (资源: https://xkcd.com/327/

这个SQL做了什么:

Robert'); DROP TABLE STUDENTS; --

我都知道 ' 和 -- 是征求意见,但不是这个词 DROP 得到评论,因为它是同一行的一部分?


985
2017-12-01 21:50


起源


如果你听 Stack Overflow播客#31 (2008年11月27日),他们实际上讨论了这一点。 - EBGreen
在MySQL中, ' 不适合 注释。即使它是,它之前没有空间所以它只能结束它之前的字符串。 - Lightness Races in Orbit
就XKCD而言,如果您对某些漫画有任何疑问,可以随时前往 解释XKCD 并找到你的答案。甚至有一个 XKCD维基,这对一些棘手的漫画很有帮助 XKCD geohashing - Anatoli
+1这是我们在StackOverflow上偶尔需要的有趣问题。 :) - CodingInCircles
我相信这个链接必须记录在这里: bobby-tables.com - Arioch 'The


答案:


它放下了学生桌。

学校课程中的原始代码可能看起来像

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

这是将文本输入添加到查询中的天真方式,而且是 很坏,你会看到。

从名字,中间名称文本框的值后 FNMName.Text (是的 Robert'); DROP TABLE STUDENTS; --)和姓氏文本框 LName.Text (我们称之为 Derper)与查询的其余部分连接,结果实际上是 两个问题 被分开了 声明终结者 (分号)。第二个问题是 注射 进入第一个。当代码对数据库执行此查询时,它将如下所示

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

用简单的英语粗略地翻译成两个查询:

使用名称值“Robert”向Students表添加新记录

删除学生表

超过第二个查询的所有内容都是 标记为评论--', 'Derper')

' 在学生的名字不是评论,它是结束 字符串分隔符。由于学生的名字是一个字符串,因此在语法上需要完成假设的查询。注入攻击只能起作用 当他们注入的SQL查询导致有效的SQL时

编辑 再次 按照 dan04精明的评论


1020
2017-12-01 21:55



嗯,参数周围带括号的WHERE相当不寻常,但至少它避免了语法错误...... :-) - PhiLho
@PhiLho:如果原始陈述是 INSERT那么括号会更有意义。它还解释了为什么数据库连接不是只读模式。 - dan04
正如@ dan04所解释的那样,括号更有意义 INSERT。向后思考, SELECT 因为表中的Little Bobby Tables的插入已经放弃了表格,所以无论如何都不会运行。 - ypercubeᵀᴹ
实际上,在这个例子中,第一个查询(“添加新记录......”)将失败,因为 Students 期望不仅仅是一列(原始/正确的声明提供了两列)。也就是说,第二列的存在有助于说明为什么需要评论;因为一个人不能改变Bobby的名字,所以最好只留下这个观察作为脚注。 - eggyal
鲍比的姓氏 - 或者至少是他母亲的姓氏 罗伯茨,每 解释XKCD。不过,我不确定纠正这会改善答案的清晰度。 - WBT


假设该名称用于变量, $Name。然后运行此查询:

INSERT INTO Students VALUES ( '$Name' )

代码错误地将用户提供的任何内容作为变量放置。您希望SQL为:

插入学生价值(''罗伯特表`)

但聪明的用户可以提供他们想要的任何东西:

插入学生价值(''罗伯特'); DROP TABLE学生; - ')

你得到的是:

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

-- 只评论该行的其余部分。


547
2017-09-14 10:12



这比投票最高的要好得多,因为它解释了右括号。 - Tim Büthe
究竟。这是我见过的SQL注入最简洁的解释。 - recluze


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

但是,您可以通过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注入,但如果您能够提供多个语句,则可以执行更具破坏性的操作。


145
2017-12-01 22:01





没有, ' 不是SQL中的注释,而是分隔符。

妈妈认为数据库程序员提出的请求如下:

INSERT INTO 'students' ('first_name', 'last_name') VALUES ('$firstName', '$lastName');

(例如)添加新学生,其中 $xxx 变量内容直接取自HTML表单,不检查格式也不转义特殊字符。

因此,如果 $firstName 包含 Robert'); DROP TABLE students; -- 数据库程序将直接在DB上执行以下请求:

INSERT INTO 'students' ('first_name', 'last_name') VALUES ('Robert'); DROP TABLE students; --', 'XKCD');

即。它会在插入语句的早期终止,执行破解者想要的任何恶意代码,然后注释掉可能存在的任何剩余代码。

嗯,我太慢了,我在橙色乐队之前已经看到了8个答案...... :-)似乎是一个热门话题。


68
2017-09-26 20:20





TL; DR

 - 应用程序接受输入,在本例中为“Nancy”,而不尝试
 - 清理输入,例如通过转义特殊字符
学校=>插入学生价值观('南希');
插入0 1

 - 操作数据库命令的输入时发生SQL注入
 - 使数据库服务器执行任意SQL
学校=>插入学生价值观('罗伯特'); DROP TABLE学生; - ');
插入0 1
DROP TABLE

 - 学生记录现已消失 - 情况可能更糟!
学校=> SELECT * FROM学生;
错误:关系“学生”不存在
第1行:SELECT * FROM学生;
                      ^

这会删除(删除)学生表。

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

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

school => CREATE TABLE学生(姓名TEXT PRIMARY KEY);
注意:CREATE TABLE / PRIMARY KEY将为表“学生”创建隐式索引“students_pkey”
创建表
学校=>插入学生的价值观('约翰');
插入0 1

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

插入学生VALUES('foobar');

更换 foobar 与学生的实际名称。正常的插入操作如下所示:

 - 输入:南希
学校=>插入学生价值观('南希');
插入0 1

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

学校=> SELECT * FROM学生;
 名称
-------
 约翰
 南希
(2排)

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

 - 输入:Robert'); DROP TABLE学生; - 
学校=>插入学生价值观('罗伯特'); DROP TABLE学生; - ');
插入0 1
DROP TABLE

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

重要的是要注意到 INSERT 操作应用程序不检查任何特殊字符的输入,因此允许将任意输入输入SQL命令。这意味着恶意用户可以将通常用于用户输入的字段插入特殊符号(如引号和任意SQL代码)以使数据库系统执行它,因此 SQL注射

结果?

学校=> SELECT * FROM学生;
错误:关系“学生”不存在
第1行:SELECT * FROM学生;
                      ^

SQL注入是远程数据库的等价物 任意代码执行 操作系统或应用程序中的漏洞。成功的SQL注入攻击的潜在影响不容小觑 - 取决于数据库系统和应用程序配置,攻击者可以使用它来导致数据丢失(如本例所示),获取未经授权的数据访问,甚至执行主机本身的任意代码。

正如XKCD漫画所指出的,防止SQL注入攻击的一种方法是清理数据库输入,例如通过转义特殊字符,以便它们不能修改底层SQL命令,因此不会导致执行任意SQL代码。如果使用参数化查询,例如使用 SqlParameter 在ADO.NET中,输入至少会自动清理以防止SQL注入。

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


34
2017-12-01 21:56





说你天真地写了这样的学生创作方法:

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

有人输入名字 Robert'); DROP TABLE STUDENTS; --

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

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

分号结束插入命令并启动另一个命令; - 评论其余部分。执行DROP TABLE命令...

这就是绑定参数是一件好事的原因。


27
2017-12-01 22:03





单引号是字符串的开头和结尾。分号是声明的结尾。所以,如果他们正在做这样的选择:

Select *
From Students
Where (Name = '<NameGetsInsertedHere>')

SQL将成为:

Select *
From Students
Where (Name = 'Robert'); DROP TABLE STUDENTS; --')
--             ^-------------------------------^

在某些系统上, select 会先跑,然后跑 drop 声明!消息是:不要将值嵌入到您的SQL中。而是使用参数!


24
2017-12-01 21:53