1166

只是看着:

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

这个 SQL 做了什么:

Robert'); DROP TABLE STUDENTS; --

我都知道'并且是为了发表评论,但由于它是同一行的一部分,所以--这个词也没有得到评论吗?DROP

4

13 回答 13

1175

它掉落了学生表。

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

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')

用简单的英语,大致翻译为两个查询:

将名称值为“罗伯特”的新记录添加到学生表

删除学生表

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

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

根据dan04的精明评论再次编辑

于 2008-12-01T21:50:58.753 回答
644

假设名称在变量中使用,$Name. 然后运行此查询:

INSERT INTO Students VALUES ( '$Name' )

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

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

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

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

你得到的是:

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

唯一注释该--行的其余部分。

于 2008-12-01T21:55:08.683 回答
171

正如其他人已经指出的那样,');关闭原始语句,然后是第二条语句。大多数框架,包括像 PHP 这样的语言,现在都有默认的安全设置,不允许在一个 SQL 字符串中使用多个语句。mysqli_multi_query例如,在 PHP 中,使用该函数只能在一个 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 注入,尽管如果您能够提供多个语句,您可以做更具破坏性的事情。

于 2011-09-14T10:12:21.957 回答
72

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

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

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

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

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

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

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

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

于 2008-12-01T22:01:41.367 回答
41

TL;博士

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

-- SQL 注入发生在数据库命令的输入被操纵到
-- 使数据库服务器执行任意 SQL
学校=> 插入学生价值观(“罗伯特”);DROP TABLE 学生;--');
插入 0 1
删除表

——学生记录现在不见了——情况可能更糟!
学校=> 从学生中选择 *;
错误:关系“学生”不存在
第 1 行:从学生中选择 *;
                      ^

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

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

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

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

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

插入学生价值观('foobar');

替换foobar为学生的实际姓名。正常的插入操作如下所示:

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

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

学校=> 从学生中选择 *;
 姓名
--------
 约翰
 南希
(2 行)

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

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

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

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

结果?

学校=> 从学生中选择 *;
错误:关系“学生”不存在
第 1 行:从学生中选择 *;
                      ^

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

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

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

于 2011-09-26T20:20:57.627 回答
31

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

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

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

于 2008-12-01T21:56:45.060 回答
29

单引号是字符串的开始和结束。分号是语句的结尾。因此,如果他们正在做这样的选择:

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

SQL 将变为:

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

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

于 2008-12-01T22:03:23.440 回答
19

结束查询,');不开始评论。然后它删除学生表并注释应该执行的其余查询。

于 2008-12-01T21:53:49.230 回答
19

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

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

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

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

于 2008-12-01T21:56:30.100 回答
18

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

于 2008-12-01T21:54:33.613 回答
18

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

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

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

于 2008-12-01T21:55:04.547 回答
8

它是这样工作的:假设管理员正在查找学生的记录

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

由于未对用户输入进行清理,因此上述查询被分为两部分

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

DROP TABLE STUDENTS; --

双破折号 (--) 只会注释掉查询的剩余部分。

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

第一个将进行正常搜索。

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

于 2013-12-02T10:17:02.697 回答
7

您不需要输入表单数据来进行 SQL 注入。

之前没有人指出这一点,所以我可能会提醒你们中的一些人。

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

<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! :) *************/
...
于 2019-07-30T22:04:55.057 回答