XSS***全称跨站脚本***,是为不和层叠样式表(Cascading Style Sheets, CSS)的缩写混淆,故将跨站脚本***缩写为XSS,XSS是一种在web应用中的计算机安全漏洞,它允许恶意web用户将代码植入到提供给其它用户使用的页面中。
一、XSS ***
常见的XSS***有三种:反射型、DOM-based 型、存储型。其中反射型、DOM-based 型可以归类为非持久型XSS***,存储型归类为持久型XSS***。
1、反射型
反射型XSS一般是***者通过特定手法(如电子邮件),诱使用户去访问一个包含恶意代码的URL,当受害者点击这些专门设计的链接的时候,恶意代码会直接在受害者主机上的浏览器执行。
对于访问者而言是一次性的,具体表现在我们把我们的恶意脚本通过URL 的方式传递给了服务器,而服务器则只是不加处理的把脚本“反射”回访问者的浏览器而使访问者的浏览器执行相应的脚本。反射型XSS的触发有后端的参与,要避免反射性XSS,必须需要后端的协调,后端解析前端的数据时首先做相关的字串检测和转义处理。
此类XSS通常出现在网站的搜索栏、用户登录口等地方,常用来窃取客户端Cookies 或进行钓鱼欺骗。
2、DOM-based 型
客户端的脚本程序可以动态地检查和修改页面内容,而不依赖于服务器端的数据。例如客户端如从URL 中提取数据并在本地执行,如果用户在客户端输入的数据包含了恶意的JavaScript 脚本,而这些脚本没有经过适当的过滤和消毒,那么应用程序就可能受到DOM-based XSS***。需要特别注意以下的用户输入document.URL、 location.hash、 location.search、 document.referrer 等。
3、存储型
***者事先将恶意代码上传或储存到漏洞服务器中,只要受害者浏览包含此恶意代码的页面就会执行恶意代码。这就意味着只要访问了这个页面的访客,都有可能会执行这段恶意脚本,因此储存型XSS的危害会更大。
存储型XSS一般出现在网站留言、评论、博客日志等交互处,恶意脚本存储到客户端或者服务端的数据库中。
二、XSS ***的危害
XSS可以导致:
(1)***劫持访问;
(2)盗用cookie 实现无密码登录;
(3)配合csrf ***完成恶意请求;
(4)使用js 或css 破坏页面正常的结构与样式等。
三、XSS ***防御方法
1、XSS 防御之HTML 编码
应用范围:将不可信数据放入到HTML 标签内(例如div、span等)的时候进行HTML编码。
编码规则:将& < > " ' / 转义为实体字符(或者十进制、十六进制)。
示例代码:
function encodeForHTML ( str , kwargs ){
return ( '' + str )
. replace ( /&/ g , '&' )
. replace ( /</ g , '<' ) // DEC=> < HEX=> < Entity=> <
. replace ( />/ g , '>' )
. replace ( /"/ g , '"' )
. replace ( /'/ g , ''') // ' 不推荐,因为它不在 HTML 规范中
. replace ( /// g , '/' );
};
HTML 有三种编码表现方式:十进制、十六进制、命名实体。例如小于号(<)可以编码为"十进制> <", "十六进制=> <", "命名实体=> <" 三种方式。对于单引号(')由于实体字符编码方式不在HTML 规范中,所以此处使用了十六进制编码。
2、XSS 防御之HTML Attribute 编码
应用范围:将不可信数据放入HTML 属性时(不含src、href、style 和事件处理属性),进行HTML Attribute 编码。
编码规则:除了字母数字字符以外,使用HH;(或者可用的命名实体)格式来转义ASCII值小于256所有的字符。
示例代码:
function encodeForHTMLAttibute ( str , kwargs ){
let encoded = '' ;
for ( let i = 0 ; i < str . length ; i ++) {
let ch = hex = str [ i ];
if (!/[ A - Za - z0 - 9 ]/. test ( str [ i ]) && str . charCodeAt ( i ) < 256 ) {
hex = '&#x' + ch . charCodeAt ( 0 ). toString ( 16 ) + ';' ;
}
encoded += hex ;
}
return encoded ;
};
3、XSS 防御之JavaScript 编码
作用范围:将不可信数据放入事件处理属性、JavaScirpt值时进行JavaScript 编码。
编码规则:除字母数字字符外,请使用xHH格式转义ASCII码小于256的所有字符。
示例代码:
function encodeForJavascript ( str , kwargs ) {
let encoded = '' ;
for ( let i = 0 ; i < str . length ; i ++) {
let cc = hex = str [ i ];
if (!/[ A - Za - z0 - 9 ]/. test ( str [ i ]) && str . charCodeAt ( i ) < 256 ) {
hex = '\x' + cc . charCodeAt (). toString ( 16 );
}
encoded += hex ;
}
return encoded ;
};
4、XSS 防御之URL 编码
作用范围:将不可信数据作为URL 参数值时需要对参数进行URL 编码。
编码规则:将参数值进行encodeURIComponent 编码。
示例代码:
function encodeForURL ( str , kwargs ){
return encodeURIComponent ( str );
};
5、XSS 防御之CSS 编码
作用范围:将不可信数据作为CSS 时进行CSS 编码。
编码规则:除了字母数字字符以外,使用XXXXXX格式来转义ASCII值小于256的所有字符。
示例代码:
function encodeForCSS ( attr , str , kwargs ){
let encoded = '' ;
for ( let i = 0 ; i < str . length ; i ++) {
let ch = str . charAt ( i );
if (! ch . match ( /[a-zA-Z0-9]/ ) {
let hex = str . charCodeAt ( i ). toString ( 16 );
let pad = '000000' . substr (( hex . length ));
encoded += '\' + pad + hex ;
} else {
encoded += ch ;
}
}
return encoded ;
};
四、结语
在任何时候用户的输入都是不可信的。对于HTTP 参数,理论上都要进行验证,例如某个字段是枚举类型,其就不应该出现枚举以为的值;对于不可信数据的输出要进行相应的编码;此外 httpOnly、 CSP、 X-XSS-Protection、 Secure Cookie等也可以起到有效的防护。
XSS漏洞有时比较难发现,所幸当下React、Vue等框架都从框架层面引入了XSS防御机制,一定程度上解放了我们的双手。
但是作为开发人员依然要了解XSS基本知识、于细节处避免制造XSS漏洞。框架是辅助,我们仍需以人为本,规范开发习惯,提高Web 前端安全意识。