1.
XSS攻击原理
XSS原称为CSS(Cross-Site Scripting),因为和层叠样式表(Cascading Style Sheets)重名,所以改称为XSS(X一般有未知的含义,还有扩展的含义)。XSS攻击涉及到三方:攻击者,用户,web server。用户是通过浏览器来访问web server上的网页,XSS攻击就是攻击者通过各种办法,在用户访问的网页中插入自己的脚本,让其在用户访问网页时在其浏览器中进行执行。攻击者通过插入的脚本的执行,来获得用户的信息,比如cookie,发送到攻击者自己的网站(跨站了)。所以称为
跨站脚本攻击
。XSS可以分为反射型XSS和持久性XSS,还有DOM Based XSS。(一句话,XSS就是在用户的浏览器中执行攻击者自己定制的脚本。)
1.1
反射型XSS
反射性XSS,也就是非持久性XSS。用户点击攻击链接,服务器解析后响应,在返回的响应内容中出现攻击者的XSS代码,被浏览器执行。一来一去,
XSS攻击脚本被web server反射回来给浏览器执行
,所以称为反射型XSS。
特点:
1> XSS攻击代码非持久性,也就是没有保存在web server中,而是出现在URL地址中;
2> 非持久性,那么攻击方式就不同了。一般是攻击者通过邮件,聊天软件等等方式发送攻击URL,然后用户点击来达到攻击的;
1.2
持久型XSS
区别就是XSS恶意代码
存储在web server中
,这样,每一个访问特定网页的用户,都会被攻击。
特点:
1> XSS攻击代码存储于web server上;
2> 攻击者,一般是通过网站的留言、评论、博客、日志等等功能(所有能够向web server输入内容的地方),将攻击代码存储到web server上的;
有时持久性XSS和反射型XSS是同时使用的,比如先通过对一个攻击url进行编码(来绕过xss filter),然后提交该web server(存储在web server中), 然后用户在浏览页面时,如果点击该url,就会触发一个XSS攻击。当然用户点击该url时,也可能会触发一个CSRF(Cross site request forgery)攻击。
1.3
DOM based XSS
基于DOM的XSS,
也就是web server不参与,仅仅涉及到浏览器的XSS
。比如根据用户的输入来动态构造一个DOM节点,如果没有对用户的输入进行过滤,那么也就导致XSS攻击的产生。过滤可以考虑采用
esapi4js
。
参见:http://www.freebuf.com/articles/web/29177.html ,
http://www.zhihu.com/question/26628342/answer/33504799
2.
XSS 存在的原因
XSS 存在的根本原因是,对URL中的参数,对用户输入提交给web server的内容,没有进行充分的过滤。如果我们能够在web程序中,对用户提交的URL中的参数,和提交的所有内容,进行充分的过滤,将所有的不合法的参数和输入内容过滤掉,那么就不会导致“在用户的浏览器中执行攻击者自己定制的脚本”。
但是,
其实充分而完全的过滤,实际上是无法实现的
。因为攻击者有各种各样的神奇的,你完全想象不到的方式来绕过服务器端的过滤,最典型的就是对URL和参数进行各种的编码,比如escape, encodeURI, encodeURIComponent, 16进制,10进制,8进制,来绕过XSS过滤。那么我们如何来防御XSS呢?
3.
XSS 攻击的防御
XSS防御的总体思路是:
对输入(和URL参数)进行过滤,对输出进行编码
。
也就是对提交的所有内容进行过滤,对url中的参数进行过滤,过滤掉会导致脚本执行的相关内容;然后对动态输出到页面的内容进行html编码,使脚本无法在浏览器中执行。
虽然对输入过滤可以被绕过,但是也还是会拦截很大一部分的XSS攻击
。
4、XSS过滤函数总结:
strip_tags( string
$str [, string
$allowable_tags ] )
该函数尝试返回给定的字符串
str 去除空字符、HTML 和 PHP 标记后的结果。
(allowable_tags
使用可选的第二个参数指定不被去除的字符列表。
例如:
strip_tags
(
$text
,
'<p><a>'
);
// 允许 <p> 和 <a>
)
htmlspecialchars
把预定义的字符 "<" (小于)和 ">" (大于)转换为 HTML 实体
。
(
预定义的字符是:
- & (和号)成为 &
- " (双引号)成为 "
- ' (单引号)成为 '
- < (小于)成为 <
- > (大于)成为 >)
htmlentities
把字符转换为 HTML 实体
5、特殊设置预防
(1)
HttpOnly防止劫取Cookie
HttpOnly最早由微软提出,至今已经成为一个标准。浏览器将禁止页面的Javascript访问带有HttpOnly属性的Cookie。目前主流浏览器都支持,HttpOnly解决是XSS后的Cookie支持攻击。
6、
过滤函数总结
(1)通用的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,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,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
; }
(2)
Discuz系统中 防止XSS漏洞攻击,过滤HTML危险标签属性的PHP函数
function
string_remove_xss(
$html
) {
preg_match_all(
"/\<([^\<]+)\>/is"
,
$html
,
$ms
);
$searchs
[] =
'<'
;
$replaces
[] =
'<'
;
$searchs
[] =
'>'
;
$replaces
[] =
'>'
;
if
(
$ms
[1]) {
$allowtags
=
'img|a|font|div|table|tbody|caption|tr|td|th|br|p|b|strong|i|u|em|span|ol|ul|li|blockquote'
;
$ms
[1] =
array_unique
(
$ms
[1]);
foreach
(
$ms
[1]
as
$value
) {
$searchs
[] =
"<"
.
$value
.
">"
;
$value
=
str_replace
(
'&'
,
'_uch_tmp_str_'
,
$value
);
$value
= string_htmlspecialchars(
$value
);
$value
=
str_replace
(
'_uch_tmp_str_'
,
'&'
,
$value
);
$value
=
str_replace
(
array
(
'\\'
,
'/*'
),
array
(
'.'
,
'/.'
),
$value
);
$skipkeys
=
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'
,
'javascript'
,
'script'
,
'eval'
,
'behaviour'
,
'expression'
,
'style'
,
'class'
);
$skipstr
= implode(
'|'
,
$skipkeys
);
$value
= preg_replace(
array
(
"/($skipstr)/i"
),
'.'
,
$value
);
if
(!preg_match(
"/^[\/|\s]?($allowtags)(\s+|$)/is"
,
$value
)) {
$value
=
''
;
}
$replaces
[] =
empty
(
$value
) ?
''
:
"<"
.
str_replace
(
'"'
,
'"'
,
$value
) . ">";
}
}
$html
=
str_replace
(
$searchs
,
$replaces
,
$html
);
return
$html
;
}
function
string_htmlspecialchars(
$string
,
$flags
= null) {
if
(
is_array
(
$string
)) {
foreach
(
$string
as
$key
=>
$val
) {
$string
[
$key
] = string_htmlspecialchars(
$val
,
$flags
);
}
}
else
{
if
(
$flags
=== null) {
$string
=
str_replace
(
array
(
'&'
,
'"'
,
'<'
,
'>'
),
array
(
'&'
,
'"'
,
'<'
,
'>'
),
$string
);
if
(
strpos
(
$string
,
'&#'
) !== false) {
$string
= preg_replace(
'/&((#(\d{3,5}|x[a-fA-F0-9]{4}));)/'
,
'&\\1'
,
$string
);
}
}
else
{
if
(PHP_VERSION <
'5.4.0'
) {
$string
= htmlspecialchars(
$string
,
$flags
);
}
else
{
if
(!defined(
'CHARSET'
) || (
strtolower
(CHARSET) ==
'utf-8'
)) {
$charset
=
'UTF-8'
;
}
else
{
$charset
=
'ISO-8859-1'
;
}
$string
= htmlspecialchars(
$string
,
$flags
,
$charset
);
}
}
}
return
$string
;
}