Files
js-xss/lib/default.js
2014-02-13 11:18:03 +08:00

273 lines
6.3 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* 默认配置
*
* @author 老雷<leizongmin@gmail.com>
*/
// 默认白名单
var whiteList = {
h1: [],
h2: [],
h3: [],
h4: [],
h5: [],
h6: [],
hr: [],
span: [],
strong: [],
b: [],
i: [],
br: [],
p: [],
pre: [],
code: [],
a: ['target', 'href', 'title'],
img: ['src', 'alt', 'title', 'width', 'height'],
div: [],
table: ['width', 'border'],
tr: ['rowspan'],
td: ['width', 'colspan'],
th: ['width', 'colspan'],
tbody: [],
thead: [],
ul: [],
li: [],
ol: [],
dl: [],
dt: [],
em: [],
cite: [],
section:[],
header: [],
footer: [],
blockquote: [],
audio: ['autoplay', 'controls', 'loop', 'preload', 'src'],
video: ['autoplay', 'controls', 'loop', 'preload', 'src', 'height', 'width']
};
/**
* 匹配到标签时的处理方法
*
* @param {String} tag
* @param {String} html
* @param {Object} options
* @return {String}
*/
function onTag (tag, html, options) {
}
/**
* 匹配到不在白名单上的标签时的处理方法
*
* @param {String} tag
* @param {String} html
* @param {Object} options
* @return {String}
*/
function onIgnoreTag (tag, html, options) {
}
/**
* 匹配到标签属性时的处理方法
*
* @param {String} tag
* @param {String} name
* @param {String} value
* @return {String}
*/
function onTagAttr (tag, name, value) {
}
/**
* 匹配到不在白名单上的标签属性时的处理方法
*
* @param {String} tag
* @param {String} name
* @param {String} value
* @return {String}
*/
function onIgnoreTagAttr (tag, name, value) {
}
/**
* HTML转义
*
* @param {String} html
*/
function escapeHtml (html) {
return html.replace(REGEXP_LT, '&lt;').replace(REGEXP_GT, '&gt;');
}
/**
* 安全的标签属性值
*
* @param {String} tag
* @param {String} name
* @param {String} value
* @return {String}
*/
function safeAttrValue (tag, name, value) {
// 转换为友好的属性值,再做判断
value = friendlyAttrValue(value);
if (name === 'href' || name === 'src') {
// 过滤 href 和 src 属性
// javascript:
REGEXP_DEFAULT_ON_TAG_ATTR_1.lastIndex = 0;
if (REGEXP_DEFAULT_ON_TAG_ATTR_1.test(value)) {
return '#';
}
// /*注释*/
REGEXP_DEFAULT_ON_TAG_ATTR_2.lastIndex = 0;
if (REGEXP_DEFAULT_ON_TAG_ATTR_2.test(value)) {
return '#';
}
// data:
REGEXP_DEFAULT_ON_TAG_ATTR_5.lastIndex = 0;
if (REGEXP_DEFAULT_ON_TAG_ATTR_5.test(value)) {
// 允许 data: image/* 类型
REGEXP_DEFAULT_ON_TAG_ATTR_6.lastIndex = 0;
if (!REGEXP_DEFAULT_ON_TAG_ATTR_6.test(value)) {
return '#';
}
}
} else if (name === 'style') {
// 过滤 style 属性 这个xss漏洞较老了可能已经不适用
// javascript:
REGEXP_DEFAULT_ON_TAG_ATTR_3.lastIndex = 0;
if (REGEXP_DEFAULT_ON_TAG_ATTR_3.test(value)) {
return '#';
}
// /*注释*/
REGEXP_DEFAULT_ON_TAG_ATTR_4.lastIndex = 0;
if (REGEXP_DEFAULT_ON_TAG_ATTR_4.test(value)) {
return '';
}
}
// 输出时需要转义<>"
value = escapeAttrValue(value);
return value;
}
// 正则表达式
var REGEXP_LT = /</g;
var REGEXP_GT = />/g;
var REGEXP_QUOTE = /"/g;
var REGEXP_QUOTE_2 = /&quot;/g;
var REGEXP_ATTR_VALUE_1 = /&#([a-zA-Z0-9]*);?/img;
var REGEXP_ATTR_VALUE_COLON = /&colon;?/img;
var REGEXP_ATTR_VALUE_NEWLINE = /&newline;?/img;
var REGEXP_DEFAULT_ON_TAG_ATTR_1 = /\/\*|\*\//mg;
var REGEXP_DEFAULT_ON_TAG_ATTR_2 = /^[\s"'`]*((j\s*a\s*v\s*a|v\s*b|l\s*i\s*v\s*e)\s*s\s*c\s*r\s*i\s*p\s*t\s*|m\s*o\s*c\s*h\s*a)\:/ig;
var REGEXP_DEFAULT_ON_TAG_ATTR_3 = /\/\*|\*\//mg;
var REGEXP_DEFAULT_ON_TAG_ATTR_4 = /((j\s*a\s*v\s*a|v\s*b|l\s*i\s*v\s*e)\s*s\s*c\s*r\s*i\s*p\s*t\s*|m\s*o\s*c\s*h\s*a)\:/ig;
var REGEXP_DEFAULT_ON_TAG_ATTR_5 = /^[\s"'`]*(d\s*a\s*t\s*a\s*)\:/ig;
var REGEXP_DEFAULT_ON_TAG_ATTR_6 = /^[\s"'`]*(d\s*a\s*t\s*a\s*)\:\s*image\//ig;
/**
* 对双引号进行转义
*
* @param {String} str
* @return {String} str
*/
function escapeQuote (str) {
return str.replace(REGEXP_QUOTE, '&quote;');
}
/**
* 对双引号进行转义
*
* @param {String} str
* @return {String} str
*/
function unescapeQuote (str) {
return str.replace(REGEXP_QUOTE_2, '"');
}
/**
* 对html实体编码进行转义
*
* @param {String} str
* @return {String}
*/
function escapeHtmlEntities (str) {
return str.replace(REGEXP_ATTR_VALUE_1, function replaceUnicode (str, code) {
return (code[0] === 'x' || code[0] === 'X')
? String.fromCharCode(parseInt(code.substr(1), 16))
: String.fromCharCode(parseInt(code, 10));
});
}
/**
* 对html5新增的危险实体编码进行转义
*
* @param {String} str
* @return {String}
*/
function escapeDangerHtml5Entities (str) {
return str.replace(REGEXP_ATTR_VALUE_COLON, ':')
.replace(REGEXP_ATTR_VALUE_NEWLINE, ' ');
}
/**
* 清除不可见字符
*
* @param {String} str
* @return {String}
*/
function clearNonPrintableCharacter (str) {
var str2 = '';
for (var i = 0, len = str.length; i < len; i++) {
str2 += str.charCodeAt(i) < 32 ? ' ' : str.charAt(i);
}
return str2.trim();
}
/**
* 将标签的属性值转换成一般字符,便于分析
*
* @param {String} str
* @return {String}
*/
function friendlyAttrValue (str) {
str = unescapeQuote(str); // 双引号
str = escapeHtmlEntities(str); // 转换HTML实体编码
str = escapeDangerHtml5Entities(str); // 转换危险的HTML5新增实体编码
str = clearNonPrintableCharacter(str); // 清除不可见字符
return str;
}
/**
* 转义用于输出的标签属性值
*
* @param {String} str
* @return {String}
*/
function escapeAttrValue (str) {
str = escapeQuote(str);
str = escapeHtml(str);
return str;
}
exports.whiteList = whiteList;
exports.onTag = onTag;
exports.onIgnoreTag = onIgnoreTag;
exports.onTagAttr = onTagAttr;
exports.onIgnoreTagAttr = onIgnoreTagAttr;
exports.safeAttrValue = safeAttrValue;
exports.escape = escapeHtml;
exports.escapeQuote = escapeQuote;
exports.unescapeQuote = unescapeQuote;
exports.escapeHtmlEntities = escapeHtmlEntities;
exports.escapeDangerHtml5Entities = escapeDangerHtml5Entities;
exports.clearNonPrintableCharacter = clearNonPrintableCharacter;
exports.friendlyAttrValue = friendlyAttrValue;
exports.escapeAttrValue = escapeAttrValue;