原始SQL

编写SQL时-对于确实需要人工输入的任何事情,已经做了很多事情来避免注入。

每个听到的人SQL注入知道(我将使用PHP作为示例)这样做是不安全的:

$sql = "SELECT * FROM `users` 
.  WHERE `userName` = "{$_POST["username"]}" 
.  AND `pass` = "{$_POST["pass"]}";";


Magic

然后,当然,有人想到了使用“魔术转义引号”来处理程序输入的错误输入,这些输入由于错误的做法而没有被正确处理并直接放入sql中。这并不能真正解决SQL注入的问题,但这确实意味着所有用户输入都被弄乱了。

添加斜线

因此,有些人关闭了魔术引号。然后,他们通过addslashes()解析了SQL之前的用户输入,从理论上讲,它转义了所有引号,并且您的黑客无法执行' OR 1=1,但是即使是关于addlashes的文档,它也自称您不应该使用adslashes,而是使用特定于数据库的功能,例如mysql_real_escape_string(),但是对于某些人来说,这还是不够的。 *_real_escape_string,我们不能使用add slashes,“魔术引号”会引起很多问题,并且网络上充斥着简短的引号,例如:


“一个专门的黑客会发现
跳过引号转义循环的一种方法,
只需使用DBAL准备的语句”-John Q任何程序员


好吧,这样就吓到我了使用prepare语句和DBAL。它并没有真正解释任何内容,但是听起来不错,因为我已经听了很多。

准备好的语句

所以现在我们使用的是PDO或DBAL从框架或其他包装我们所有sql并确保某人不能运行sql注入的其他东西。

我的问题基本上是“为什么不?”,而不是“我应该使用什么?”。网络上到处都是人告诉你要使用这个或使用那个或其他任何东西,但是没有解释为什么这些事情必须发生的原因。
直接提问

指出的问题(提醒,我问的是SQL,PHP是一种示例语言,因为它在SQL方面的表现不佳,概念是通用的):


为什么我们不能使用“ magic”转义所有用户输入
为什么addlashes不够好?
使用特定于数据库的转义函数有什么问题,为什么比addlashes更好呢?
为什么准备使用框架和PDO的预备语句被誉为SQL的黄金标准?他们为什么更好?我为什么不能用这些方法进行SQL注入,而我可以使用前面提到的方法呢?程序员可以不以某种方式设法解决这个问题吗?他们应该注意什么?
我还没有提出其他任何疑问?


#1 楼


为什么我们不能使用“魔术”来转义所有用户输入?


在应用魔术时,尚不清楚数据将在何处结束。因此,魔术引号正在破坏数据,除非将其不转义地写入数据库。

它只能用于发送回客户端的HTML响应中。想一想尚未完全填写的表格,因此会再次向用户显示。使用魔术引号,第一次尝试输入的数据现在将被转义为SQL,这在HTML页面上是没有意义的。更糟糕的是:在第二次提交时,数据再次被SQL转义。


为什么加号“不够好”?


它具有多字节字符的问题:

' 27
\ 5c
뼧 bf 5c


那是两个字节,但是只有一个Unicode字符。

由于addslashes对Unicode一无所知,它将输入bf 27转换为bf 5c 27。如果由支持Unicode的程序读取此内容,则将其视为뼧。 Boom。

http://shiflett.org/blog/2006/jan/addslashes-versus-mysql-real-escape-string

使用特定于数据库的转义函数有什么问题,为什么它比添加斜杠更好?


它们更好,因为它们确保转义解释了数据中的数据。数据库也是如此(请参阅最后一个问题)。

从安全性的角度来看,如果将它们用于每个数据库输入,则可以使用它们。但是它们冒着使您可能会忘记其中任何一个的风险。通过将输入转换为相应的数据类型或将其转换为实际的数据类型,它们是实际数字。/ Edit

从软件开发的角度来看,它们是不好的,因为它们使添加对其他SQL数据库服务器软件的支持变得更加困难。 br />

