如何从“Bobby Tables”XKCD漫画工作SQL注入?
只是看着:
(来源: https : //xkcd.com/327/ )
这个SQL做什么:
Robert'); DROP TABLE STUDENTS; --
我知道'
和--
都是为了评论,但是DROP
这个词不会被评论,因为它是同一行的一部分?
它放下学生表。
学校程序中的原始代码可能看起来像这样
q = "INSERT INTO Students VALUES ('" + FNMName.Text + "', '" + LName.Text + "')";
这是将文本input添加到查询中的天真方式, 非常糟糕 ,正如您将看到的。
从名字,中间名文本框FNMName.Text (这是Robert'); DROP TABLE STUDENTS; --
Robert'); DROP TABLE STUDENTS; --
Robert'); DROP TABLE STUDENTS; --
)和姓氏文本框LName.Text (我们称之为Derper
)与查询的其余部分连接在一起,结果实际上是由语句终结符 (分号)分隔的两个查询 。 第二个查询已被注入到第一个查询中。 当代码对数据库执行此查询时,它将如下所示
INSERT INTO Students VALUES ('Robert'); DROP TABLE Students; --', 'Derper')
用简单的英文粗略地翻译为两个查询:
向名称值为“Robert”的学生表中添加一条新logging
和
删除学生表
过去第二个查询的所有内容都被标记为注释 : --', 'Derper')
'
在学生的名字是不是一个评论,这是closuresstring分隔符 。 由于学生的姓名是一个string,因此需要从语法上完成假设的查询。 注入攻击仅在SQL查询注入结果为有效的SQL时才起作用。
按照dan04的精明评论再次编辑
假设这个名字被用在一个variables$Name
。 然后你运行这个查询:
INSERT INTO Students VALUES ( '$Name' )
你得到的是:
INSERT INTO Students VALUES ( 'Robert' ); DROP TABLE STUDENTS; --' )
只有--
评论剩下的行。
正如其他人已经指出的那样, closures原来的声明,然后第二个声明如下。 大多数框架,包括像PHP这样的语言,现在都有默认的安全设置,不允许在一个SQLstring中使用多个语句。 例如,在PHP中,只能使用mysqli_multi_query
函数在一个SQLstring中运行多个语句。
但是,您可以通过SQL注入操作现有的SQL语句,而不必添加第二个语句。 假设你有一个login系统,用这个简单的select来检查用户名和密码:
$query="SELECT * FROM users WHERE username='" . $_REQUEST['user'] . "' and (password='".$_REQUEST['pass']."')"; $result=mysql_query($query);
如果您提供peter
作为密码的用户名和密码,则生成的SQLstring将如下所示:
SELECT * FROM users WHERE username='peter' and (password='secret')
一切安好。 现在想象你提供这个string作为密码:
' OR '1'='1
那么结果的SQLstring将是这样的:
SELECT * FROM users WHERE username='peter' and (password='' OR '1'='1')
这将使您能够在不知道密码的情况下login到任何帐户。 所以你不需要能够使用两个语句来使用SQL注入,但是如果你能够提供多个语句,你可以做更多的破坏性的事情。
不, '
不是SQL中的注释,而是分隔符。
妈妈假定数据库程序员提出了一个请求,如下所示:
INSERT INTO 'students' ('first_name', 'last_name') VALUES ('$firstName', '$lastName');
(例如)添加新学生,其中$xxx
variables内容直接从HTML表单中取出,而不检查格式或转义特殊字符。
所以如果$firstName
包含Robert'); DROP TABLE students; --
Robert'); DROP TABLE students; --
Robert'); DROP TABLE students; --
数据库程序将直接在DB上执行以下请求:
INSERT INTO 'students' ('first_name', 'last_name') VALUES ('Robert'); DROP TABLE students; --', 'XKCD');
即。 它会尽早终止插入语句,执行任何恶意代码,然后注释掉可能的代码。
嗯,我太慢了,我看到我的橙色乐队已经有8个答案了…… :-)看来是一个很受欢迎的话题。
TL; DR
- 应用程序接受input,在这种情况下,“南希”,而不尝试 - 消毒input,如通过转义特殊字符 学校=>插入学生VALUES('南希'); INSERT 0 1 - input到数据库命令被操纵时发生SQL注入 - 使数据库服务器执行任意的SQL 学校=>插入学生VALUES('罗伯特'); DROP TABLE学生; - '); INSERT 0 1 DROP TABLE - 学生logging现在消失了 - 情况可能会更糟! 学校=>select*从学生; 错误:关系“学生”不存在 第一行:select*从学生; ^
这将删除学生表。 为了弄清楚发生了什么,我们用一个只包含名字段的简单表来添加一行(用PostgreSQL9.1.2testing):
学校=> CREATE TABLE学生(名字为TEXT PRIMARY KEY); 注意:CREATE TABLE / PRIMARY KEY将为表“students”创build隐式索引“students_pkey” 创build表 学校=>插入学生VALUES('约翰'); INSERT 0 1
假设应用程序使用以下SQL将数据插入到表中:
插入学生VALUES('foobar');
将foobar
replace为学生的真实姓名。 正常的插入操作如下所示:
- input:南希 学校=>插入学生VALUES('南希'); INSERT 0 1
当我们查询表时,我们得到这个:
学校=>select*从学生; 名称 ------- 约翰 南希 (2行)
当我们将Little Bobby Tables的名字插入表中时会发生什么?
- input:Robert'); DROP TABLE学生; - 学校=>插入学生VALUES('罗伯特'); DROP TABLE学生; - '); INSERT 0 1 DROP TABLE
这里的SQL注入是学生终止语句的名字的结果,包括一个单独的DROP TABLE
命令; input末尾的两个破折号用于注释掉任何会导致错误的残余代码。 输出的最后一行确认数据库服务器已经丢弃了该表。
请注意,在INSERT
操作期间,应用程序不检查任何特殊字符的input,因此可以将任意inputinput到SQL命令中。 这意味着恶意用户可以在通常为用户input的字段中插入特殊符号(例如引号)以及任意的SQL代码,以使数据库系统执行它,从而SQL“注入” 。
结果?
学校=>select*从学生; 错误:关系“学生”不存在 第一行:select*从学生; ^
SQL注入是操作系统或应用程序中的远程任意代码执行漏洞的等效数据库。 成功的SQL注入攻击的潜在影响不可低估 – 根据数据库系统和应用程序的configuration,攻击者可以使用它来造成数据丢失(如在这种情况下),获得未经授权的数据访问,甚至执行主机本身的任意代码。
正如XKCD漫画所指出的那样,防止SQL注入攻击的一种方法是清理数据库input,例如通过转义特殊字符,以便它们不能修改底层的SQL命令,因此不会导致执行任意的SQL代码。 如果您使用参数化查询,例如在ADO.NET中使用SqlParameter
,则input将自动为您清理。
假设你天真地写了一个像这样的学生创作方法:
void createStudent(String name) { database.execute("INSERT INTO students (name) VALUES ('" + name + "')"); }
有人input名字Robert'); DROP TABLE STUDENTS; --
Robert'); DROP TABLE STUDENTS; --
这个查询在数据库上运行的是什么:
INSERT INTO students (name) VALUES ('Robert'); DROP TABLE STUDENTS --')
分号结束插入命令并启动另一个; – 评论剩下的行。 DROP TABLE命令被执行…
这就是为什么绑定参数是一件好事。
单引号是string的开始和结尾。 分号是语句的结尾。 所以,如果他们正在做这样的select:
Select * From Students Where (Name = '<NameGetsInsertedHere>')
SQL会变成:
Select * From Students Where (Name = 'Robert'); DROP TABLE STUDENTS; --') -- ^-------------------------------^
在某些系统上, select
会先运行,然后是drop
语句! 消息是:不要embedded你的SQL值。 而是使用参数!
');
结束查询,它不会开始评论。 然后,它放弃学生表并评论应该执行的查询的其余部分。
数据库的作者可能做了一个
sql = "SELECT * FROM STUDENTS WHERE (STUDENT_NAME = '" + student_name + "') AND other stuff"; execute(sql);
如果student_name是给定的名称,那么用名字“Robert”进行select,然后删除表格。 “ – ”部分将给定查询的其余部分更改为注释。
在这种情况下,“不是注释字符。 它用于分隔string文字。 这位漫画家正在把这个想法看作是一个dynamic的sql,
$sql = "INSERT INTO `Students` (FirstName, LastName) VALUES ('" . $fname . "', '" . $lname . "')";
所以现在'字符在程序员期待之前结束string字面值。 加之; 字符来结束语句,攻击者现在可以添加他们想要的任何SQL。 最后的 – 注释是确保原始语句中的任何剩余的sql不会阻止查询在服务器上编译。
FWIW,我也认为有问题的漫画有一个重要的细节错误:如果你正在考虑清理你的数据库input,正如漫画所暗示的那样,你仍然做错了。 相反,您应该考虑隔离数据库input,正确的方法是通过参数化查询。
SQL中'
字符用于string常量。 在这种情况下,它用于结束string常量而不是注释。
这是如何工作的:让我们假设pipe理员正在查找学生的logging
Robert'); DROP TABLE STUDENTS; --
由于pipe理员帐户具有较高的权限,因此从该帐户删除该表是可能的。
从请求检索用户名的代码是
现在查询会是这样的(search学生表)
String query="Select * from student where username='"+student_name+"'"; statement.executeQuery(query); //Rest of the code follows
结果查询变成
Select * from student where username='Robert'); DROP TABLE STUDENTS; --
由于用户input没有被消毒,所以上面的查询被分成两部分
Select * from student where username='Robert'); DROP TABLE STUDENTS; --
双破折号( – )只是注释掉查询的剩余部分。
这是危险的,因为它可以使密码validation无效(如果存在)
第一个会做正常的search。
如果帐户有足够的权限,第二个将放弃表学生(一般学校pipe理员帐户将运行这样的查询,并将具有上面谈到的特权)。