我知道你绝不应该信任表单中的用户输入,主要是因为SQL注入的可能性.
但是,这是否也适用于唯一输入来自下拉列表的表单(见下文)?
我将保存$_POST['size']
到一个会话,然后在整个站点中使用该会话来查询各种数据库(使用mysqli
Select查询),任何SQL注入肯定会损害(可能会丢失)它们.
查询数据库的类型用户输入没有区域,只有下拉列表.
doppelgreene.. 194
让我告诉你为什么,使用Firefox的开发者控制台:
如果不清除此数据,则会破坏您的数据库.(这可能不是一个完全有效的SQL语句,但我希望我已经明确了我的观点.)
仅仅因为您限制了下拉列表中可用的选项并不意味着您限制了我可以发送服务器的数据.
如果您尝试使用页面上的行为进一步限制此操作,我的选项包括禁用该行为,或者只是向服务器写入自定义HTTP请求,无论如何都会模仿此表单提交.有一个名为curl的工具正是用于此,我认为提交此SQL注入的命令看起来像这样:
curl --data "size=%27%29%3B%20DROP%20TABLE%20*%3B%20--" http://www.example.com/profile/save
(这可能不是一个完全有效的curl命令,但是,我希望我已经明确了我的观点.)
所以,我会重申:
不要假设任何用户输入是安全的.即使它通过表格之外的某些方式到达,它也可能是不安全的.没有任何一个值得信赖的放弃保护自己免受SQL注入.
让我告诉你为什么,使用Firefox的开发者控制台:
如果不清除此数据,则会破坏您的数据库.(这可能不是一个完全有效的SQL语句,但我希望我已经明确了我的观点.)
仅仅因为您限制了下拉列表中可用的选项并不意味着您限制了我可以发送服务器的数据.
如果您尝试使用页面上的行为进一步限制此操作,我的选项包括禁用该行为,或者只是向服务器写入自定义HTTP请求,无论如何都会模仿此表单提交.有一个名为curl的工具正是用于此,我认为提交此SQL注入的命令看起来像这样:
curl --data "size=%27%29%3B%20DROP%20TABLE%20*%3B%20--" http://www.example.com/profile/save
(这可能不是一个完全有效的curl命令,但是,我希望我已经明确了我的观点.)
所以,我会重申:
不要假设任何用户输入是安全的.即使它通过表格之外的某些方式到达,它也可能是不安全的.没有任何一个值得信赖的放弃保护自己免受SQL注入.
您可以执行与以下示例一样简单的操作,以确保发布的大小符合您的预期.
$possibleOptions = array('All', 'Large', 'Medium', 'Small'); if(in_array($_POST['size'], $possibleOptions)) { // Expected } else { // Not Expected }
然后使用mysqli_*如果你使用的是php> = 5.3.0的版本,你应该保存你的结果.如果使用正确,这将有助于sql注入.
由于这个问题是用sql-injection标记的,所以这里有一个关于这种特殊攻击的答案:
正如您在评论中被告知的那样,您必须为涉及任何变量数据的每个查询使用预准备语句,没有例外.
无论任何HTML的东西!
必须了解SQL查询必须正确格式化,无论外部因素如何,无论是HTML输入还是其他任何因素.
虽然您可以使用其他答案中建议的白名单来进行输入验证,但它不应该影响任何与SQL相关的操作 - 无论您是否验证了HTML输入,它们都必须保持不变.这意味着在向查询中添加任何变量时,您仍然必须使用预准备语句.
在这里你可以找到一个彻底的解释,为什么准备好的陈述是必须的,如何正确使用它们以及它们不适用的地方以及在这种情况下该怎么做:Hitchhiker的SQL注入保护指南
此外,这个问题用mysqli标记.我猜想,大多是偶然的,但无论如何,我必须警告你,原始的mysqli 不能替代旧的mysq_*函数.仅仅因为如果使用旧样式,它将根本不添加任何安全性.虽然它对准备好的语句的支持是痛苦和麻烦的,但普通的PHP用户根本无法完成它们.因此,如果没有ORM或某种抽象库可供选择,那么PDO是您唯一的选择.
是.
任何人都可以欺骗任何实际发送的值 -
因此,为了验证下拉菜单,您可以检查以确保您正在使用的值位于下拉列表中 - 这样的方式将是最好的(最明显的偏执)方式:
if(in_array($_POST['ddMenu'], $dropDownValues){ $valueYouUseLaterInPDO = $dropDownValues[array_search("two", $arr)]; } else { die("effin h4x0rs! Keep off my LAMP!!"); }
防止用户使用控制台更改下拉列表的一种方法是仅在其中使用整数值.然后,您可以验证POST值是否包含整数,并在需要时使用数组将其转换为文本.例如:
'All', 1 => 'Large', 2 => 'Medium', 3 => 'Small'); $size = filter_input(INPUT_POST, "size", FILTER_VALIDATE_INT); echo '';
然后,您可以$size
在查询中使用它只包含的知识FALSE
或整数.
其他答案已涵盖您需要了解的内容.但也许有助于澄清更多:
有两件事情你需要做的:
正如Jonathan Hobbs的回答非常清楚地表明,为表单输入选择html元素并不能为您做任何可靠的过滤.
验证通常以不改变数据的方式进行,但是再次显示表单,标记为"请更正此"的字段.
大多数框架和CMS都有表单构建器,可以帮助您完成此任务.不仅如此,它们还有助于反对CSRF(或"XSRF"),这是另一种形式的攻击.
..或者让准备好的陈述为你完成这项工作.
如果使用用户提供或不提供的任何变量构建(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注入.
即使你根本没有表格输入,你仍然可以拥有来自各种来源的字符串:从文件系统中读取,从互联网上删除等等.没有人可以保证这些字符串是安全的.
您的网络浏览器"不知道"它正在从php接收页面,它看到的只是html.并且http层知道甚至更少.你需要能够处理几乎任何可以跨越http层的输入(幸运的是,大多数输入php都会给出错误).如果您试图阻止恶意请求弄乱您的数据库,那么您需要假设另一端的人知道他在做什么,并且他不仅限于您在正常情况下在浏览器中看到的内容(更不用说你可以摆弄浏览器的开发者工具了.所以是的,您需要满足下拉列表中的任何输入,但对于大多数输入,您可以给出错误.
您将用户限制为仅使用某个下拉列表中的值这一事实无关紧要.技术用户可以在离开网络之前捕获发送到服务器的http请求,使用本地代理服务器等工具对其进行更改,然后继续使用它.使用更改的请求,他们可以发送不是您在下拉列表中指定的参数值.开发人员必须具备客户端限制通常毫无意义的心态,因为客户端上的任何内容都可以被更改.客户端数据进入的每个点都需要服务器验证.在这方面,攻击者依赖于开发人员的天真.
最好使用参数化查询以确保不会出现SQL注入.在这种情况下,查询的外观将是这样的:
SELECT * FROM table WHERE size = ?
当您提供如上所述的查询时,文本未经验证是为了完整性(输入未在服务器上验证)并且它包含SQL注入代码,它将被正确处理.换句话说,请求将导致在数据库层中发生这样的事情:
SELECT * FROM table WHERE size = 'DROP table;'
这将简单地选择返回0结果,这将使查询无效实际上对数据库造成损害而无需白名单,验证检查或其他技术.请注意,负责任的程序员将分层执行安全性,并且除了参数化查询之外,还经常进行验证.但是,从性能角度来看,没有理由不对查询进行参数化,并且通过这种做法添加的安全性是熟悉参数化查询的一个很好的理由.