前言:
公司项目被安全机构检测到了存在DOM型XSS漏洞,实施又刚好把这个漏洞的修复交给了我。我并不了解这个漏洞是什么,打算面对百度编程(笑),但是查了很久都是一无所获,所以打算自己想办法解决....
在使用了easyUI的项目中,如果向单元格输入或者粘贴
<script>alert(1);</script>
然后点击另一个单元格,前端就会很老实地弹出一个框.....
假如脚本代码里面不是alert(1)而是恶意代码的话,有可能会引起很严重的问题,所以需要修复这个漏洞。
解决方案:
在日常使用中,我们可能会使用复制、输入对输入框进行操作,而代码又是在单元格失去焦点的时候运行的,那我们就可以直接通过用户在输入和复制的时候对单元格进行检测并替换内容。
代码如下:
//获取粘贴的内容,兼容IE
function getClipboard(event) {
if (window.clipboardData) {
return (window.clipboardData.getData('Text'));
}else if (event.originalEvent.clipboardData) {
return event.originalEvent.clipboardData.getData("text");
}
return "";
}
var clickElementID = null;
var receiveCode = null;
var filteringMode = 0;
$(window).on('keydown', function(e){
if(e.target.id != null && e.target.id != '' && (e.target.tagName == 'INPUT' || e.target.tagName == 'TEXTAREA')){
clickElementID = e.target.id;
receiveCode = $('#'+clickElementID).val();
var result = filterForDOMXSS(receiveCode, filteringMode);
$('#'+clickElementID).val(result.value);
if(result.status){
alertError(result.warningMessage);
}
}
});
$(window).on('keyup', function (e) {
if(e.target.id != null && e.target.id != '' && (e.target.tagName == 'INPUT' || e.target.tagName == 'TEXTAREA')){
clickElementID = e.target.id;
receiveCode = $('#'+clickElementID).val();
var result = filterForDOMXSS(receiveCode, filteringMode);
$('#'+clickElementID).val(result.value);
if(result.status){
alertError(result.warningMessage);
}
}
});
$(window).on('mouseup', function (e){
if(e.target.id != null && e.target.id != '' && (e.target.tagName == 'INPUT' || e.target.tagName == 'TEXTAREA')){
clickElementID = e.target.id;
}
});
document.addEventListener('paste', function(e){
var str = getClipboard(e);
var result = filterForDOMXSS(str, filteringMode)
$('#'+clickElementID).val(result.newValue);
if(result.status){
alertError(result.warningMessage);
}
});
我的解决办法就是绑定了全局事件,在键入以及粘贴时触发函数filterForDOMXSS。
keyup、keydown、mouseup三个事件基本都是用于获取当前单元格的id,然后触发函数后再对单元格赋予经过了过滤后的值,这样基本就可以规避这个漏洞了。
过滤函数的代码:
function filterForDOMXSS(value, modeFlag){ //value是要传入的文本,而modeFlag是使用的过滤强度
var searchStatus = false;
var crucialWord = ['javascript', 'eval', 'alert', 'onclick', 'ondblclick', 'onmousedown', 'onmouseup', 'onmouseover'
,'onmouseout', 'onmousemove', 'onkeydown', 'onkeyup', 'onkeypress', '<script+.*>', '<p+.*>', '<a+.*>'
,'<img+.*>', '<h+.*>', '<textarea+.*>', '<span+.*>', '<div+.*>', '<td+.*>', '<tr+.*>', '<th+.*>'
,'<li+.*>', '<ul+.*>', '<option+.*>', '<select+.*>', '<del+.*>', '<ol+.*>', '<label+.*>', '<button+.*>'
,'<style+.*>', '<em+.*>', '<strong+.*>', '<strike+.*>', '<b+.*>', '<s+.*>', '<u+.*>', '<sub+.*>'
,'<sup+.*>', '<small+.*>', '<dl+.*>', '<dt+.*>', '<i+.*>', '<pre+.*>', '<q+.*>', '<blockquote+.*>'
,'<br+.*>', '<code+.*>', '<cite+.*>', '\\$\\(+.*\\)'] //过滤关键词,可以后续添加
for(var i=0;i< crucialWord.length; i++){
var check = new RegExp(crucialWord[i], 'gi');
if(check.exec(value)){
searchStatus = true;
break;
}
}
if(searchStatus){
value = value.replace(/javascript/gi, '')
.replace(/eval/gi, '')
.replace(/\$\(+.*\)/g, '')
.replace(/alert/gi, '')
.replace(/onclick/gi, '')
.replace(/ondblclick/gi, '')
.replace(/onmousedown/gi, '')
.replace(/onmouseup/gi, '')
.replace(/onmouseover/gi, '')
.replace(/onmouseout/gi, '')
.replace(/onmousemove/gi, '')
.replace(/onkeydown/gi, '')
.replace(/onkeyup/gi, '')
.replace(/onkeypress/gi, '');
//仅过滤标签模式
if(modeFlag == 0){
value = value.replace(/<script+.*>/gi, '')
.replace(/<p+.*>/gi, '')
.replace(/<a+.*>/gi, '')
.replace(/<img+.*>/gi, '')
.replace(/<h+.*>/gi, '')
.replace(/<textarea+.*>/gi, '')
.replace(/<span+.*>/gi, '')
.replace(/<div+.*>/gi, '')
.replace(/<td+.*>/gi, '')
.replace(/<tr+.*>/gi, '')
.replace(/<th+.*>/gi, '')
.replace(/<li+.*>/gi, '')
.replace(/<ul+.*>/gi, '')
.replace(/<option+.*>/gi, '')
.replace(/<select+.*>/gi, '')
.replace(/<del+.*>/gi, '')
.replace(/<ol+.*>/gi, '')
.replace(/<label+.*>/gi, '')
.replace(/<button+.*>/gi, '')
.replace(/<style+.*>/gi, '')
.replace(/<em+.*>/gi, '')
.replace(/<strong+.*>/gi, '')
.replace(/<strike+.*>/gi, '')
.replace(/<b+.*>/gi, '')
.replace(/<s+.*>/gi, '')
.replace(/<u+.*>/gi, '')
.replace(/<sub+.*>/gi, '')
.replace(/<sup+.*>/gi, '')
.replace(/<small+.*>/gi, '')
.replace(/<dl+.*>/gi, '')
.replace(/<dt+.*>/gi, '')
.replace(/<i+.*>/gi, '')
.replace(/<pre+.*>/gi, '')
.replace(/<q+.*>/gi, '')
.replace(/<blockquote+.*>/gi, '')
.replace(/<br+.*>/gi, '')
.replace(/<code+.*>/gi, '')
.replace(/<cite+.*>/gi, '');
//过滤符号模式
}else if(modeFlag == 1){
value = value.replace(/</g, '')
.replace(/>/g, '')
.replace(/</gi, '')
.replace(/gt&;/gi, '')
.replace(/\$\(\)/g, '');
//全过滤模式(不推荐)
}else if(modeFlag == 2){
value = value.replace(/<\w+.*>/g, '');
}
return {'value':value, 'status': searchStatus, 'warningMessage': '系统检测到您疑似正在输入恶意代码,请注意您的行为!'};
}else {
return {'value':value, 'status': searchStatus, 'warningMessage': ''};
}
}
使用正则表达式去检测传入的文本,一旦发现了有可能会使用的恶意关键词就将searchStatus设置为true,然后去触发文本过滤,最后返回由过滤后的值、是否检测到关键词和提示信息组成的json,调用时只需要
var result = filterForDOMXSS(receiveCode, filteringMode);
$('#'+clickElementID).val(result.value);
即可。
写在最后:
忙活了好多天啦...终于通过检验啦!从无动于衷到解决了问题,基本都是靠自己想办法,也许这不是最好的解决方案,但是希望能给大家有帮助。
感谢你能阅读我的文章,有什么不足之处,敬请原谅~