需要帮助理解MySQL注入

9

http://www.tizag.com/mysqlTutorial/mysql-php-sql-injection.php得到:

SQL注入是指有人在您不知情的情况下插入要运行在您的数据库上的MySQL语句。注入通常发生在您要求用户输入(例如他们的姓名)时,他们却给您一个MySQL语句,您将无意中在数据库上运行该语句。

我阅读了整篇文章,但仍然存在一些主要问题,不理解它是什么以及如何实现。

在第一个示例中,他们实际上会看到什么?

据我所知,如果我实际上回显$name,他们将看到所有名称,因为它将始终“为真”,我正确吗?

我不理解的另一件事是,mysql_real_escape_string()是否解决了MySQL注入问题,这其中肯定还有更多的内容。

我真正不理解的是,mysql_real_escape_string()是用来解决这个问题的,为什么不自动完成呢?我的意思是说,您必须每次都添加mysql_real_escape_string(),是否有使用它的情况,这就是为什么他们不将其自动化的原因?


1
不,mysql_real_escape_string() 的作用并不是解决问题,而是转义引号。理解这一点非常重要。但几乎没有人真正理解。 - Your Common Sense
@Shrapnel上校 我有点明白了 :) 谢谢。 - Trufa
我有所疑。想象一下,你有一个可以根据用户输入进行排序的查询,例如 SELECT FROM table ORDER BY field,其中 field 是由用户输入的。你会怎么做? - Your Common Sense
我会在stackoverflow.com上发布一个求助的问题 :) 我的意思是,我理解了概念,但完全不知道如何应用它们!正如你从我的问题中所看到的,我对如何处理所有这些都很陌生!在回答你的问题之前,我需要阅读相当多的资料! - Trufa
我第一条评论中的一个声明有两个非常重要的后果:1. 转义与用户、输入或任何类似的废话无关,而是与引用字符串有关,无论其来源如何。2:只有带引号的字符串才能通过转义得到保护。对于其他任何内容进行转义都是无用的。mysql_real_escape_string() 不像 make_my_data_safe() 那样与安全有关,它只与引号有关。 - Your Common Sense
显示剩余2条评论
7个回答

10

MySQL不会自动转义,因为你是自己构建查询字符串的。例如:

$query = 'SELECT * FROM users WHERE name="' . $name . '"';

你只是直接传递了存储在 $query 中的原始字符串,这样容易遭受 SQL 注入攻击。例如,如果 $name 是 [something" OR "1=1],则查询字符串最终变成:

$query = 'SELECT * FROM users WHERE name="something" OR "1=1"

这将返回用户表中的每个用户。这就是为什么您需要转义值。但是,如果您使用PDO,则在使用绑定功能时会自动进行转义。这是一个两步过程,首先准备查询,然后将数据/变量绑定到占位符上。在PDO中,查询字符串应该类似于这样:

$query = 'SELECT * FROM users WHERE name=":name"';
$bindings = array('name'=>'something');
prepare($query);
execute($bindings);

然后,这些内容会自动转义。


我一定会尝试的!看起来非常有趣,而且非常简单,谢谢 +1 - Trufa
只是一条注释。如果不在兼容模式下,PDO 不会转义值。 - Your Common Sense
是的,但没有人像这样使用1=1。那只用于绕过身份验证系统。这个例子不是真的。 - rook
真的吗?这是一个真实的例子,虽然很简单。 - Brent Baisley

5

