PHP的XSS攻击过滤函数

本文介绍了一种使用PHP函数RemoveXSS来防止XSS攻击的方法。该函数通过替换和过滤潜在的恶意字符及标签来确保输入的安全。此外还提供了详细的代码实现。

XSS攻击在最近很是流行,往往在某段代码里一不小心就会被人放上XSS攻击的代码,看到国外有人写上了函数,咱也偷偷懒,悄悄的贴上来。。。
原文如下:
The goal of this function is to be a generic function that can be used to parse almost any input and render it XSS safe. For more information on actual XSS attacks, check out http://ha.ckers.org/xss.html. Another excellent site is the XSS Database which details each attack and how it works.

PHP代码
  1. <?php  
  2. function RemoveXSS($val) {  
  3.    // remove all non-printable characters. CR(0a) and LF(0b) and TAB(9) are allowed  
  4.    // this prevents some character re-spacing such as <java/0script>  
  5.    // note that you have to handle splits with /n, /r, and /t later since they *are* allowed in some inputs  
  6.    $val = preg_replace('/([/x00-/x08,/x0b-/x0c,/x0e-/x19])/'''$val);  
  7.      
  8.    // straight replacements, the user should never need these since they're normal characters  
  9.    // this prevents like <IMG SRC=@avascript:alert('XSS')>  
  10.    $search = 'abcdefghijklmnopqrstuvwxyz'; 
  11.    $search .= 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';  
  12.    $search .= '1234567890!@#$%^&*()'; 
  13.    $search .= '~`";:?+/={}[]-_|/'//'; 
  14.    for ($i = 0; $i < strlen($search); $i++) { 
  15.       // ;? matches the ;, which is optional 
  16.       // 0{0,7} matches any padded zeros, which are optional and go up to 8 chars 
  17.     
  18.       // @ @ search for the hex values 
  19.       $val = preg_replace('/(&#[xX]0{0,8}'.dechex(ord($search[$i])).';?)/i', $search[$i], $val); // with a ; 
  20.       // @ @ 0{0,7} matches '0' zero to seven times  
  21.       $val = preg_replace('/(&#0{0,8}'.ord($search[$i]).';?)/', $search[$i], $val); // with a ; 
  22.    } 
  23.     
  24.    // now the only remaining whitespace attacks are /t, /n, and /r 
  25.    $ra1 = Array('javascript', 'vbscript', 'expression', 'applet', 'meta', 'xml', 'blink', 'link', 'style', 'script', 'embed', 'object', 'iframe', 'frame', 'frameset', 'ilayer', 'layer', 'bgsound', 'title', 'base'); 
  26.    $ra2 = Array('onabort', 'onactivate', 'onafterprint', 'onafterupdate', 'onbeforeactivate', 'onbeforecopy', 'onbeforecut', 'onbeforedeactivate', 'onbeforeeditfocus', 'onbeforepaste', 'onbeforeprint', 'onbeforeunload', 'onbeforeupdate', 'onblur', 'onbounce', 'oncellchange', 'onchange', 'onclick', 'oncontextmenu', 'oncontrolselect', 'oncopy', 'oncut', 'ondataavailable', 'ondatasetchanged', 'ondatasetcomplete', 'ondblclick', 'ondeactivate', 'ondrag', 'ondragend', 'ondragenter', 'ondragleave', 'ondragover', 'ondragstart', 'ondrop', 'onerror', 'onerrorupdate', 'onfilterchange', 'onfinish', 'onfocus', 'onfocusin', 'onfocusout', 'onhelp', 'onkeydown', 'onkeypress', 'onkeyup', 'onlayoutcomplete', 'onload', 'onlosecapture', 'onmousedown', 'onmouseenter', 'onmouseleave', 'onmousemove', 'onmouseout', 'onmouseover', 'onmouseup', 'onmousewheel', 'onmove', 'onmoveend', 'onmovestart', 'onpaste', 'onpropertychange', 'onreadystatechange', 'onreset', 'onresize', 'onresizeend', 'onresizestart', 'onrowenter', 'onrowexit', 'onrowsdelete', 'onrowsinserted', 'onscroll', 'onselect', 'onselectionchange', 'onselectstart', 'onstart', 'onstop', 'onsubmit', 'onunload'); 
  27.    $ra = array_merge($ra1, $ra2); 
  28.     
  29.    $found = true; // keep replacing as long as the previous round replaced something 
  30.    while ($found == true) { 
  31.       $val_before = $val; 
  32.       for ($i = 0; $i < sizeof($ra); $i++) { 
  33.          $pattern = '/'; 
  34.          for ($j = 0; $j < strlen($ra[$i]); $j++) { 
  35.             if ($j > 0) { 
  36.                $pattern .= '(';  
  37.                $pattern .= '(&#[xX]0{0,8}([9ab]);)'; 
  38.                $pattern .= '|';  
  39.                $pattern .= '|(&#0{0,8}([9|10|13]);)'; 
  40.                $pattern .= ')*'; 
  41.             } 
  42.             $pattern .= $ra[$i][$j]; 
  43.          } 
  44.          $pattern .= '/i';  
  45.          $replacement = substr($ra[$i], 0, 2).'<x>'.substr($ra[$i], 2); // add in <> to nerf the tag  
  46.          $val = preg_replace($pattern$replacement$val); // filter out the hex tags  
  47.          if ($val_before == $val) {  
  48.             // no replacements were made, so exit the loop  
  49.             $found = false;  
  50.          }  
  51.       }  
  52.    }  
  53.    return $val;  
  54. }   

