你一定会遇到的安全问题,前端如何预防XSS攻击?

201

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);//&lt;p&gt;&lt;b&gt;looper.zhuo&lt;/b&gt;&lt;/p&gt;

let text = "&lt;p&gt;&lt;b&gt;looper.zhuo&lt;/b&gt;&lt;/p&gt; ";
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 = '&quot;';
            break;
        case 38: // &
            escape = '&amp;';
            break;
        case 39: // '
            escape = '&#39;';
            break;
        case 60: // <
            escape = '&lt;';
            break;
        case 62: // >
            escape = '&gt;';
            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(/&quot;/g, '"')
        .replace(/&#39;/g, '\'')
        .replace(/&lt;/g, '<')
        .replace(/&gt;/g, '>')
        .replace(/&amp;/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 中,这很容易造成注入漏洞。攻击者可以利用漏洞,构造出恶意的代码指令,进而利用恶意代码危害数据安全。