XSS簡單防禦模塊

聲明:以下僅爲簡單嘗試模塊,未經測試;如要使用,還請詳細斟酌。需要結合jquery使用。

模塊代碼

// ### protect.js ###
/**
 * @desc 防止XSS攻擊
 * @date 2020-02-25
 * @author Chang-Jin
 */
function Protect() {
    var hex = new Array('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f');

    // charCode轉換map
    var charCodeMap = {
        10: "<br/>", // newline
        32: "&nbsp", // space
        34: "&quot", // "
        38: "&amp;", // &
        39: "&#x27;", // '
        47: "&#x2F;", // /
        60: "&lt;", // <
        62: "&gt;",
        198: "&AElig;",
        193: "&Aacute;",
        194: "&Acirc;",
        192: "&Agrave;",
        197: "&Aring;",
        195: "&Atilde;",
        196: "&Auml;",
        199: "&Ccedil;",
        208: "&ETH;",
        201: "&Eacute;",
        202: "&Ecirc;",
        200: "&Egrave;",
        203: "&Euml;",
        205: "&Iacute;",
        206: "&Icirc;",
        204: "&Igrave;",
        207: "&Iuml;",
        209: "&Ntilde;",
        211: "&Oacute;",
        212: "&Ocirc;",
        210: "&Ograve;",
        216: "&Oslash;",
        213: "&Otilde;",
        214: "&Ouml;",
        222: "&THORN;",
        218: "&Uacute;",
        219: "&Ucirc;",
        217: "&Ugrave;",
        220: "&Uuml;",
        221: "&Yacute;",
        225: "&aacute;",
        226: "&acirc;",
        230: "&aelig;",
        224: "&agrave;",
        229: "&aring;",
        227: "&atilde;",
        228: "&auml;",
        231: "&ccedil;",
        233: "&eacute;",
        234: "&ecirc;",
        232: "&egrave;",
        240: "&eth;",
        235: "&euml;",
        237: "&iacute;",
        238: "&icirc;",
        236: "&igrave;",
        239: "&iuml;",
        241: "&ntilde;",
        243: "&oacute;",
        244: "&ocirc;",
        242: "&ograve;",
        248: "&oslash;",
        245: "&otilde;",
        246: "&ouml;",
        223: "&szlig;",
        254: "&thorn;",
        250: "&uacute;",
        251: "&ucirc;",
        249: "&ugrave;",
        252: "&uuml;",
        253: "&yacute;",
        255: "&yuml;",
        162: "&cent;",
        '\r': "\r"
    }
    var thecharcharMap = {
        '\n': "\\n", // newline
        '\r': "\\r", // Carriage return
        '\'': "\\'",
        '"': "\\\"",
        '\&': "\\&",
        '\\': "\\\\",
        '\t': "\\t",
        '\b': "\\b",
        '\f': "\\f",
        '/': "\\x2F",
        '<': "\\x3C",
        '>': "\\x3E"
    }

    if (!Protect.prototype.escapeCharx) {
        Protect.prototype.escapeCharx = function(original) {
            var thechar = original.charCodeAt(0);

            if (charCodeMap[thechar]) {
                return charCodeMap[thechar];
            } else {
                if (thechar > 127) {
                    var c = thechar;
                    var a4 = c % 16;
                    c = Math.floor(c / 16);
                    var a3 = c % 16;
                    c = Math.floor(c / 16);
                    var a2 = c % 16;
                    c = Math.floor(c / 16);
                    var a1 = c % 16;

                    return "&#x" + hex[a1] + hex[a2] + hex[a3] + hex[a4] + ";";
                }

                return original;
            }
        }

        Protect.prototype.changeTo16Hex = function(charCode) {
            return "\\x" + charCode.charCodeAt(0).toString(16);
        }

        Protect.prototype.encodeCharx = function(original) {
            var thecharchar = original.charAt(0);
            var thechar = original.charCodeAt(0);

            if (thecharcharMap[thecharchar]) {
                return thecharcharMap[thecharchar];
            } else {
                if (thechar > 47 && thechar < 58) { // 數字
                    return original;
                }

                if (thechar > 64 && thechar < 91) { // 大寫字母
                    return original;
                }

                if (thechar > 96 && thechar < 123) { // 小寫字母
                    return original;
                }

                if (thechar > 127) { // 大於127用unicode
                    var c = thechar;
                    var a4 = c % 16;
                    c = Math.floor(c / 16);
                    var a3 = c % 16;
                    c = Math.floor(c / 16);
                    var a2 = c % 16;
                    c = Math.floor(c / 16);
                    var a1 = c % 16;
                    return "\\u" + hex[a1] + hex[a2] + hex[a3] + hex[a4] + "";
                } else {
                    return this.changeTo16Hex(original);
                }

            }
        }

        Protect.prototype.htmlEncode = function(str) { // HTML轉碼

            var preescape = str;
            var escaped = "";

            for (var i = 0; i < preescape.length; i++) {
                var p = preescape.charAt(i);
                escaped = escaped + this.escapeCharx(p);
            }

            return escaped;
        }

        // 使用“\”對特殊字符進行轉義,除數字字母之外,小於127使用16進制“\xHH”的方式進行編碼,大於用unicode(非常嚴格模式)。
        Protect.prototype.javaScriptEncode = function(str) {
            var preescape = str;
            var escaped = "";
            var i = 0;
            for (i = 0; i < preescape.length; i++) {
                escaped = escaped + this.encodeCharx(preescape.charAt(i));
            }
            return escaped;
        }

        // 聯合使用HTML和JavaScript編碼
        Protect.prototype.encode = function(str) {
            return this.htmlEncode(this.javaScriptEncode(str));
        }

        /**
         * 對被保護對象的val值進行編碼
         * 防止xss攻擊
         *
         * @param {*} protectedSelector 被保護對象的選擇器
         * @param {*} eventType 被保護對象在什麼事件類型下觸發保護
         */
        Protect.prototype.bind = function(protectedSelector, eventType) {
            var self = this;
            eventType = eventType || 'change'; // 默認綁定change事件

            $(document).on(eventType + '.xss', protectedSelector, function() {
                var $this = $(this);
                var val = $this.val();
                var tagReg = /<[\S\s]*>/;

                if (val.match(tagReg)) {
                    val = self.htmlEncode(val);
                } else {
                    val = self.javaScriptEncode(val);
                }

                $this.val(val);
            })

            return this; // 支持鏈式調用
        }

        // 解除保護
        Protect.prototype.unbind = function(protectedSelector, eventType) {
            eventType = eventType || 'change'; // 默認綁定change事件

            $(document).off(eventType + '.xss', protectedSelector);

            return this; // 支持鏈式調用
        }
    }
}

使用方法

  1. 保護某個元素。
    初始化一個protect對象,調用bind方法,傳入需要綁定dom的選擇器;
    默認會綁定change事件,如需綁定其他事件,傳入bind的第二個參數
var protect = new Protect()

protect.bind('input');
  1. 解除對某個元素的綁定。
protect.unbind(selector, eventType);
  1. 編碼某個值。
var protect = new Protect();

protect.htmlEncode(val); // 編碼html
protect.javaScriptEncode(val); // 編碼js
protect.encode(val); // 同時調用兩個
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章