经过这样的过滤后,应该被攻击的机会会少上很多吧?试试看呢?

<think> 我们正在讨论如何编写一个PHP函数过滤输入以防止XSS攻击。根据之前讨论,XSS防御的核心在于输出时的转义,而不是输入时。但是,用户明确要求一个用于“过滤输入”的函数,所以我们需要明确:在输入时进行全局过滤通常不是最佳实践(因为会破坏原始数据或无法适应不同场景),但有时为了快速防御或特定需求,也会在输入层进行一些清理。 不过,我们要注意:在输入时进行XSS清理(如去除或转义HTML标签)可能会导致数据损坏,因为数据可能需要在非HTML场景使用(如JSON API、文本文件导出等)。因此,更推荐的做法是在输出时根据上下文进行转义。 但是,如果用户确实需要一个在输入阶段进行XSS过滤函数,我们可以提供一个使用`htmlspecialchars`或`strip_tags`的函数。然而,请注意: 1. `strip_tags`会直接移除HTML标签,可能会破坏需要保留HTML的内容(如富文本编辑器输入)。 2. `htmlspecialchars`会将特殊字符转换为HTML实体,这样在输出到HTML时就不会被解析,但这样存储的数据已经是转义后的,如果要在非HTML场景使用,就会显示转义后的字符(如“<”而不是“<”)。 因此,我们通常不建议在输入时对整个`$_POST`做全局的XSS过滤。然而,对于一些特定的字段,如果明确知道该字段永远只作为纯文本输出(比如用户名、标题等),那么在入库前进行过滤或转义也是可行的,但要注意一致性。 下面提供两种思路的函数: 方案一:使用`htmlspecialchars`对字符串进行转义(适用于不包含HTML的纯文本字段) 方案二:使用`strip_tags`去除所有HTML标签(同样适用于纯文本字段,但会完全移除标签) 方案三:使用更高级的HTML净化器(如HTML Purifier)来允许安全的HTML(适用于富文本字段) 考虑到用户要求的是“防止XSS攻击”,且没有特别说明是否允许HTML,这里我们先提供一个通用的函数,它可以选择两种方式:转义或者去除标签。同时,我们也可以提供一个使用HTML Purifier的例子(但需要额外安装库)。 注意:我们并不推荐在输入时全局使用这些函数,而是应该根据每个字段的用途,在需要的时候调用。 以下是几个函数示例: 1. 转义函数(使用htmlspecialchars): ```php /** * 对字符串进行XSS安全转义(转换为HTML实体) * @param string $input 要转义的字符串 * @return string 转义后的字符串 */ function xss_clean_escape($input) { return htmlspecialchars($input, ENT_QUOTES | ENT_HTML5, 'UTF-8'); } ``` 2. 去除HTML标签函数: ```php /** * 去除字符串中的HTML标签(防止XSS) * @param string $input 要处理的字符串 * @return string 处理后的字符串 */ function xss_clean_strip_tags($input) { return strip_tags($input); } ``` 3. 使用HTML Purifier(需要先安装库:composer require ezyang/htmlpurifier): ```php /** * 使用HTML Purifier过滤XSS(允许安全的HTML) * @param string $input 要过滤的HTML字符串 * @return string 过滤后的安全HTML */ function xss_clean_html($input) { require_once 'path/to/HTMLPurifier.auto.php'; $config = HTMLPurifier_Config::createDefault(); $purifier = new HTMLPurifier($config); return $purifier->purify($input); } ``` 重要提示:这些函数应该在明确字段用途的情况下使用。例如: - 对于纯文本字段(如用户名、标题),可以考虑使用`xss_clean_escape`或`xss_clean_strip_tags`,但注意转义后的数据存储的是实体,如果需要在非HTML环境使用,就会显示实体。所以通常我们更推荐在输出时转义。 - 对于富文本字段(如文章内容),应该使用`xss_clean_html`(HTML Purifier)来过滤不安全的标签和属性,同时保留安全的HTML。 再次强调最佳实践:在输出时根据输出环境进行转义!例如: - 输出到HTML:使用`htmlspecialchars` - 输出到JavaScript:使用`json_encode`(注意加上JSON_HEX_TAG等选项) - 输出到URL:使用`urlencode` 另外,在输入时,我们应该做的是验证输入数据是否符合预期的格式(比如邮箱格式、数字范围等),而不是进行XSS转义。 因此,我们建议用户重新考虑:在输入时通常不需要做XSS过滤,而是应该在输出时做转义。如果一定要在输入时做,那么请谨慎使用以上函数。 总结:根据用户需求,我们提供了三个函数,但请注意它们的适用场景和潜在问题。我们更推荐在输出时转义。 相关问题: 1. 如何在PHP输出时正确防御XSS攻击? 2. 如何安全地允许用户输入HTML内容? 3. 在PHP中,输入验证和输出转义有什么区别? </think> 以下是针对PHP过滤输入、防止XSS攻击的实用函数实现与解析,结合最佳实践和安全原则: ### 一、XSS过滤核心函数 ```php /** * 防XSS过滤函数 * @param string|array $input 待过滤数据(支持字符串或数组) * @param bool $strip_tags 是否彻底移除HTML标签(默认转义保留标签) * @return mixed 安全处理后的数据 */ function xss_clean($input, $strip_tags = false) { if (empty($input)) return $input; // 数组递归处理 if (is_array($input)) { return array_map('xss_clean', $input); } // 标准化输入类型 $data = (string) $input; // 选项1:彻底移除HTML标签(适用于纯文本场景) if ($strip_tags) { $data = strip_tags($data); } // 选项2:转义特殊字符(保留原始HTML结构) else { $data = htmlspecialchars($data, ENT_QUOTES | ENT_HTML5, 'UTF-8', false); } // 额外防护:过滤高危属性(如on事件) $patterns = [ '/(on[a-z]+)\s*=\s*["\'].*?["\']/i', // 移除on事件 '/javascript\s*:/i' // 阻止JS伪协议 ]; $data = preg_replace($patterns, '', $data); return $data; } ``` ### 二、函数用法示例 #### 1. 基础输入过滤 ```php // 对POST单值过滤 $username = xss_clean($_POST['username']); // 批量过滤整个POST数组 $_POST = xss_clean($_POST); ``` #### 2. 富文本内容过滤(保留安全HTML) ```php // 使用HTMLPurifier库(需安装:composer require ezyang/htmlpurifier) function sanitize_html($html) { $config = HTMLPurifier_Config::createDefault(); $config->set('HTML.Allowed', 'p,b,a[href],img[src]'); // 定义允许的标签和属性 $purifier = new HTMLPurifier($config); return $purifier->purify($html); } // 使用示例 $articleContent = sanitize_html($_POST['content']); ``` ### 三、关键防护机制解析 1. **多层防御策略** ```mermaid graph TD A[用户输入] --> B[输入验证] B --> C[输出转义] C --> D[CSP策略] ``` 2. **数学原理保障** - 转义函数本质是将危险字符映射到安全实体: $$ \{ <, >, ", ', & \} \xrightarrow{htmlspecialchars} \{ \<, \>, \", \', \& \} $$ - 正则过滤确保高危模式被清除: $$ \text{Pattern: } \frac{1}{(on[a-z]+)\s*=\s*["\'].*?["\']} \rightarrow \emptyset $$ 3. **防御场景覆盖** | 数据类型 | 推荐方法 | 示例 | |----------------|------------------------|-----------------------| | 纯文本 | `xss_clean($input, true)` | 用户名/标题 | | 受限HTML | `sanitize_html()` | 博客评论 | | JSON API输出 | `json_encode(, JSON_HEX_TAG)` | API响应 | ### 四、增强安全措施 1. **HTTP-only Cookie**(阻止JS窃取) ```php session_set_cookie_params([ 'httponly' => true, 'samesite' => 'Strict' ]); ``` 2. **Content Security Policy (CSP)** [^2] ```php // 在响应头中设置(禁止不安全内联脚本) header("Content-Security-Policy: default-src 'self'; script-src 'self' https://trusted.cdn.com"); ``` 3. **输入格式验证** ```php // 邮箱格式验证+XSS过滤 $email = filter_var(xss_clean($_POST['email']), FILTER_VALIDATE_EMAIL); ``` > ⚠️ **重要提醒**:XSS防护的核心是**输出时的上下文相关转义**。此输入过滤函数作为额外安全层,不能替代输出防护。永远对输出到HTML的内容使用`htmlspecialchars()`[^3]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值