提问人:Tatters 提问时间:3/20/2014 最后编辑:doppelgreenerTatters 更新时间:6/3/2019 访问量:12294
如果我使用下拉列表,我是否必须防止 SQL 注入?
Do I have to guard against SQL injection if I used a dropdown?
问:
我知道你永远不应该相信来自表单的用户输入,主要是因为SQL注入的机会。
但是,这是否也适用于唯一输入来自下拉列表的表单(见下文)?
我正在将其保存到一个会话中,然后在整个站点中使用该会话来查询各种数据库(使用选择查询),任何 SQL 注入肯定会损害(可能删除)它们。$_POST['size']
mysqli
没有用于查询数据库的类型化用户输入区域,只有下拉列表。
<form action="welcome.php" method="post">
<select name="size">
<option value="All">Select Size</option>
<option value="Large">Large</option>
<option value="Medium">Medium</option>
<option value="Small">Small</option>
</select>
<input type="submit">
</form>
答:
是的。
任何人都可以欺骗任何实际发送的值 --
因此,为了验证下拉菜单,您可以检查以确保您正在使用的值在下拉列表中 - 像这样的东西将是最好的(最理智的偏执)方法:
if(in_array($_POST['ddMenu'], $dropDownValues){
$valueYouUseLaterInPDO = $dropDownValues[array_search("two", $arr)];
} else {
die("effin h4x0rs! Keep off my LAMP!!");
}
评论
您可以执行以下示例中简单的操作,以确保发布的大小符合您的预期。
$possibleOptions = array('All', 'Large', 'Medium', 'Small');
if(in_array($_POST['size'], $possibleOptions)) {
// Expected
} else {
// Not Expected
}
如果您使用的是 php >= 5.3.0 版本,请使用 mysqli_* 来保存结果。如果使用得当,这将有助于 sql 注入。
评论
mysqli_real_escape_string
。在输出值时也要正确转义它们(例如,在 HTML 文档中使用 htmlspecialchars())。
in_array
true
防止用户使用控制台更改下拉列表的一种方法是仅在其中使用整数值。然后,您可以验证 POST 值是否包含整数,并在需要时使用数组将其转换为文本。例如:
<?php
// No, you don't need to specify the numbers in the array but as we're using them I always find having them visually there helpful.
$sizes = array(0 => 'All', 1 => 'Large', 2 => 'Medium', 3 => 'Small');
$size = filter_input(INPUT_POST, "size", FILTER_VALIDATE_INT);
echo '<select name="size">';
foreach($sizes as $i => $s) {
echo '<option value="' . $i . '"' . ($i == $size ? ' selected' : '') . '>' . $s . '</option>';
}
echo '</select>';
然后,您可以在知道它只包含整数的情况下在查询中使用。$size
FALSE
评论
filter_input
filter_input
由于这个问题被标记为sql-injection,以下是有关这种特定类型攻击的答案:
正如您在评论中被告知的那样,您必须对涉及任何变量数据的每个查询使用准备好的语句,没有例外。
不管任何 HTML 的东西!
必须了解,无论任何外部因素(无论是 HTML 输入还是其他任何因素)如何,都必须正确格式化 SQL 查询。
尽管您可以使用其他答案中建议的白名单进行输入验证,但它不会影响任何与 SQL 相关的操作 - 无论您是否验证了 HTML 输入,它们都必须保持不变。这意味着在向查询中添加任何变量时,您仍然必须使用预准备语句。
在这里,您可以找到详尽的解释,为什么必须准备好的语句,以及如何正确使用它们,它们在哪些地方不适用,以及在这种情况下该怎么做: SQL注入保护的漫游指南
此外,这个问题被标记为mysqli。我猜这主要是偶然的,但无论如何,我必须警告你,原始 mysqli 并不能充分替代旧的 mysq_* 函数。仅仅因为如果以旧样式使用,它根本不会增加安全性。虽然它对准备好的语句的支持是痛苦和麻烦的,但普通PHP用户根本无法尝试它们。因此,如果没有 ORM 或某种抽象库是选项,那么 PDO 是您唯一的选择。
评论
你的 Web 浏览器并不“知道”它正在接收来自 php 的页面,它看到的只是 html。而 http 层知道的甚至比这还要少。你需要能够处理几乎任何类型的输入,这些输入可以跨越http层(幸运的是,对于大多数输入来说,php已经给出了一个错误)。 如果你试图防止恶意请求弄乱你的数据库,那么你需要假设另一端的人知道他在做什么,并且他不仅限于你在正常情况下可以在浏览器中看到的内容(更不用说你可以摆弄浏览器的开发人员工具了)。 所以是的,你需要满足下拉列表中的任何输入,但对于大多数输入,你可能会给出一个错误。
您限制用户只能使用某个下拉列表中的值这一事实无关紧要。技术用户可以在服务器离开其网络之前捕获发送到服务器的 http 请求,使用本地代理服务器等工具对其进行更改,然后继续执行。使用更改的请求,他们可以发送参数值,这些参数值不是您在下拉列表中指定的参数值。开发人员必须有这样一种心态,即客户端限制通常毫无意义,因为客户端上的任何内容都可以更改。在客户端数据输入的每个点都需要进行服务器验证。攻击者仅依靠开发人员在这方面的天真。
最好使用参数化查询,以确保不会出现 SQL 注入。在这种情况下,查询的外观如下:
SELECT * FROM table WHERE size = ?
当您提供如上所述的查询时,如果文本的完整性未经验证(输入未在服务器上验证),并且其中包含 SQL 注入代码,则将正确处理该查询。换句话说,该请求将导致数据库层发生类似的事情:
SELECT * FROM table WHERE size = 'DROP table;'
这将在返回时简单地选择 0 个结果,这将使查询在实际对数据库造成损害方面无效,而无需白名单、验证检查或其他技术。请注意,负责任的程序员将分层进行安全保护,并且除了参数化查询外,还经常进行验证。但是,从性能的角度来看,没有理由不对查询进行参数化,并且这种做法增加的安全性是熟悉参数化查询的一个很好的理由。
是的,您需要防止这种情况。
让我告诉你为什么,使用Firefox的开发者控制台:
如果不清除此数据,数据库将被销毁。(这可能不是一个完全有效的 SQL 语句,但我希望我已经明白了我的观点。
仅仅因为您限制了下拉列表中可用的选项并不意味着您限制了我可以发送给您的服务器的数据。
如果您尝试使用页面上的行为进一步限制这种情况,我的选择包括禁用该行为,或者只是向您的服务器编写一个自定义 HTTP 请求,无论如何都会模仿此表单提交。有一个名为 curl 的工具用于此目的,我认为无论如何提交此 SQL 注入的命令将如下所示:
curl --data "size=%27%29%3B%20DROP%20TABLE%20*%3B%20--" http://www.example.com/profile/save
(这可能不是一个完全有效的 curl 命令,但同样,我希望我已经表达了我的观点。
所以,我要重申:
永远不要相信用户输入。始终保护自己。
不要假设任何用户输入都是安全的。即使它通过表格以外的其他方式到达,它也可能不安全。没有一个值得信赖,以至于放弃保护自己免受SQL注入的侵害。
评论
curl
curl
其他答案已经涵盖了您需要了解的内容。但也许它有助于澄清更多:
您需要做两件事:
1. 验证表单数据。
正如乔纳森·霍布斯(Jonathan Hobbs)的回答非常清楚地表明的那样,为表单输入选择html元素并不能为您进行任何可靠的过滤。
验证通常以不更改数据的方式完成,但会再次显示表单,字段标记为“请更正此项”。
大多数框架和 CMS 都有表单构建器来帮助您完成此任务。不仅如此,它们还有助于对抗 CSRF(或“XSRF”),这是另一种形式的攻击。
2. 清理/转义 SQL 语句中的变量。
..或者让准备好的语句为您完成这项工作。
如果使用任何变量(无论是否由用户提供)构建 (My)SQL 语句,则需要转义并引用这些变量。
通常,您插入到MySQL语句中的任何此类变量都应该是字符串,或者PHP可以可靠地转换为MySQL可以消化的字符串。如数字。
对于字符串,您需要选择几种方法之一来转义字符串,这意味着,替换任何在MySQL中会产生副作用的字符。
- 在老式的 MySQL + PHP 中,mysql_real_escape_string() 可以完成这项工作。问题是它太容易忘记了,所以你绝对应该使用准备好的语句或查询构建器。
- 在 MySQLi 中,您可以使用预准备语句。
- 大多数框架和 CMS 都提供查询生成器来帮助您完成此任务。
如果要处理一个数字,则可以省略转义和引号(这就是预准备语句允许指定类型的原因)。
需要指出的是,您转义了 SQL 语句的变量,而不是数据库本身的变量。数据库将存储原始字符串,但语句需要转义版本。
如果省略其中之一会怎样?
如果你不使用表单验证,但你确实清理了你的 SQL 输入,你可能会看到各种不好的事情发生,但你不会看到 SQL 注入!(*)
首先,它可以将您的应用程序带入您未计划的状态。例如,如果要计算所有用户的平均年龄,但有一个用户为年龄提供了“aljkdfaqer”,则计算将失败。
其次,您可能需要考虑各种其他注入攻击:例如,用户输入可能包含 javascript 或其他内容。
数据库可能仍然存在问题:例如,如果一个字段(数据库表列)限制为 255 个字符,并且字符串比该长度长。或者,如果该字段只接受数字,而您尝试保存非数字字符串。但这不是“注入”,它只是“使应用程序崩溃”。
但是,即使您有一个自由文本字段,您可以在其中允许任何完全不进行验证的输入,您仍然可以像这样将其保存到数据库中,前提是当它转到数据库语句时正确转义它。当您想在某处使用此字符串时,问题就来了。
(*) 否则这将是非常奇特的东西。
如果您没有对 SQL 语句的变量进行转义,但您确实验证了表单输入,那么您仍然可以看到不好的事情发生。
首先,当您将数据保存到数据库并再次加载时,它不再是相同的数据,“在翻译中丢失”。
其次,它可能导致无效的 SQL 语句,从而使您的应用程序崩溃。例如,如果任何变量包含引号或双引号字符,则根据您使用的引号类型,您将获得无效的MySQL语句。
第三,它仍然会导致SQL注入。
如果您的用户从表单输入已经过过滤/验证,则如果您的输入被简化为硬编码的选项列表,或者如果它仅限于数字,则有意的 SQl 注入的可能性可能会降低。但是,如果您没有正确转义 SQL 语句中的变量,则任何自由文本输入都可以用于 SQL 注入。
即使你根本没有表单输入,你仍然可以有来自各种来源的字符串:从文件系统中读取,从互联网上抓取,等等。没有人能保证这些字符串是安全的。
评论
从您的表单中提交的任何内容都会以文本的形式通过网络发送到您的服务器。没有什么能阻止任何人创建一个机器人来模仿客户端,或者如果他们愿意的话,可以从终端输入它。永远不要以为因为你对客户端进行了编程,它就会像你想象的那样运行。这真的很容易被欺骗。
当您信任客户端时可能发生和将要发生的情况示例。
黑客可以通过使用 Telnet 发送请求来完全绕过浏览器,包括 Javascript 表单检查。当然,他会查看你的html页面的代码,以获得他必须使用的字段名称,但从那时起,对他来说就是“一切正常”。因此,您必须检查服务器上提交的所有值,就好像它们不是来自您的 html 页面一样。
评论
<select>