XSS 的本质
恶意代码未经过滤,与网站正常的代码混在一起;浏览器无法分辨哪些脚本是可信的,导致恶意脚本被执行。
- 通常页面中包含的用户输入内容都在固定的容器或者属性内,以文本的形式展示。
- 攻击者利用这些页面的用户输入片段,拼接特殊格式的字符串,突破原有位置的限制,形成了代码片段。
- 攻击者通过在目标网站上注入脚本,使之在用户的浏览器上运行,从而引发潜在风险。
如何预防xss攻击
- 通过 HTML 转义,可以防止 XSS 攻击
- 白名单的方法
html里的innerText和innerHTML其实可以实现相互转译
转译(把标签转译成特殊字符)
function htmlEncode(html) {
let output=''
if(html){
let temp = document.createElement("div");
temp.innerText = html
output = temp.innerHTML;
}
return output;
}
反转译(把特殊字符转移成标签)
function htmlDecode(text) {
let output=''
if(text){
let temp = document.createElement("div");
temp.innerHTML = text;
output = temp.innerText; // 返回div元素下的所有文本节点值,这样避免了text里面包含的标签,因为包含的标签已经被上一步解析过了,innerText不会返回解析过的元素节点。
}
return output;
}
测试
let htmlText = "<p><b>looper.zhuo</b></p> ";
let encodeText = htmlEncode(htmlText);
console.log(encodeText);//<p><b>looper.zhuo</b></p>
let text = "<p><b>looper.zhuo</b></p> ";
let decodeText = htmlDecode(text);
console.log(decodeText);//<p><b>looper.zhuo</b></p> ;
这样就可以解决一切转译出现的问题哦
nodejs中间层如何做好xss安全
/**
* 过滤字符串中的特殊标签属性
* @param {String} str 源字符串
* @return {String} 过滤后字符串
*/
filterString = function(str) {
const whiteList = xss.whiteList;
const filter = new xss.FilterXSS({ whiteList });
return filter.process(str);
};
/**
* 参数过滤,对敏感字符进行转义
*
* @param {string} originStr 处理前字符串
* @return {string}
* @public
*/
function escapeHtml(originStr) {
const matchHtmlRegExp = /["'&<>]/;
let str = '' + originStr;
let match = matchHtmlRegExp.exec(str);
if (!match) {
return str;
}
let escape;
let html = '';
let index = 0;
let lastIndex = 0;
for (index = match.index; index < str.length; index++) {
switch (str.charCodeAt(index)) {
case 34: // "
escape = '"';
break;
case 38: // &
escape = '&';
break;
case 39: // '
escape = ''';
break;
case 60: // <
escape = '<';
break;
case 62: // >
escape = '>';
break;
default:
continue;
}
if (lastIndex !== index) {
html += str.substring(lastIndex, index);
}
lastIndex = index + 1;
html += escape;
}
return lastIndex !== index
? html + str.substring(lastIndex, index)
: html;
};
/**
* 参数还原,反转义还原敏感字符
*
* @param {String} html
* @return {String}
*/
function unEscapeHtml(html) {
return String(html)
.replace(/"/g, '"')
.replace(/'/g, '\'')
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/&/g, '&');
};
使用方法
let sendData = {};
const content = utilMod.unEscapeHtml(req.body.content); // 防止直接通过接口传入转码后的字符串,无法白名单过滤,先进行反转义
const filterContent = toolMod.filterString(content); // 白名单过滤
sendData.content = utilMod.escapeHtml(filterContent); // 转码
XSS注入的方法:
- 在 HTML 中内嵌的文本中,恶意内容以 script 标签形成注入。
- 在内联的 JavaScript 中,拼接的数据突破了原本的限制(字符串,变量,方法名等)。
- 在标签属性中,恶意内容包含引号,从而突破属性值的限制,注入其他属性或者标签。
- 在标签的 href、src 等属性中,包含 javascript: 等可执行代码。
- 在 onload、onerror、onclick 等事件中,注入不受控制代码。
- 在 style 属性和标签中,包含类似 background-image:url("javascript:..."); 的代码(新版本浏览器已经可以防范)。
- 在 style 属性和标签中,包含类似 expression(...) 的 CSS 表达式代码(新版本浏览器已经可以防范)。
HTML 转义是非常复杂的,在不同的情况下要采用不同的转义规则。如果采用了错误的转义规则,很有可能会埋下 XSS 隐患。
应当尽量避免自己写转义库,而应当采用成熟的、业界通用的转义库。
总之,如果开发者没有将用户输入的文本进行合适的过滤,就贸然插入到 HTML 中,这很容易造成注入漏洞。攻击者可以利用漏洞,构造出恶意的代码指令,进而利用恶意代码危害数据安全。