看看:

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

这个SQL做什么:

Robert'); DROP TABLE STUDENTS; --

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


当前回答

在本例中,'不是注释字符。它用于分隔字符串字面量。漫画艺术家的想法是,学校在某个地方有动态sql,看起来像这样:

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

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

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

其他回答

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

Robert'); DROP TABLE STUDENTS; --

由于admin帐户拥有很高的权限,因此可以从该帐户中删除表。

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

现在查询是这样的(搜索student表)

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

双破折号(——)将注释掉查询的其余部分。

这是很危险的,因为如果存在密码验证,它会使密码验证无效

第一个会进行正常的搜索。

如果帐户有足够的权限,第二个将删除表student(通常学校管理帐户将运行这样的查询,并将具有上面讨论的权限)。

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

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

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

SQL将变成:

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

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

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

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

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

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

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

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

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

正如其他人已经指出的那样,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注入,您不需要能够使用两条语句,尽管如果您能够提供多条语句,您可能会做更多破坏性的事情。