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

时间:2008-12-01 21:50:10

标签: security validation sql-injection

只看:

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

这个SQL做了什么:

Robert'); DROP TABLE STUDENTS; --

我知道'--都是评论,但是DROP这个词也没有被评论,因为它是同一行的一部分吗?

13 个答案:

答案 0 :(得分:1068)

它会丢弃学生表。

学校计划中的原始代码可能类似于

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

这是将文本输入添加到查询中的天真方式,并且非常糟糕,如您所见。

来自名字的值,中间名称文本框 FNMName.Text (即Robert'); DROP TABLE STUDENTS; --)和姓氏文本框 LName.Text (让我们看看)将其称为Derper)与查询的其余部分连接,结果现在实际上两个查询statement terminator(分号)分隔。第二个查询已注入到第一个查询中。当代码对数据库执行此查询时,它将如下所示

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

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

  

将新记录添加到Students表,其名称值为'Robert'

  

删除学生表

第二个查询之后的所有内容都是marked as a comment--', 'Derper')

学生姓名中的'不是评论,而是结束string delimiter。由于学生的名字是一个字符串,因此在语法上需要完成假设的查询。当注入的SQL查询导致有效的SQL 时,注入攻击仅在中起作用。

根据dan04的敏锐评论 再次编辑

答案 1 :(得分:583)

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

INSERT INTO Students VALUES ( '$Name' )

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

  

插入学生价值观(' Robert Tables `)

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

  

插入学生价值观(' Robert'); DROP TABLE学生; - ')

你得到的是:

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

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

答案 2 :(得分:153)

正如其他人已经指出的那样,');关闭了原始语句,然后是第二个语句。大多数框架(包括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注入,但如果您能够提供多个语句,则可以执行更具破坏性的操作。

答案 3 :(得分:69)

不,'不是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个答案...... :-)似乎是一个热门话题。

答案 4 :(得分:36)

TL; DR

-- 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;
                      ^

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

此答案中的所有代码示例均在PostgreSQL 9.1.2数据库服务器上运行。

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

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 injection

结果?

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

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

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

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

答案 5 :(得分:27)

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

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命令...

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

答案 6 :(得分:24)

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

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

SQL将成为:

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

在某些系统上,select将首先运行,然后运行drop语句!消息是:不要将值嵌入到您的SQL中。而是使用参数!

答案 7 :(得分:16)

');结束查询,但不会发表评论。然后它删除学生表并评论应该执行的查询的其余部分。

答案 8 :(得分:15)

数据库的作者可能做了

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

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

答案 9 :(得分:15)

在这种情况下,'不是评论字符。它用于分隔字符串文字。漫画艺术家认为有问题的学校在某处看起来像是动态的sql:

$sql = "INSERT INTO `Students` (FirstName, LastName) VALUES ('" . $fname . "', '" . $lname . "')";

所以现在'字符在程序员期待它之前结束了字符串文字。结合;要结束语句的字符,攻击者现在可以添加他们想要的任何sql。 - 最后的注释是为了确保原始语句中剩余的sql不会阻止查询在服务器上进行编译。

FWIW,我也认为有问题的漫画有一个重要的细节错误:如果你正在考虑清理你的数据库输入,正如漫画所暗示的那样,你仍然做错了。相反,您应该考虑隔离数据库输入,正确的方法是通过参数化查询。

答案 10 :(得分:14)

SQL中的'字符用于字符串常量。在这种情况下,它用于结束字符串常量而不用于注释。

答案 11 :(得分:5)

这是它的工作原理: 让我们假设管理员正在寻找学生的记录

Robert'); DROP TABLE STUDENTS; --

由于管理员帐户具有较高权限,因此可以从此帐户中删除该表。

从请求中检索用户名的代码是

现在查询将是这样的(搜索学生表)

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; --

由于未对用户输入进行清理,因此将上述查询操作为2部分

Select * from student where username='Robert'); 

DROP TABLE STUDENTS; --

双短划线( - )只会注释掉查询的剩余部分。

这很危险,因为它可以使密码验证无效(如果存在)

第一个将进行正常搜索。

如果该帐户具有足够的权限,则第二个将删除该表学生(通常,学校管理员帐户将运行此类查询并且将具有上述权限)。

答案 12 :(得分:4)

您无需输入表单数据即可进行SQL注入。

之前没有人指出这一点,因此我可能会提醒一些人。

大多数情况下,我们将尝试修补表单输入。但这不是您唯一会受到SQL注入攻击的地方。您可以使用URL进行非常简单的攻击,该URL通过GET请求发送数据。 请考虑以下示例:

<a href="/show?id=1">show something</a>

您的网址会看起来 http://yoursite.com/show?id=1

现在有人可以尝试这样的事情

http://yoursite.com/show?id=1;TRUNCATE table_name

尝试用实际的表名替换table_name。如果他正确设置了您的表名,他们将清空您的表! (通过简单的脚本强行强制使用此URL很容易)

您的查询看起来像这样...

"SELECT * FROM page WHERE id = 4;TRUNCATE page"

使用PDO的PHP易受攻击的代码示例:

<?php
...
$id = $_GET['id'];

$pdo = new PDO($database_dsn, $database_user, $database_pass);
$query = "SELECT * FROM page WHERE id = {$id}";
$stmt = $pdo->query($query);
$data = $stmt->fetch(); 
/************* You have lost your data!!! :( *************/
...

解决方案-使用PDO prepare()和bindParam()方法:

<?php
...
$id = $_GET['id'];

$query = 'SELECT * FROM page WHERE id = :idVal';
$stmt = $pdo->prepare($query);
$stmt->bindParam('idVal', $id, PDO::PARAM_INT);
$stmt->execute();
$data = $stmt->fetch();
/************* Your data is safe! :) *************/
...