什么是最好的PHP输入清理功能?


161

我正在尝试提出一个函数,该函数可以传递所有字符串以进行清理。这样,从其中出来的字符串就可以安全地插入数据库了。但是有很多过滤功能,我不确定应该使用/需要哪些功能

请帮助我填写空白:

function filterThis($string) {
    $string = mysql_real_escape_string($string);
    $string = htmlentities($string);
    etc...
    return $string;
}

4
对于插入,可以使用mysql_real_escape_string对SQL注入进行消毒。当您使用SELECTED数据(在html输出或php公式/函数中)时,应应用htmlentities
davidosomething 2010年

看到 清理数据库插入的特定答案, stackoverflow.com/questions/60174/…(它提供了一个PDO的示例,下面其他人会提到)。
专利

Answers:


433

停止!

您在这里犯了一个错误。哦,不,您选择了正确的PHP函数来使您的数据更安全。没关系。您的错误在于操作顺序以及如何以及在何处使用这些功能。

重要的是要了解在清理和验证用户数据,转义数据进行存储和转义数据进行呈现之间的区别。

消毒和验证用户数据

用户提交数据时,您需要确保他们提供了期望的内容。

消毒和过滤

例如,如果您希望输入数字,请 确保提交的数据是数字。你也可以投射用户数据转换为其他类型。最初提交的所有内容都像字符串一样对待,因此将已知数值数据强制为整数或浮点数可以使清理工作变得快捷而轻松。

自由格式的文本字段和文本区域呢?您需要确保在这些字段中没有任何意外。主要是,您需要确保不应包含任何HTML内容的字段实际上不包含HTML。有两种方法可以解决此问题。

首先,您可以尝试使用来转义 HTML输入htmlspecialchars。你不应该使用htmlentities中和HTML,因为它还会对重音符号和它认为也需要编码的其他字符进行编码。

其次,您可以尝试删除所有可能的HTML。 strip_tags既方便又快捷,而且草率。 HTML净化器在剥离所有HTML以及允许通过标记和属性的选择性白名单方面做得更加彻底。

现代PHP版本附带有filter扩展,它提供了一种清理用户输入的综合方法。

验证方式

确保提交的数据中没有意外内容,只是工作的一半。您还需要尝试确保提交的数据包含您可以实际使用的值。

如果期望数字介于1到10之间,则需要检查该值。如果您正在使用带有微调器和步骤的那些新颖的HTML5-era数字输入之一,请确保提交的数据与步骤一致。

如果该数据来自应该是下拉菜单的数据,请确保提交的值是菜单中显示的值。

满足其他需求的文本输入呢?例如,日期输入应通过strtotimeDateTime类进行验证。给定的日期应该在您期望的范围之间。电子邮件地址呢?尽管我是is_email库的粉丝,但前面提到的过滤器扩展可以检查地址格式是否正确

所有其他窗体控件也是如此。有单选按钮吗?根据列表进行验证。有复选框吗?根据列表进行验证。有文件上传吗?确保文件为预期类型,并将文件名视为未过滤的用户数据。

每个现代的浏览器都内置有完整的开发人员工具集,这对任何人操作您的表单来说都是微不足道的。 您的代码应假定用户已完全删除了对表单内容的所有客户端限制

转义数据进行存储

现在,您已经确保数据采用预期的格式并且仅包含预期的值,接下来您需要担心将数据持久存储到存储中。

每个数据存储机制都有特定的方法来确保对数据进行正确的转义和编码。如果要构建SQL,则可接受的在查询中传递数据的方法是使用带占位符的预备语句

在PHP中使用大多数SQL数据库的一种更好的方法是PDO扩展。它遵循准备语句的常见模式,将变量绑定到语句,然后将语句和变量发送到服务器。如果您以前没有使用过PDO,那么这里有一个非常不错的面向MySQL的教程

一些SQL数据库在PHP中具有自己的特殊扩展,包括SQL ServerPostgreSQLSQLite 3。这些扩展中的每一个都具有准备好的语句支持,该语句支持的执行方式与PDO相同的prepare-bind-execute执行方式。有时,您可能需要使用这些扩展而不是PDO来支持非标准功能或行为。