Bobby Tables提供了一个非常好的关于SQL注入如何工作的概述。其中在多种语言(C#,Java,Perl,PHP等)中给出了很多有益的例子。

在PHP的情况下,它很大程度上取决于你如何访问数据库。您可以受益于使用数据库提取层,例如ADODB,它可以参数化查询。


看起来不错!我一定会好好阅读的!非常感谢! - Trufa

3

当讨论SQL注入时,最常见的例子是“foo' OR 1 = 1”删除整个表或显示密码。这些注入可以通过转义字符串来防止。

然而,还有更简单的注入,其中mysql_real_escape_string()无效。例如,假设您有一个页面,用户可以从数据库中删除选定的条目。常见的实现方法是根据GET或POST变量构建查询以删除条目,例如:

$row_to_delete = $_POST['id'];
$query = "DELETE FROM table WHERE id=$row_to_delete";

如您所见,用户可以轻松地向此脚本发布任何他们想要的“id”,即使对字符串执行了mysql_real_escape_string()操作,也可能删除整个表。同样的漏洞也可以用来猜测管理员的“id”,并篡改各种值。据我所知,唯一的保护措施是从每个可能的角度验证所有get和post参数。基本上,不仅要进行表单验证,还要进行参数验证。
您会惊讶于将这样一个简单的漏洞引入代码中的容易程度。

当然可以!我还没有编写任何值得黑客攻击的程序,但是我的所有项目都可能被一个5岁的孩子删除或者入侵,天哪! - Trufa

3
在Tizag链接的第一个示例中,查询看起来像是脚本作者期望获取至多一行数据。因此,假设每一行都会被获取,最有可能的结果可能是将对返回的第一行信息进行操作;由于篡改后的查询中没有ORDER BY子句,这可能是存储在表中的第一个用户,但当ORDER BY子句缺失时,SQL中并未定义顺序,所以谁能说得准呢。你可以说的是,只要表不为空,它将获取有效用户的详细信息。
我不确定你所说的“如果我回显$name”是什么意思;在代码中,$name变量被赋值为"timmy"。所以他们会看到timmy,我猜。如果你的意思是,如果你尝试通过查询回显给用户获取的信息,他们会看到什么-好吧,这取决于你使用的代码。如果你正在遍历结果集并且他们使用SQL注入来获取你没有预期获取的行,那么他们很可能会看到所有行,包括你不想让他们看到的行。如果你的代码只获取并处理了一行信息,那么他们仍然会看到一行,尽管这可能是你不希望他们能够访问的一行。
至于为什么mysql_real_escape_string()提供的功能不是自动的,那是因为它要求计算机能够从你的SQL代码中找出你想要做什么,而不仅仅是按照你说的去做。这既困难又不理想,因为没有人希望计算机猜测他们想要做什么(特别是程序员)。
如果你想摆脱使用mysql_real_escape_string()之类的函数,你可能需要考虑使用参数化查询,这样可以让你采取稍微更加自由的方法。不过,你仍然需要让计算机清楚地知道你的查询中哪些部分是你想转义的变量,因为这是向计算机传达你想要发生的事情的一部分。

非常感谢您的回答,非常清晰简洁。但据我所知,在高级编程中,实际上是让计算机为您“决定”很多事情,因为如果您开始决定一切,最终会为 HTML 页面编写汇编语言(只是开个玩笑,但你懂我的意思吧?) - Trufa
有些事情是真的,而其他事情则不是,您将希望对发生的细节保持很大的控制权。对发送到数据库的字符串进行操作不可避免地必须是其中之一。例如,请考虑以下两个命令:UPDATE Message SET MessageText = '\'; --' WHERE ID = 12345;UPDATE Message SET MessageText = ''; --' WHERE ID = 12345;。这两个命令具有非常不同的效果。如果您尝试提交其中一个,您真的希望PHP决定您意味着另一个吗? - Hammerite
真聪明的回答! - Trufa

1
据我所知,在制作网站时,您必须始终假定最终用户是一个肮脏的恶棍,想要破坏您的东西。因此,您应该始终使用mysql_real_escape_string();htmlentities();等方法来清理您的字符串。代码可以被注入到您的表单数据中,从而退出它正在执行的操作,插入新代码,然后完全控制您的数据库,并可能控制您的文件结构,这取决于它所能访问的内容。这意味着表、值、密码和整个数据库都可能被销毁或修改。
有些情况下,您可能希望自己注入代码,例如,如果您想制作一个用户界面,可以将代码输入到您的数据库中(类似于phpMyAdmin)。也许最好自动转义代码,然后在需要时取消转义。也许这应该与PHP/mySQL的创建者讨论一下?
这就是我所知道的。我希望其他人能给您更多的见解。只需记住始终清理来自表单和用户输入的返回值即可。

非常感谢您的反馈。 "......最终用户是一个肮脏、恶臭的威胁,想要破坏您的东西。" - 我忍不住笑了!顺便说一下,这是有趣的客户定义。 - Trufa
@Trufa 我也笑得停不下来了,“最终用户是一个肮脏的恶棍,想要破坏你的东西。”这是对转义必要性最愚蠢的解释。你需要转义字符串并不是因为这个原因,而是因为未转义的引号会破坏你的查询。与此无关的是用户。 - Your Common Sense
这太荒谬了!这个线程是关于MySQL注入和黑客想要访问数据库的可能性,而不一定是合法的代码注入!你在逗我吗?当然,mysql_real_escape_string(); 不是为了这个唯一的原因而设计的。放松点,保持相关性... - Partack

1

你说得对,在第一个例子中,输入“不当”名称的人已被允许更改正在执行的数据库查询,并且在这种情况下,他们已将其更改为显示表中的所有行。

至于如何像使用子程序转义特殊字符一样轻松地预防它,您需要了解一个字符串(或任何数据)可以在不同级别上理解。当您接受用户输入然后使用它来构建数据库查询时,您希望数据库服务器将该字符串解释为数据。但是,数据库服务器之所以这样做,仅是因为您使用了单引号等特殊字符,以便它知道字符串何时开始和结束。 转义字符的作用是告诉数据库服务器(或任何其他解释它们的系统)不要将特殊字符解释为特殊字符,而是将它们解释为数据,就像字符串的其余部分一样。这样,如果您的字符串中有其中一个特殊字符,它的特殊功能将被忽略。

关于为什么不自动完成这个过程?这是因为数据库服务器无法知道哪些数据可以信任,哪些数据不能。只有程序员知道,如果他们很幸运的话!而且你不能对所有数据都这样做,因为那些特殊字符(比如单引号)存在是有原因的——它们向数据库服务器传达意义——如果你转义了所有这些字符,那么就没有办法传达它们的意义了。这是计算机科学中非常基本的概念——在系统中,相同的信息可以在不同的层次上进行解释,系统可能会使用该信息中的特殊数据模式来表示何时需要在不同的层次上解释数据。
你可能会发现阅读抽象层概念也很有用,以获得更基本的理解。
祝你好运!

你的解释太糟糕了。根本没有所谓的“可信”或“不可信”的数据,哈哈。只有带引号的字符串。就是这样。转义字符也只适用于带引号的字符串。对于其他任何情况,无论是可信还是不可信,都没有任何帮助。 - Your Common Sense

0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接