这是工作记录,慢慢的不知不觉就形成篇文章了,就发在这里吧。
-- Split --
acookie 就是段统计代码,它的原理就是发送个 GET 请求到统计服务器,并附上本地用户的浏览器类型、分辨率等其他信息。
问题
有人报告 acookie 的代码出现 XSS ,原理是利用某 GET 参数 bypass 未过滤完全的字符。这里是原先的代码摘录:
(function() {
function akrand(num) {
return Math.floor(Math.random() * num) + 1
}
var P = location.pathname;
if ((parent === self) || P.indexOf('{...}') != - 1 || P.indexOf('{...}') != - 1) {
var R = escape(document.referrer);
var id = "" + akrand(9999999) + akrand(9999999);
var title = escape(document.title);
document.write('<img src="http://foo/1.gif?acookie_load_id=' + id + '&title=' + title + '&pre=' + R
+ '¶m0={...}¶m1={...}¶m2={...}" width="0" height="0" style="display:none;" />');
}
})();
这段代码「年事已高」每次的修修补补都是治标不治本,于是干脆考虑重写这段统计代码。
目标
在开始重写之前,为将要写的代码列了几条目标:
- 流量方面的考虑,代码要尽可能的简短
- 尽可能的优化性能
- 「绿色」,不干扰页面的其他脚本
- 脚本尽可能得做到安全
思考
- 使用原先的 document.write 会在页面中加入 DOM,直接使用 new Image 更好
- 原先的判断条件很冗余,完全可以考虑个函数搞定
- 安全方面虽然可以写个简短的过滤函数,但这样代码又会很长
解决
下面是反复修改后的最终代码:
(function(){
var M = function(n) {
return Math.floor(Math.random() * n) + 1;
},
I = function() {
for (var i = 0, P = location.pathname, args = arguments, len = args.length; i < len; i++) {
if (P && P.indexOf(args[i]) !== -1) {
return 1;
}
}
return 0;
},
D = document, R = escape(D.referrer), S = screen, T = escape(D.title);
if (parent === self || I('{...}') || I('{...}')) {
try {
return new Image().src = [
"http://foo/1.gif?cache=" + M(9999999),
'&pre='+ R +'&scr='+ S.width +'x'+ S.height + '&title=' + T,
'¶m0=' + '{...}',
'¶m1=' + '{...}',
'¶m2=' + '{...}'
].join('');
} catch (e) {}
}
})();
本来想使用 ~
function() {}; 这样的闭包, 结果发现效率方面还是原先的理想 (当然不会差很多),加之可能以后阅读的同事会有困扰,还是采用原先的吧。
在这个案例中,其实返回 0 和 1 同比返回 false 和 true 是同样的道理,处于节省代码量考虑,直接使用整型值。
安全方面出于简单原则考虑,没有考虑使用转义函数,而是采用数组拼贴的方式,这样就算有注入也会造成语法报错而不能指定此段脚本,前面加 return
也是这样考虑。
后记
考虑以后的代码可维护性很重要,这个例子中代码虽然简单,当然还会有更极端的写法,不过处于以后同事的合作考虑,尽量不要写得过于的「专业」。
采用 document.write 的考虑是想直接使用脚本在页面上输出,因此在本案例中不是很适用,同时才用这一方法在安全的角度上考虑需要过滤的字符太多,因此如果了解需求(比如仅仅是生成个带参数的 URL)还是避免使用它
XSS 和 CSRF 等虽说后台处理是治本的,但如果使用不当前后台考虑不周全就算加入了对应的过滤函数,也有可能造成注入。也从另个角度考虑,有时候完善的代码也能从一定程度上加大注入的难度,尤其是 Javascript 等这种「每个人都可以看见得脚本」。
-- EOF --
直接使用new Image().src,实际使用中会有30%左右的丢失率。可能与浏览器垃圾回收机制有关。
一般是使用全局变量,window.xxx=new Image(),可将丢失率减少到10%以下
@axu 能否提供相关资料,还真没注意过这点
第一次到访,希望以后多多交流
代码严谨专业很重要,同样也要规范,可以方便地进行后续开发.
作者一看就是NB人物.呵呵.