如果我使用下拉列表,是否必须防止SQL注入?

时间:2014-03-20 13:16:40

标签: php mysql mysqli sql-injection

据我所知,您不应该信任表单中的用户输入,主要是因为SQL注入的可能性。

但是,这是否也适用于唯一输入来自下拉列表的表单(见下文)?

我将$_POST['size']保存到会话中,然后在整个站点中使用该会话来查询各种数据库(使用mysqli选择查询)并且任何SQL注入肯定会造成损害(可能会丢失)他们。

查询数据库没有用于输入用户输入的区域,只有下拉列表。

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

11 个答案:

答案 0 :(得分:193)

是的,你需要防范这一点。

让我用Firefox的开发者控制台向您展示原因:

  

i've edited one of the values in the dropdown to be a drop table statement

如果您不清理此数据,您的数据库将被销毁。 (这可能不是一个完全有效的SQL语句,但我希望我已经明确了我的观点。)

仅仅因为您限制了下拉列表中可用的选项并不意味着您限制了我可以发送给您服务器的数据。

如果您尝试使用页面上的行为进一步限制此操作,我的选项包括禁用该行为,或者只是向您的服务器编写自定义HTTP请求,无论如何都会模仿此表单提交。有一个名为curl的工具正好用于此,我认为提交此SQL注入的命令无论如何都会是这样的:

curl --data "size=%27%29%3B%20DROP%20TABLE%20*%3B%20--"  http://www.example.com/profile/save

(这可能不是一个完全有效的卷曲命令,但我希望我已经明确了我的观点。)

所以,我会重申:

永远不要信任用户输入。总是保护自己。

不要假设任何用户输入都是安全的。即使它通过表格之外的某些方式到达,它也可能是不安全的。没有任何一个值得信赖的放弃保护自己免受SQL注入。

答案 1 :(得分:68)

您可以执行与以下示例一样简单的操作,以确保发布的大小符合您的预期。

$possibleOptions = array('All', 'Large', 'Medium', 'Small');

if(in_array($_POST['size'], $possibleOptions)) {
    // Expected
} else {
    // Not Expected
}

如果您使用的是php&gt; = 5.3.0版本,则使用mysqli_ *来保存结果。如果使用正确,这将有助于sql注入。

答案 2 :(得分:44)

由于此问题标有,以下是针对此特定类型攻击的答案:

正如您在评论中所说,您必须使用准备好的语句来处理涉及任何变量数据的每个查询无例外

无论HTML内容如何!
必须了解SQL查询必须正确格式化,无论是否为任何外部因素,无论是HTML输入还是其他任何因素。

虽然您可以使用其他答案中建议的白名单来进行输入验证,它不应该影响任何与SQL相关的操作 - 无论您是否验证,它们都必须保持不变HTML输入与否。这意味着在向查询中添加任何变量时,您仍然必须使用预准备语句。

在这里,您可以找到一个详尽的解释,为什么准备好的陈述是必须的,如何正确使用它们以及它们不适用的地方以及在这种情况下该怎么做:The Hitchhiker's Guide to SQL Injection protection

此外,此问题已标记为。我猜想,大多是偶然的,但无论如何,我必须警告你,原始的mysqli 不能替代旧的mysq_ *函数。仅仅因为如果在旧样式中使用,它将根本不添加任何安全性。虽然它对准备好的语句的支持是痛苦和麻烦的,但普通的PHP用户只是无法在所有。因此,如果没有选择ORM或某种抽象库,那么PDO是您唯一的选择。

答案 3 :(得分:12)

是。

任何人都可以为实际发送的值欺骗任何内容 -

因此,为了验证下拉菜单,您可以检查以确保您正在使用的值在下拉列表中 - 这样的方式将是最好的(最明显的偏执)方式:

if(in_array($_POST['ddMenu'], $dropDownValues){

    $valueYouUseLaterInPDO = $dropDownValues[array_search("two", $arr)];

} else {

    die("effin h4x0rs! Keep off my LAMP!!"); 

}

答案 4 :(得分:8)

使用控制台防止用户更改下拉列表的一种方法是仅在其中使用整数值。然后,您可以验证POST值是否包含整数,并在需要时使用数组将其转换为文本。 E.g:

<?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或整数。

答案 5 :(得分:7)

其他答案已涵盖您需要了解的内容。但也许有助于澄清更多:

您需要做两件事

1。验证表单数据。

由于Jonathan Hobbs' answer显示非常清楚,因此表单输入的html元素选择不会对您进行任何可靠的过滤。

验证通常以不改变数据的方式进行,但会再次显示表单,字段标记为&#34;请更正此情况&#34;。

大多数框架和CMS都有表单构建器,可以帮助您完成此任务。不仅如此,它们还有助于反对CSRF(或#34; XSRF&#34;),这是另一种形式的攻击。<​​/ p>

2。 SQL语句中的清理/转义变量..

..或者让准备好的陈述为你完成这项工作。

如果使用用户提供或不提供的任何变量构建(My)SQL语句,则需要转义并引用这些变量。

通常,您插入MySQL语句的任何此类变量都应该是字符串,或者PHP可以可靠地转换为MySQL可以消化的字符串。比如,数字。

对于字符串,您需要选择几种方法之一来转义字符串,这意味着,替换任何在MySQL中会产生副作用的字符。

  • 在老式的MySQL + PHP中,mysql_real_escape_string()完成了这项工作。问题是它太容易忘记了,所以你绝对应该使用预准备语句或查询构建器。
  • 在MySQLi中,您可以使用预准备语句。
  • 大多数框架和CMS都提供了帮助您完成此任务的查询构建器。

如果您正在处理数字,则可以省略转义和引号(这就是准备好的语句允许指定类型的原因)。

重要的是要指出你为SQL语句转义变量,而不是数据库本身。数据库将存储原始字符串,但该语句需要转义版本。

如果省略其中一个,会发生什么?

如果您不使用表单验证,但是您确实清理了SQL输入,那么您可能会看到各种不良内容,但您不会看到SQL注入! (*)

首先,它可以使您的应用程序进入您没有计划的状态。例如。如果你想计算所有用户的平均年龄,但是一个用户给了&#34; aljkdfaqer&#34;对于这个年龄,你的计算将失败。

其次,您可能需要考虑各种其他注射攻击:例如用户输入可能包含javascript或其他内容。

数据库仍然存在问题:例如如果字段(数据库表列)限制为255个字符,并且字符串长于该字符串。或者,如果该字段仅接受数字,则您尝试保存非数字字符串。但这不是&#34;注射&#34;,只是&#34;崩溃了应用程序&#34;。

但是,即使您有一个自由文本字段,您允许任何输入完全没有验证,您仍然可以将其保存到数据库中,如果您在转到数据库语句时正确地将其保存。当你想在某个地方使用这个字符串时会出现问题。

(*)或者这将是非常奇特的东西。

如果你没有逃避SQL语句的变量,但你确实验证了表单输入,那么你仍然可以看到发生了不好的事情。

首先,您冒着将数据保存到数据库并再次加载数据的风险,它不再是相同的数据,并且#34;在翻译中丢失了#34;。

其次,它可能导致无效的SQL语句,从而导致应用程序崩溃。例如。如果任何变量包含引号或双引号字符,则根据您使用的引用类型,您将获得无效的MySQL语句。

第三,它仍然可以导致SQL注入。

如果您的表单用户输入已经过滤/验证,那么故意 SQl注入可能会降低,如果您的输入被缩减为硬编码选项列表,或者仅限于数字。但是,如果你没有正确地转义SQL语句中的变量,任何自由文本输入都可以用于SQL注入。

即使你根本没有表格输入,你仍然可以拥有来自各种来源的字符串:从文件系统读取,从互联网上删除等等。没有人可以保证这些字符串是安全的。

答案 6 :(得分:6)

您的网络浏览器不知道&#34;它正在接收来自php的页面,它看到的只是html。并且http层知道甚至更少。你需要能够处理几乎任何可以跨越http层的输入(幸运的是,大多数输入php都会给出错误)。 如果您试图阻止恶意请求弄乱您的数据库,那么您需要假设另一端的人知道他在做什么,并且他不仅限于您在正常情况下在浏览器中看到的内容(更不用说你可以摆弄浏览器的开发者工具了。 所以,是的,您需要满足下拉列表中的任何输入,但对于大多数输入,您可以给出错误。

答案 7 :(得分:6)

您已将用户限制为仅使用某个下拉列表中的值这一事实无关紧要。技术用户可以在离开网络之前捕获发送到服务器的http请求,使用本地代理服务器等工具对其进行更改,然后继续使用它。使用更改的请求,他们可以发送不是您在下拉列表中指定的参数值。开发人员必须具有客户端限制通常毫无意义的心态,因为客户端上的任何内容都可以被更改。客户端数据进入的每个点都需要进行服务器验证。在这方面,攻击者依赖于开发人员的天真。

答案 8 :(得分:5)

最好使用参数化查询以确保不会出现SQL注入。在这种情况下,查询的外观将是:

SELECT * FROM table WHERE size = ?

当您提供类似上述的查询时,文本未经验证是为了完整性(输入未在服务器上验证)并且它包含SQL注入代码,它将被正确处理。换句话说,请求将导致在数据库层中发生类似这样的事情:

SELECT * FROM table WHERE size = 'DROP table;'

这将只返回0结果,因为它返回,这将使查询无效实际上对数据库造成伤害,而无需白名单,验证检查或其他技术。请注意,负责任的程序员将分层执行安全性,并且除了参数化查询之外,还经常进行验证。但是,从性能角度来看,没有理由不参数化您的查询,并且通过这种做法添加的安全性是熟悉参数化查询的一个很好的理由。

答案 9 :(得分:4)

无论您的表单提交什么,都会通过电汇以文本形式进入您的服务器。没有什么可以阻止任何人创建机器人来模仿客户端或者如果他们想要从终端输入它。永远不要假设,因为您对客户进行了编程,它会像您认为的那样行事。这很容易欺骗。

Example当您信任客户时会发生什么,将会发生什么。

答案 10 :(得分:4)

黑客可以通过使用Telnet发送请求完全绕过浏览器,包括Javascript表单检查。当然,他会查看你的html页面的代码,以获得他必须使用的字段名称,但从那时起,它的所有内容都会出现。为了他。因此,您必须检查服务器上提交的所有值,就好像它们不是来自您的html页面一样。