1) 用于去除恶意代码的函数
<?php
function cleanInput($input) {
$search = array(
'@<script[^>]*?>.*?</script>@si', // Strip out javascript
'@<[\/\!]*?[^<>]*?>@si', // Strip out HTML tags
'@<style[^>]*?>.*?</style>@siU', // Strip style tags properly
'@<![\s\S]*?--[ \t\n\r]*>@' // Strip multi-line comments
);
$output = preg_replace($search, '', $input);
return $output;
}
?>
2) 清理函数
使用上面的函数,并添加斜杠以避免破坏数据库函数。
<?php
function sanitize($input) {
if (is_array($input)) {
foreach($input as $var=>$val) {
$output[$var] = sanitize($val);
}
}
else {
if (get_magic_quotes_gpc()) {
$input = stripslashes($input);
}
$input = cleanInput($input);
$output = mysql_real_escape_string($input);
}
return $output;
}
?>
用法
<?php
$bad_string = "Hi! <script src='http://www.evilsite.com/bad_script.js'></script> It's a good day!";
$good_string = sanitize($bad_string);
// $good_string returns "Hi! It\'s a good day!"
// Also use for getting POST/GET variables
$_POST = sanitize($_POST);
$_GET = sanitize($_GET);
?>
我使用以下自己的函数
其中“fsk_”前缀用于 WYSIWYG 编辑器变量。 完美运行。
@iMaxEst: 我认为你可能错过了重点。 准备数据只是一个旁枝末节。 清理数据可以防止代码注入攻击。
stripslashes() != sanitize()
Chris 的函数很棒! 使用正则表达式的方式很巧妙,这些代码片段一定会出现在我的库脚本中。
非常感谢!
为什么要从输入中清除 html/script 标签?
你只需要在准备输出时担心 XSS!
通过准备好的语句保护你的数据库,htmlspecialchars() 将负责输出。
答案是:性能。 清理输入而不是输出意味着你将运行一个可能消耗资源的例程一次,并且很有可能在应用程序的隔离区域(即可能是一个流量远小于公共区域的会员区域)中运行。 清理输出意味着在许多地方运行同一个可能消耗资源的例程。 随着网站的增长和流量的增加,CPU、磁盘空间和内存成为宝贵的商品,你可能会因为一遍又一遍地清理输出而浪费掉它们,而不是简单地清理问题的根源:输入。 此外,随着对更多内存、磁盘空间和 CPU 的需求,你的成本会增加,因为需要更多机器,甚至可能需要更强大的机器和磁盘。 食物可供思考,在我的情况下,我宁愿运行一个可扩展、可持续和干净的 Web 应用程序。
清理输入似乎是个好主意。 我为什么要在我的数据库中存储可能存在恶意的代码?
ASP 怎么办? 有人知道吗?
Phil,这可以用于 ASP.NET
AntiXSS 可防止跨站点脚本和 SQL 注入
http://wpl.codeplex.com/
这些代码片段通过 RSS 呈现得不太好。 所有换行符似乎都消失了。
你可以根据自己的需要修改敏感化过程。 你想让用户做的事情越多,你的网站就越不安全,反之亦然。 找到适合你的平衡点。 低流量页面不必过多担心安全性,而高流量页面则会更难处理安全性。
我个人会看看我想让用户做的事情,只允许那些事情。 然后你会禁止所有其他事情。 我的正则表达式通常以列出允许使用的东西的方式创建,而不是试图寻找我不希望用户存储在我的数据库中的东西。
我必须说,这个例子很好,我认为可以尝试一下。
这些几乎没用且过于复杂; 例如,html 的那个只匹配包含“<”的任何内容,那么为什么不显式地这样做呢? 目前这就是该表达式的全部功能,所有这些额外的内容仅仅是为了掩盖问题。 例如,javascript 的那个不起作用,我只需要添加一个空格:“<script>scripthere”,浏览器会明白我的意思,并且该脚本将执行。
我道歉,无论是谁写了这个过滤器,他们做对了也错了(错了,因为他们只是删除它,而不是转义它,对了,因为它捕获了它),我已经用手工转义的字符清理了它,这应该有效
这些几乎没用且过于复杂; 例如,html 的那个只匹配包含“<”的任何内容,那么为什么不显式地这样做呢? 目前这就是该表达式的全部功能,所有这些额外的内容仅仅是为了掩盖问题。 例如,javascript 的那个不起作用,我只需要添加一个空格:“<script>scripthere</script>”,浏览器会明白我的意思,并且该脚本将执行。
我的非常小而且方便,可以用来去除讨厌的黑客注入
<SCRIPT SRC=http://hackers.com/xss.js></SCRIPT>
这太棒了! 真是太棒了。 谢谢。
我的只是删除了括号。
您好,
我们正在寻找一位顾问,可以评估我们的网站并检查我们对这些恶意脚本的脆弱程度。
有什么推荐吗?
谢谢,
Mark
不知道顾问,但我正在阅读“pro PHP security – from application security principles to the implementation of XSS defenses”,它很好地解释了这些东西。
Mark,不知道你是否还有兴趣,但我在这方面工作了多年。 我还可以为你调查其他几个攻击领域。 只需通过 http://www.matatechconsulting.com/contact/ 联系我,了解更多详情。
Chris,为什么要使用你的正则表达式而不是 PHP 的 strip_tags(),正如 gibigbig 所建议的那样? 我不明白这样做的路线会增加什么功能。
很高兴找到。 我昨天刚在想这个,需要一个更好的 fn。
filter_var() fn 有用吗? 例如 filter_var($value, FILTER_SANITIZE_STRING)
感谢您的教程,这非常有用
最安全的方法是使用 PDO 等类对输入进行参数化,因为 PHP 是一种弱类型语言。
或者简单地将输入强制转换为你期望的类型,例如期望一个整数? 只需在输入之前加上 (int)。 类型转换是清理数字的最快操作。
嗨! 今天真是好日子!
我只是在测试这怎么工作?
我认为 satinize 函数需要几行代码,因为这两个值不会被过滤
<script src=”js/jquery-1.6.4.min.js” />
<span><script src=”js/jquery-1.6.4.min.js”</span>
感谢您的倾听
我的意思是 cleanInput 函数。 对不起
嗨,你能发布一下经过清理的数据的示例输出吗?
哈哈,“evilsite.com”
我真的很喜欢这个函数的效率。
很棒的函数,克里斯!
但是当我尝试它们时,遇到了这个错误。
警告:mysql_real_escape_string() [function.mysql-real-escape-string]:用户 ''@'localhost' 访问被拒绝(使用密码:NO)
警告:mysql_real_escape_string() [function.mysql-real-escape-string]:无法建立到服务器的连接
这是导致错误的代码行
$output = mysql_real_escape_string($input);
来自此用于剥离恶意部分的函数
您需要先建立与数据库的连接,然后再使用 mysql_real_escape_string(),否则会出错。
我使用此函数来转义输入数据库的日期
function escape($string = null)
{
if (empty($string))
{
return FALSE;
}
if (function_exists(‘mysql_real_escape_string’))
{
return mysql_real_escape_string($string);
}
else
{
return str_replace(“‘”, “\'”, $string);
}
}
至于标签和 XSS,我使用了一些更硬核的方法……当我说是硬核时,它基本上会剥离所有我不想要的东西,只允许我在脚本中定义的数组中列出的内容:-
public function input($string = null)
{
if (empty($string))
{
return ”;
}
// 剥离所有 bbcode
$string = preg_replace(‘/\[(.*?)\](.*?)\[\/?(.*?)\]/iu’, ‘\\2’, $string);
// 将允许的标记转换为 bbcode
$string = preg_replace(array_keys($this->markup), $this->markup, $string);
// 剥离所有 html 标签
$string = preg_replace(‘/\(.*?)\/iu’, ‘\\2’, $string);
// 运行 strip tags 以确保我们获取了所有内容
$string = strip_tags($string);
// 将双引号替换为单引号
$string = preg_replace(‘/(“)+/u’, “‘”, $string);
// 匹配一个或多个空格并将其替换为单个空格
$string = preg_replace(‘/( )+/u’, ‘ ‘, trim($string));
return trim($string);
}
希望这两个函数能帮助其他人。
马特 :)
$_POST = sanitize($_POST);
$_GET = sanitize($_GET);
这是我见过的最糟糕的清理方法。你永远不应该将清理后的输入存储回同一个数据流中,主要原因是脚本可以执行,并且几乎立即发生的第二次提交可以将 $_POST 的值更改为与清理后的输入不同的值。
这意味着在双重提交攻击发生后,对 $_POST 的任何后续使用都是有风险的。
将清理后的输入存储到一个安全的变量中是最佳选择。
白名单的操作也是一个好主意,通过只接受来自特定 POST 字段的输入,您还可以限制通过您可能永远不会使用的变量进行 POST 攻击的能力。
另外……Strip_tags() 存在的理由与
$search = array(
'@]*?>.*?@si', // 剥离 javascript
'@<[\/\!]*?[^]*?>@si', // 剥离 HTML 标签
'@]*?>.*?@siU', // 正确剥离样式标签
'@@' // 剥离多行注释
);
相同。该函数从字符串中删除元素,并涵盖任何可用于破坏服务器或脚本的 HTML。
我的主要观点是,即使您清理了 $_POST,也不应该信任该变量,事情会发生变化,几纳秒前存在的东西可能在您的程序开始使用这些变量时就不存在了。如果您不使用安全的方法来清理和使用数据流,就会给自己带来麻烦。
为了上帝的爱和安全的网络,请不要尝试编写自己的清理方法。使用已建立的、经过同行评审的库。
阅读 Pádraic Brady 的文章“HTML 清理:魔鬼在细节中(以及漏洞)”以了解更多详细信息:http://blog.astrumfutura.com/2010/08/html-sanitisation-the-devils-in-the-details-and-the-vulnerabilities/
在对自己的方法(我在上面发布了)进行了广泛的测试(使用 Web 漏洞扫描程序),并对标准 PHP 方法(即 strip_tags 等)进行了相同的测试后,我更喜欢坚持自己的方法。
但抛开这一切不谈,如果任何开发人员、客户或托管提供商真正关心其在线产品和服务器的安全性,他们就会有意识地部署服务器级统一安全解决方案,例如 ASL(原子安全 Linux),以防止不仅是 Web 级别的攻击,还有服务器级攻击。
我这样做。
非常有趣的话题!安全问题总是很有趣也很有用!
这是否通过转义撇号来防止 SQL 注入?我理解正确了吗?
如果您使用 mysql-real-escape-string,那么是的,它可以。但是,要使用 mysql-real-escape-string(),您必须先建立数据库连接,因此最好将任何 mysql-real-escape-string() 清理函数放在您的数据库类中。
啊,是的,我总是将连接数据传递给 mysql_* 函数,在那里我可以,而且我确实将清理函数放在了我的数据库类中。
感谢您的确认;现在我可以安心地睡着了,不用担心那些小淘气在里面放一个 '); DROP TABLE valuabledata;– 。
我在 Web 表单中使用的简单规则。
1. 表单用 3 件事加盐
a) 一个盐值,它是一个基于请求 IP 地址和服务器值组合的哈希值,因此您创建了一个加盐的字符串,该字符串被放置在一个隐藏的 <input 中
b) 一个隐藏的 <input,它总是应该为空,并且是只读和禁用的,这个名称并不重要,但我经常将这个名称设置为请求浏览器的 IP 地址,但不要过分强调它的可靠性。
c) 一个隐藏的二级 <input,它的名称是一个诱人的名称,比如“login”,它也是只读和禁用的
**我还有几种其他方法,我确实使用它们在我的检查中添加了另一层洋葱,并且还有一个表单生成了一组随机命名的字段,并且可以验证它们是否出现在 POST 表单中,以进一步确保我的服务器发出了表单。**
2. 检查发布的表单,以便存在一个“提交”元素(按钮本身是提交过程的一部分),如果不存在,则拒绝表单
3. 然后重新计算表单哈希并进行匹配,如果它不匹配,则意味着发布的 IP 地址不匹配,拒绝表单。
4. 检查空字段是否为空,它们是只读和禁用的,但对于冒充您的表单的 BOT 来说,这并不重要,因为它会用数据填充所有输入流,如果有任何数据存在,则拒绝表单。
5. 如果您的恶狼似乎已经过关,您的守卫不应该放松警惕……然后您应该迁移到一个白名单,以便您接受为表单输入的输入流
6. 只清理白名单流到一个数组中,然后您将在您的脚本中使用该数组。您应该使用内置的 PHP 函数来清理您的字符串,因为它们是二进制安全的。
7. 清理后,只使用一个清理过的数组来存储您的数据流
8. 使用内置的 mysql_real_escape_string 发布到您的数据库,因为这将确保发布的数据不会因为格式错误的字符串而破坏服务器。
最后…….
即使表单失败,我仍然会为提交发布感谢通知,并且永远不会将用户带回表单页面,而是带回网站根目录。
最重要的是,如果它闻起来不对劲,就拒绝、拒绝、拒绝……将访问者发送到网站根目录,并且像我一样做,拥有一个可疑 IP 地址的单独数据库,如果您的网站被某个特定 IP 地址淹没,只需完全丢弃表单数据并记录他们发送数据的次数,如果他们超过某个阈值,那么您可以让您的表单忽略该 IP 地址,并显示一个 404 错误页面。
我要说的是,很多顽固的程序员试图用多个 IP 地址的古老论据来嘲笑或戳穿这一点,并且哈希可以被解码,我想消除对彩虹表对 IP 地址的使用的迷思,虽然对原始 IP 地址有效,但你如何“创造性地”哈希你的盐,只有当你使用服务器端添加到加盐过程时才会有效。这取决于你作为程序员,在你试图创建无法解码的哈希的过程中使用短而可预测的盐取决于你如何做到这一点,所以如果你被黑了,那就是你的实现和可预测的本质。
例如。
$server_salt = “一个非常长的随机字符串,永远不会改变,直到你需要改变它以挫败对你的 IP 地址实现的彩虹表攻击,这是明智的。”
$hash = md5( $_SERVER[‘REMOTE_ADDR’] . $server_salt );
我实现的其他方法包括请求表单的小时,在写到表单检查中的 $hash 中使用它,可以让你拒绝过时的表单。这意味着如果一个 bot 成功模拟了你的网站,你至少可以通过你的哈希系统在时间上限制它。
然后你有选择使用会话来为表单添加一层额外的偏执。
我希望这对某些人有用,请在应得的地方给予认可。
我使用类似的东西,但没有提到它,因为这个主题更多地是关于清理数据输入。但这是我倾向于使用的方法。
从表单端,我使用了一个哈希值,它结合了较长的随机盐值以及当天、当月、当年的 MD5 哈希值,这显然意味着最终的哈希值每天都会改变,所有这些都经过 SHA1 处理以形成最终的隐藏哈希字段。
至于实际数据,我有一个单独的验证类来验证各种字段类型,例如电子邮件验证、密码验证等等。
然后,在数据进入数据库之前,我使用一个包装函数来剥离所有不在定义的白名单上的 html 和 bbcode 标记。
最后,在实际查询中,我使用修改后的 mysql real escape string 作为最终的安全级别。
但正如我之前提到的,无论如何,我也运行服务器/内核级安全套件。
哦,还有一点,我会添加到混合中,但我只倾向于用在不需要解密的数据上,例如密码,就是为每个用户生成一个随机哈希值,这个哈希值在密码验证期间使用。每个哈希值都是唯一的,完全随机的,因此使用彩虹表没有任何帮助。
这引出了一个非常重要的安全点。在任何情况下都不要以纯文本格式将用户密码保存在任何文件或数据库表中。这是一个常见的错误,也是一个大忌。
这个函数有很多问题。没有过滤 onclick 事件,也没有过滤最简单的技巧
使用 HTML Purifier 这个库可以满足你的需求。而且这些家伙在 html 清理方面有许多年的经验。相信他们就行了。
如何使用 htmlpurifier 的示例函数。
嗨,你能发布一下经过清理的数据的示例输出吗?
mysql_real_escape_string
从 php 5.5 开始被弃用 (文档)。我强烈建议将所有 mysql 内容替换为 PDO 方法,但如果你需要快速转换,请使用 mysqli_real_escape_string(注意 i?)。
嘿,
当你搜索 php 和 sanitize 输入时,这个帖子在 google 索引中排名很高。
在你进入数据库时进行消毒是一回事,另一件事应该在接受用户输入的那一刻就进行。
这样,你可以直接纠正你所处理的数据,或者要求你的客户进行修正。
因此,我想为 php 贡献一种消毒方法
php.net 文档
filter_input 和 filter_var 函数为此提供了一整套解决方案。
你可以通过 消毒过滤器 对输入进行消毒(将输入重塑为可接受的输入)。
你可以通过 验证过滤器 对输入进行验证(检查输入是否符合所需的输入类型)。
后者使你能够编写自己的正则表达式检查,如果需要,还可以将“html”逻辑转换为“开发人员”逻辑,例如将字符串(“true”)转换为布尔值(true)。
此致,
SakuraNoMae
另一种非常简单的解决方案是在进入数据库或渲染时对传入文本进行“HTML 编码”。无需清理任何内容。
很棒,谢谢,
对于那些将 html 放入数据库的人来说,sanitize(htmlspecialchars($input)) 似乎是完美的。
当你打印时,你应该使用 htmlspecialchars_decode($output);
我创建了自己的函数并使用它
它与你的类似:D
请告诉我这段代码有什么问题
这是我点击按钮时出现的错误
这是我的代码
mysql_real_escape_string 已被弃用