如果我使用下拉菜单,是否必须防范SQL注入?
我知道你永远不要信任表单中的用户input,主要是由于SQL注入的机会。
但是,这是否也适用于唯一的input来自下拉列表(见下文)的表单?
我将$_POST['size']
到一个会话中,然后在整个站点中使用它来查询各种数据库(使用mysqli
Select查询),任何SQL注入都肯定会损害(可能会丢失)它们。
有没有types的用户input查询数据库的区域,只有下拉(S)。
<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>
你可以做一些简单的事情,以确保发布的大小是你所期望的。
$possibleOptions = array('All', 'Large', 'Medium', 'Small'); if(in_array($_POST['size'], $possibleOptions)) { // Expected } else { // Not Expected }
然后使用mysqli_ *,如果您使用的是php> = 5.3.0的版本,则应该保存您的结果。 如果使用正确,这将有助于SQL注入。
是的,你需要防止这一点。
让我告诉你为什么,使用Firefox的开发者控制台:
如果你不清理这些数据,你的数据库将被销毁。 (这可能不是一个完全有效的SQL语句,但我希望我已经明白了。
仅仅因为你限制了下拉菜单中的选项,并不意味着你限制了我可以发送你的服务器的数据。
如果您试图进一步限制您的页面上的行为,我的选项包括禁用该行为,或者只是写一个自定义的HTTP请求到您的服务器模仿此表单提交无论如何。 有一个叫做curl的工具,我认为无论如何提交这个SQL注入的命令是这样的:
curl --data "size=%27%29%3B%20DROP%20TABLE%20*%3B%20--" http://www.example.com/profile/save
(这可能不是一个完全有效的curl命令,但是我希望我已经明白了。
所以,我会重申:
永远不要信任用户的input。 总是保护自己。
不要以为任何用户input都是安全的。 即使通过表单之外的其他方式到达也可能是不安全的。 从来没有一个可信度足以放弃SQL注入的保护。
由于这个问题被标记为SQL注入 ,这是一个关于这种特殊types的攻击的答案:
正如您在评论中所说的那样,您必须为涉及任何可变数据的每个查询使用准备好的语句, 无一例外 。
不pipe任何HTML的东西!
理解SQL查询必须被正确格式化是非常重要的, 无论外部因素如HTMLinput还是其他。
虽然您可以使用其他答案中提供的白名单来进行inputvalidation, 但不应影响任何与SQL相关的操作 – 无论您是否validation了HTMLinput,它们都必须保持不变。 这意味着在向查询中添加任何variables时仍然需要使用准备好的语句。
在这里你可以find一个详细的解释,为什么准备好的语句是必须的,以及如何正确地使用它们,在哪里是不适用的,以及在这种情况下做什么: SQL注入保护的旅游指南
另外,这个问题被标记为mysqli 。 主要是偶然的,我猜想,但无论如何,我必须警告你,原始的mysqli 不能代替旧的mysq_ *函数 。 只是因为如果用在旧的风格,它将不会增加任何安全性。 虽然对准备好的语句的支持是痛苦和麻烦的,但是一般的PHP用户根本就无法完成这些操作。 因此,如果没有ORM或某种抽象库选项,那么PDO是您唯一的select。
是。
任何人都可以欺骗任何实际发送的值 –
所以,为了validation下拉菜单,你可以检查一下你正在使用的值是否在下拉菜单中 – 像这样的话是最好的(最理智的偏执)方式:
if(in_array($_POST['ddMenu'], $dropDownValues){ $valueYouUseLaterInPDO = $dropDownValues[array_search("two", $arr)]; } else { die("effin h4x0rs! Keep off my LAMP!!"); }
防止用户使用控制台更改下拉列表的一种方法是仅在其中使用整数值。 然后,您可以validationPOST值是否包含整数,并在需要时使用数组将其转换为文本。 例如:
<?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
或者一个整数。
其他答案已经涵盖了你需要知道的。 但也许有助于澄清更多:
有两件事你需要做:
1.validation表单数据。
正如Jonathan Hobbs的答案显示非常清楚,表单input的html元素的select不会为您做任何可靠的过滤。
validation通常以不改变数据的方式完成,但是会再次显示表单,其中标记为“请更正此处”。
大多数框架和CMS都有表单构build器,可帮助您完成此任务。 不仅如此,他们还帮助抵制CSRF(或“XSRF”),这是另一种攻击forms。
2.在SQL语句中清理/转义variables
..或让准备好的陈述为你做的工作。
如果使用用户提供的任何variables构build(My)SQL语句,则需要转义并引用这些variables。
一般来说,任何插入MySQL语句的variables都应该是一个string,或者PHP可以可靠地变成一个MySQL可以消化的string。 如数字。
对于string,您需要select几种方法之一来转义string,也就是说,replace任何在MySQL中会有副作用的字符。
- 在老派的MySQL + PHP中,mysql_real_escape_string()完成这项工作。 问题在于它太容易忘记,所以你应该绝对使用预处理语句或查询构build器。
- 在MySQLi中,您可以使用预准备语句。
- 大多数框架和CMS提供查询构build器,可帮助您完成此任务。
如果你正在处理一个数字,你可以省略转义和引号(这就是为什么准备好的语句允许指定一个types)。
重要的是要指出,你逃避了SQL语句的variables,而不是数据库本身 。 数据库将存储原始string,但该语句需要转义版本。
如果你忽略其中的一个,会发生什么?
如果你没有使用表单validation ,但是你要清理你的SQLinput,你可能会看到各种不好的东西发生,但是你不会看到SQL注入! (*)
首先,它可以把你的应用程序变成你没有计划的状态。 例如,如果你想计算所有用户的平均年龄,但是一个用户给这个年龄的“aljkdfaqer”,你的计算就会失败。
其次,可以有其他各种各样的注入攻击你需要考虑:例如,用户input可能包含JavaScript或其他东西。
数据库仍然存在问题:例如,如果一个字段(数据库表列)限制为255个字符,并且string比这个长。 或者如果该字段只接受数字,而您尝试保存一个非数字string。 但这不是“注入”,只是“崩溃应用程序”。
但是,即使你有一个允许任何input但没有任何validation的自由文本字段,你仍然可以像这样保存到数据库,如果你正确地将它转义到数据库语句。 当你想在某个地方使用这个string时,问题就来了。
(*)或者这将是非常奇特的事情。
如果你不逃避SQL语句的variables ,但是你确实已经validation了表单input,那么你仍然可以看到不好的东西发生。
首先,当您将数据保存到数据库并重新加载时,您将面临风险,这将不再是“翻译中丢失的数据”。
其次,它可能会导致无效的SQL语句,从而导致应用程序崩溃。 例如,如果任何variables包含引号或双引号字符,则取决于您使用的引用types,您将获得无效的MySQL语句。
第三,它仍然可以导致SQL注入。
如果来自表单的用户input已被过滤/validation,那么有意识的 SQl注入可能变得不太可能,如果您的input被缩减为硬编码的选项列表,或者被限制为数字。 但是,如果您不正确地转义SQL语句中的variables,则任何自由文本input都可以用于SQL注入。
即使你没有任何表单input,你仍然可以从各种来源获得string:从文件系统读取,从互联网上抓取等。没有人能保证这些string是安全的。
你的网页浏览器并不知道它是从php接收一个页面,它看到的只是html。 而http层知道甚至更less。 你需要能够处理几乎任何可以通过http层的input(幸运的是,对于大多数inputPHP将已经给出了一个错误)。 如果你试图防止恶意请求搞乱你的数据库,那么你需要假设另一端的人知道他在做什么,而且他并不局限于你在正常情况下在浏览器中看到的东西(更不要说你可以用浏览器的开发工具来操作了)。 所以是的,你需要迎合你的任何input,但是对于大多数input你可以给出一个错误。
您已经限制用户只使用某个下拉列表中的值是不相关的。 技术用户可以捕获发送到服务器的http请求,然后离开他们的networking,使用诸如本地代理服务器之类的工具对其进行修改,然后继续进行。 使用已更改的请求,他们可以发送不是您在下拉列表中指定的参数值。 开发人员必须有这样的心态:客户限制往往是毫无意义的,因为客户端上的任何东西都可以被改变。 服务器validation在客户端数据input的每一个点上都是必需的。 攻击者依靠这个唯一方面的开发人员的天真。
为了确保防止SQL注入,最好使用参数化查询。 在这种情况下,查询的外观是这样的:
SELECT * FROM table WHERE size = ?
当你提供一个类似上面的查询,未经validation的完整文本(input未在服务器上validation),它包含SQL注入代码,它将被正确处理。 换句话说,请求会导致类似的情况发生在数据库层:
SELECT * FROM table WHERE size = 'DROP table;'
这将简单地select0结果作为返回,这将使实际上造成损害数据库的查询无效,而不需要白名单,validation检查或其他技术。 请注意,一个负责任的程序员将在层中执行安全性,除了参数化查询以外,还会经常进行validation。 然而,从性能的angular度来看,没有参数化你的查询的原因很less,而且这种做法所增加的安全性是熟悉参数化查询的好理由。
不pipe从你的表单提交什么都作为文本跨越你的服务器。 没有任何东西可以阻止任何人创build一个机器人来模仿客户端,或者如果他们想要从terminalinput。 不要认为,因为你编程的客户端,它会像你想象的那样行事。 这很容易欺骗。
当你信任客户端时会发生什么事情的例子 。
黑客可以通过使用Telnet发送请求,完全绕过浏览器,包括Javascript表单检查。 当然,他会查看你的html页面的代码,以获得他必须使用的字段名称,但从那时起,对他来说,“一切都会好起来”。 所以,你必须检查服务器上提交的所有值,就好像它们不是源自你的html页面一样。