MySQL还具有自己的PHP扩展。实际上,其中两个。您只想使用一个称为mysqli的版本。旧的“ mysql”扩展名已被弃用,在现代时代使用它并不安全或理智。

我个人不喜欢mysqli。它对准备好的语句执行变量绑定的方式不灵活,使用起来很痛苦。如有疑问,请改用PDO。

如果您不使用SQL数据库存储数据,请查看用于数据库接口的文档,以确定如何安全地通过数据库传递数据。

如果可能,请确保数据库以适当的格式存储数据。将数字存储在数字字段中。将日期存储在日期字段中。将钱存储在十进制字段中,而不是浮点字段中。查看数据库提供的有关如何正确存储不同数据类型的文档。

转义数据以进行演示

每次向用户显示数据时,都必须确保安全地转义了数据,除非您知道不应转义。

发出HTML时,几乎应该始终通过传递用户最初提供的任何数据htmlspecialchars。实际上,唯一不应该做的是,当您知道用户提供了HTML,并且知道已经使用白名单对其进行了清理时。

有时您需要使用PHP生成一些Javascript。Javascript没有与HTML相同的转义规则!通过PHP向Javascript提供用户提供的值的安全方法是通过json_encode

和更多

数据验证还有许多细微差别。

例如,字符集编码可能是一个巨大的陷阱。您的应用程序应遵循“ 贯穿整个UTF-8 ”中概述的做法。当您将字符串数据视为错误的字符集时,可能会发生假想的攻击。

之前我提到了浏览器调试工具。这些工具也可以用于处理Cookie数据。 Cookies应该被视为不受信任的用户输入

数据验证和转义只是Web应用程序安全性的一方面。您应该使自己了解Web应用程序攻击方法,以便针对它们建立防御。


并且在指定它时,请确保它在支持的编码列表中。
查尔斯

3
并且完全不要使用htmlentities,而是将其替换为htmlspecialchars,目的是替换<>,而不是替换其实体中的每个字符
您的常识2010年

6
只需确保不要打htmlspecialchars两次电话,因为他在“用户提交数据时”部分和“显示数据时”部分中都提到了这一点。
Savageman 2010年

2
已投票。我从许多有关SQL注入的问答中获得了最有帮助的答案。
akinuri

绝对是一个高质量的答案,其中包含许多解释和链接,以供将来的用户探索更多选择。我也得到了一个
James Walker

32

防止SQL注入的最有效方法是使用PDO。使用参数化查询,查询与数据分离,从而消除了一阶SQL注入的威胁。

就删除HTML而言,删除HTML strip_tags可能是最好的主意,因为它只会删除所有内容。 htmlentities听起来像什么,所以也可行。如果需要解析允许使用的HTML(即,您希望允许某些标签),则应使用成熟的现有解析器,例如HTML Purifier。


2
哎呀,我之所以写下那堵巨大的文字墙,是因为我没有看到任何人提到HTML Purifier,在这里,您击败了我40分钟。;)
查尔斯

3
您不应该只在输出中剥离HTML吗?IMO,您永远不应更改输入数据-您永远不知道何时需要它
Joe Phillips 2010年

11

数据库输入-如何防止SQL注入

  1. 例如,通过检查数据类型是否为整数来确保其有效,以确保其有效
    • 对于非字符串,您需要确保数据实际上是正确的类型
    • 对于字符串,您需要确保字符串在查询中用引号引起来(显然,否则它甚至无法工作)
  2. 在避免SQL注入的同时将值输入数据库(mysql_real_escape_string或参数化查询)
  3. 从数据库检索值时,请确保避免将HTML注入页面(htmlspecialchars),以确保避免跨站点脚本攻击。

您需要先转义用户输入,然后再将其插入或更新到数据库中。这是一种较旧的方法。您现在想使用参数化查询(可能来自PDO类)。

$mysql['username'] = mysql_real_escape_string($clean['username']);
$sql = "SELECT * FROM userlist WHERE username = '{$mysql['username']}'";
$result = mysql_query($sql);

来自数据库的输出-如何防止XSS(跨站点脚本)