为什么带有框架和PDO的准备好的语句被誉为SQL的黄金标准?他们为什么更好?


出于软件设计的原因,PDO通常是一件好事。它使支持其他数据库服务器软件变得更加容易。它具有一个面向对象的接口,该接口抽象了许多特定于数据库的小不兼容性。


为什么我不能对这些[prepared statement]进行SQL注入,而我却可以这样做前面提到的方法是什么?


准备好的语句的“带有可变参数的常量查询”部分在这里很重要。数据库驱动程序将自动转义所有参数,而无需开发人员考虑。

出于语法原因,参数化查询通常比带有转义参数的普通查询更容易阅读。根据环境的不同,它们可能会更快一些。

始终使用带参数的预备语句可以通过静态代码分析工具进行验证。不能轻易可靠地找到对xxx_escape_string的丢失调用。


程序员不能以某种方式设法仍然解决这个问题吗?他们应该注意什么?


“ Prepared statement”暗示the是常数。动态生成准备好的语句-尤其是在用户输入的情况下-仍然存在所有注入问题。

评论


这很深入,不现实。 99.9%的Web应用程序不受语言编码问题的影响。通过静态代码分析发现〜.1%的sql注入。专业人士采用了更好的技术。根据经验,我可以告诉您,我所涵盖的问题非常普遍。

–rook
2011年5月7日20:53



我不同意@ Rook,Hendrik的回答很现实。由于许多Web应用程序被构建为使用特定的DB,直到生命周期终止,因此PDO的要求几乎是不必要的。因此,开发人员可能还使用了相关功能,而不是使应用程序过于复杂以使用扩展功能的2%。

–基督徒
2011年5月8日7:55



只有4种语言集的addlashes()存在问题,gbk是其中之一。

–rook
2011年5月8日20:32



@Rook-再一次,您是在侮辱而不是回答问题。这是不可接受的。您需要理清您的沟通方式和行为,否则我们将不得不暂停。请将此作为礼貌的警告。

–Rory Alsop♦
2011年9月29日在20:44

关于最新的问题“程序员是否能以某种方式仍然无法解决问题?”,我只想链接到最近一个影响Drupal CMS内容的SQL注入问题,绕过PDO保护。

–WhiteWinterWolf
2014年12月18日在16:05

#2 楼


[1.]为什么我们不能使用“ magic”来转义所有用户输入?


因为魔术引号在两个方面都被打断了:


并非所有$ _(REQUEST / GET / POST)数据都进入DB。为了使您理解它,您必须转义输入。
它们等效于addslashes(请参阅我在[2.]上的注释),因此它们实际上并不安全。 />

[2.]为什么加号“不够好”?


有很多方法可以摆脱addslashes的限制,因此
,但是您尝试使用它。


[3.]使用特定于DB的转义函数有什么问题,为什么它比添加斜杠更好?


没什么。这只是为什么PDO / prepared“更好”的口号。它们比addslashes更好,因为它们会考虑很多因素,包括编码。这是一个小秘密; PDO在内部使用它们,而不使用addslashes ... [4。]为什么带有框架和PDO的准备好的语句为什么被誉为SQL的黄金标准?他们为什么更好?我为什么不能用这些方法进行SQL注入,就像我以前提到的方法那样?


它们不是黄金标准,不是“更多”比特定于DB的功能更安全,您也可以使用这些功能进行SQL注入。而且不,您不能使用经过正确处理的输入来进行SQL注入。

与所有现有技术一样,开发人员倾向于为任何“新”事物开发宗教(狂热)倾向。这就是为什么您会得到经验丰富的Zend Certified Engineers(TM)的建议,而不是强迫您切换到准备好的语句。

然而,现实是,这一切都取决于知道自己在做什么。如果要通过数字ID加载文章,则不需要任何转义,甚至不需要更少的PDO。您只需要确保ID确实是整数即可。

从更现实的角度来看,一般规则不是使用PDO /准备好的语句,而是使用正确的功能,也就是说,必须使用mysql_real_escape_string()而不是addslashes()mysql_escape_string()