htmlspecialchars()仅在从数据库输出数据时使用。HTML Purifier也是如此。例:

$html['username'] = htmlspecialchars($clean['username'])

最后...您的要求

我必须指出,如果将PDO对象与参数化查询一起使用(执行此操作的正确方法),那么确实没有简单的方法可以轻松地实现此目的。但是,如果您使用旧的“ mysql”方式,那么这就是您所需要的。

function filterThis($string) {
    return mysql_real_escape_string($string);
}

5

我的5美分。

这里没有人了解mysql_real_escape_string工作方式。此功能不过滤或“消毒”任何东西。
因此,您不能将此功能用作某些通用过滤器,以免注射。
只有在了解其工作原理和适用范围时,才能使用它。

对于已经写过的类似问题,我有一个答案: 在PHP中,向数据库提交字符串时,我应该使用htmlspecialchars()还是使用正则表达式来处理非法字符?
请单击以获取有关数据库侧安全性的完整说明。

至于htmlentities-Charles正确地告诉您将这些功能分开。
试想一下,您将插入一个由admin生成的数据,该数据将被允许发布HTML。您的功能会破坏它。

虽然我建议不要使用htmlentities。此功能很久以前就已过时。如果你想只更换<>"在HTML安全起见字符-使用有意开发用于这一目的的功能-一个用htmlspecialchars()之一。


1
mysql_real_escape_string转义字符串中所需的字符。它不是严格的过滤或清理,而是用引号引起来的字符串都不是(每个人都这样做,我几乎从来没有见过关于它的问题)。因此,当我们编写SQL时,没有任何东西可以消毒吗?当然不是。阻止SQL注入的是使用mysql_real_escape_string。也是用引号引起来的,但是每个人都这样做,并且如果您对所做的事情进行了测试,则最终由于此语法错误而导致SQL语法错误。真正危险的部分用处理mysql_real_escape_string
Savageman 2010年

@Savageman对不起朋友,您明白了什么。您不了解mysql_real_escape_string的工作方式。这些“所需字符”为引号。此函数或引号都不能清除任何内容。这两个东西只能一起使用。使查询字符串在语法上仅是正确的,而不是“可以安全注入”。我会得到什么样的语法错误WHERE id = 1?;)
您的常识2010年

尝试WHERE my_field = two words(不带引号)获取语法错误。您的示例很糟糕,因为它既不需要引号,也不需要转义,只需数字检查。我也不是说引号没有用。我说过每个人都使用它们,因此这并不是SQL注入问题的根源。
Savageman 2010年

1
@Savageman这样,我说过:只有在了解它的工作原理和适用范围后,才能使用它。您刚刚承认mysql_real_escape_string不适用于任何地方。至于everyone use them你可以在这里检查代码。许多人不使用带数字的引号。去搞清楚。请记住,我不在这里讨论您所说的内容,而您没有。我只是在解释基本的数据库安全规则。您最好学习而不是空谈。这里没有人提到引号或强制转换,但是m_r_e_s只是魔术。我在说什么
您的常识2010年

1
一起来,还有@Charles。作为一个新手,数据库交互...使事情对于输入和显示来说都是安全的,特殊字符,注入问题一直是非常艰巨的学习过程。阅读您的帖子和他的(以及您对其他问题的其他PHP回答,对我有很大帮助。Tx代表您的所有意见。)
James Walker

2

对于数据库插入,您只需要mysql_real_escape_string(或使用参数化查询)。通常,您不希望在保存数据之前更改数据,如果使用则会发生这种情况htmlentities。稍后当您htmlentities再次运行它以将其显示在网页上的某个位置时,这将导致乱码。

使用htmlentities当你在网页上的某处显示数据。

有点相关,如果您要在电子邮件中的某处(例如,使用联系表单)发送提交的数据,请确保从标题中将使用的任何数据中删除换行符(例如“发件人:姓名和电子邮件地址,主题等”) )

$input = preg_replace('/\s+/', ' ', $input);

如果您不这样做,垃圾邮件机器人找到您的表单并滥用它只是时间问题,我已经学到了很难的方法。



2

这取决于您使用的数据类型。一般最好的使用方法是mysqli_real_escape_string但是例如,您知道不会有HTML内容,使用strip_tags会增加额外的安全性。