[5.]程序员不能以某种方式设法继续解决这个问题吗?他们应该注意什么?


当然!但是它不是“寻找什么”,而是“知道您在做什么”。

您会看到,eval($_GET['code']) *在正确的地方(例如本地)使用完全合法PHP Test Runner)。

如果使用ORM运行条件查询,则直接输入代码,因此,如果操作不正确,则有可能获得SQL注入(取决于相关的ORM)。 (假想的)示例:

// update() sets the value of a column given a condition
//
//                         .--escaping is automatic here
//                         |                           .--hypothetical SQL injection
//                         v                           v
Db()->update('name',$_GET['newname'])->where(' id = '.$_GET['id']);


(*我只能想象无数的专业人士为此而努力)

评论


+1。尤其适用于指出将参数化查询与动态部分混合在一起的API的陷阱。

–亨德里克·布鲁默曼
2011年5月8日13:19

存储Web请求或编码数据的一种有用技术是将其编码为base64并将其存储为字符串。它不能防止SQL注入或坏数据,但是至少可以确保输入的任何内容都不会被使用。

–醉酒的代码猴子
16年7月5日在22:53

#3 楼

在一天结束时,必须完成输入转义,以保护SQL注入查询。诸如PDO和ADODB之类的参数化查询库使用转义,它们所做的只是以隐式安全的方式实施此做法。通过对比magic_quotes_gpc不会,这从根本上来说是解决问题的方法。

1)
如果您不了解自己的所作所为,转义可能会出现问题。如果启用了magic_quotes_gpc,则仍然可以利用这种类型的问题。

mysql_query("select * from users where id=".addslashes($_GET[id]));


PoC漏洞利用:
http://localhost/vuln.php?id = sleep( 30)

补丁:

mysql_query("select * from users where id='".addslashes($_GET[id])."'");


课程:
不需要引号即可利用sql注入。

2)假设您只转义了引号:

mysql_query("select * from users where name='".htmlspecialchars($_GET[name],ENT_QUOTES)."' and id='".htmlspecialchars($_GET[id],ENT_QUOTES)."'");


PoC漏洞利用:
http://localhost/vuln.php?name = \&id = +或+ sleep(30)/ *

补丁:

mysql_query("select * from users where name='".addslashes($_GET[name])."' and id='".addslashes($_GET[id])."'");


课程:反斜杠与引号一样危险。

3)语言编码呢?

mysql_query("SET CHARACTER SET 'gbk'");
mysql_query("select * from user where name='".addslashes($_GET[name])."'");


PoC漏洞利用:http://localhost/vuln.php?name =%bf%27 = sleep(30)/ *

补丁:

mysql_query("SET CHARACTER SET 'gbk'");
mysql_query("select * from user where name='".mysql_real_escape_string($_GET[name])."'");


结论:

为了防止在应用程序中进行sql注入,绝对需要转义。在PHP中,PDO和ADOB是强大的参数化查询库,可强制转义用户输入。问题是许多程序员不了解什么是转义。拥有实施该安全策略的库是最好的防御方法,因为您无需了解转义规则。

评论


“绝对必需转义以防止在应用程序中进行sql注入”,这是完全错误的。与使用转义相比,发生错误的方法要少得多,它是使用带有常量查询部分的参数化查询以及参数中所有不受信任的数据。由于数据永远都不是SQL查询字符串的一部分,因此不需要转义,并且数据库将做正确的事情。 ///请不要建议使用加号,因为PHP应用程序可能会受到字符集攻击,即使由于例如MySQL默认配置而没有显式设置字符集的情况下也是如此。

–亨德里克·布鲁默曼
2011年5月7日20:41



以mysqli驱动程序为例:函数mysqlnd_stmt_execute_store_params将查询字符串外部的参数作为二进制数据发送到服务器。

–亨德里克·布鲁默曼
2011年5月7日21:43

@Hendrik Brummermann此功能支持哪些顶级参数化查询功能(php.net/manual/en/book.mysql.php)?我认为您误会了此代码的功能。我知道说“转义是不好的,应该改用PDO”是不恰当的。

–rook
2011年5月7日在22:19

最终调用该内部驱动程序函数的相关联的高级查询函数集是prepare,bind_param和execute。在大多数情况下,我自己使用转义方式,但是使用参数化查询是一种有效的选择。并且尽管有一些框架将参数替换为SQL语句(具有适当的转义),但是数据库函数将它们与查询一起传输。

–亨德里克·布鲁默曼
2011年5月7日22:31



“拥有一个图书馆来执行这种安全策略是最好的防御方法,因为您不需要了解逃避规则。” –这就是我的朋友们,这就是我们在安全方面失败的原因。事实是程序员需要知道他/她在做什么。不知道本身就是安全问题。使用PDO等就像使用OSX,您知道自己是安全的,直到有足够多的人让黑客重新考虑他们的策略时-当该病毒消失后,Windows 98就会重新出现。

–基督徒
2011年5月8日在7:52

#4 楼

关于特定于数据库的转义功能。即使使用了mysql_real_escape_string(),在许多情况下也可能发生SQL注入。 LIMIT和ORDER BY真的很棘手!

这些示例容易受到SQL注入的攻击:

$offset = isset($_GET['o']) ? $_GET['o'] : 0;
$offset = mysql_real_escape_string($offset);
$sql = "SELECT userid, username FROM sql_injection_test LIMIT $offset, 10";

$order = isset($_GET['o']) ? $_GET['o'] : 'userid';
$order = mysql_real_escape_string($order);
$sql = "SELECT userid, username FROM sql_injection_test ORDER BY `$order`";


两种情况下都不能使用'保护封装。

源:意外的SQL注入(当转义不够时)<-阅读此内容

#5 楼

可以绕过过滤器,并且进入SQL语句的数据可以来自更多的地方,而不仅仅是用户输入。作为一个示例,可以存储受SQL影响的源/接收器的入口点(高阶),但是显然,来自HTML表单的HTTP GET参数和POST参数不是用户输入的唯一方法。

参数化查询不一定会使您的SQL语句免于注入。它们还需要变量绑定,删除字符串操作/连接以及避免使用大量SQL子句来避免某些安全问题。

大多数开发人员还忘记检查其第三方组件和其他依赖项对于SQL注入问题,有时甚至没有意识到这些包含可能会使自己的代码易受攻击。

最好的办法是雇用应用程序安全咨询公司来为您的应用程序做准备生产准备就绪,并培训您的appdev团队以将应用程序安全控制构建到他们的流程和开发技术中。

#6 楼

扩展亨德里克的答案。很容易将real_escape_string视为魔术子弹,而实际上却并非如此。我见过人们在想要使用intval或将输入转换为整数时使用该函数。如果在错误的上下文中使用转义字符串,您仍然可以注入。

#7 楼

因为关键是要帮助您学习如何编写体面的代码,而不仅仅是复制食谱,所以我会留下一些提示。


如果我们留下编码的东西,有很多注入方法sql,但并非所有人都要求您添加“或'或',因此Addslashes基本上是由la脚和不熟练的开发人员使用的la脚解决方案...。(您不应该从中读取代码的人)。
虽然并没有很多发生,但是实际上,在某些数据库引擎中,攻击者可以利用一个向量来利用预备语句,这并不常见,但是您可以记住,预备语句使用的是语句,但是表元数据列的类型为size。 ...
其他常见的la脚错误是处理喜欢查询中的搜索.....如果发现其工作原理,则可以做一些有趣的事情....

研究,永远不要相信输入,永远不要使用准备好的语句,不要理anyone任何告诉您使用转义符的人。除了期望之外,别无所求,因此强类型的api是您的朋友;)。

评论


您需要清除此答案-拼写,语法,奇数格式-请在回答中多花点时间

– schroeder♦
2014年10月8日23:09