您也可以删除不应该被允许的字符。


1

我总是建议使用像GUMP这样的小型验证包: https //github.com/Wixel/GUMP

围绕这样的库构建所有基本功能,并且几乎不可能忘记卫生条件。“ mysql_real_escape_string”不是进行良好过滤的最佳选择(就像“您的常识”所述),并且,如果您忘记只使用一次,则整个系统将可以通过注入和其他讨厌的攻击进行攻击。


1

对于所有在这里讨论并依赖mysql_real_escape_string的人,您需要注意该函数在PHP5上已被弃用,而在PHP7上不再存在。

恕我直言,完成此任务的最佳方法是通过使用PDO与数据库交互来使用参数化查询。检查此:https : //phpdelusions.net/pdo_examples/select

始终使用过滤器来处理用户输入。参见http://php.net/manual/es/function.filter-input.php


这实际上并不能回答问题。考虑修改您的答案以包括解决方案。
克里斯,

希望你喜欢!
Kuntur

我做。好答案!
克里斯,

我建议注意在PHP 7 mysqli_real_escape_string()中可用。
克里斯(Chris)

克里斯,您好,这里公开的解决方案参考了mysql_real_escape_string,我注意到谁从现在开始读到它在PHP7上已经不存在,并提出了使用PDO(和过滤器)而不是mysqli的替代方案。随意添加注释,根据您的建议解释解决方案。问候
Kuntur,

0

您可以在类似于以下代码的代码中使用mysql_real_escape_string()

$query = sprintf("SELECT * FROM users WHERE user='%s' AND password='%s'",
  mysql_real_escape_string($user),
  mysql_real_escape_string($password)
);

如文档所述,其目的是考虑到连接的当前字符集,在作为参数传递的字符串中转义特殊字符,以便可以安全地将其放置在mysql_query()中。该文档还添加:

如果要插入二进制数据,则必须使用此功能。

当您在HTML内容中输出字符串时,htmlentities()用于转换实体中的某些字符。


0

这是我目前正在练习的方式之一

  1. 植入csrf和salt诱惑令牌以及用户提出的请求,然后从请求中一起验证它们。请参考这里
  2. 确保不要过多依赖客户端cookie,并确保练习使用服务器端会话
  3. 当解析任何数据时,请确保仅接受数据类型和传输方法(例如POST和GET)
  4. 确保对您的webApp / App使用SSL
  5. 确保还生成时基会话请求以故意限制垃圾邮件请求。
  6. 将数据解析到服务器后,请确保验证请求是否应在所需的数据方法中进行,例如json,html等...,然后继续
  7. 使用转义类型...(例如realescapestring)来转义输入中的所有非法属性。
  8. 之后,验证用户想要的数据类型的纯净格式。
    示例:
    -电子邮件:检查输入是否采用有效的电子邮件格式
    -文本/字符串:仅检查输入仅是文本格式(字符串)
    -数字:仅检查数字格式是否允许。
    -等。请参考php门户中的php输入验证库
    -验证后,请继续使用准备好的SQL语句/ PDO。
    -完成后,请确保退出并终止连接
    - 完成操作后,请不要忘记清除输出值。

多数民众赞成在我相信足够的基本秒。它应该阻止所有来自黑客的重大攻击。

对于服务器端安全性,您可能需要在apache / htaccess中设置访问限制和机械手防护以及路由防护。除了服务器端系统的安全性外,还有很多要做的服务器端安全性。

您可以从htaccess apache sec级别学习(并获取sec的副本)(常见做法)


0
function sanitize($string,$dbmin,$dbmax){
$string = preg_replace('#[^a-z0-9]#i', '', $string); //useful for strict cleanse, alphanumeric here
$string = mysqli_real_escape_string($con, $string); //get ready for db
if(strlen($string) > $dbmax || strlen($string) < $dbmin){
    echo "reject_this"; exit();
    }
return $string;
}

0

那这个呢

$string = htmlspecialchars(strip_tags($_POST['example']));

或这个

$string = htmlentities($_POST['example'], ENT_QUOTES, 'UTF-8');
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.