Secur1ty just lik3 a girl. B0th of th3m h4ve s0me h0les. Y0u alw4ys try to f1nd the h0le, but n0t 3very tim3 y0u c4n 3xpl0it it!
查看文章 |
Anehta的水印(Watermark)机制
2008-10-23 23:46
Anehta中采用了一种同机识别的技术,我在项目中将其称为“水印”(Watermark). 看过我的录像演示的人应该有印象,效果如下: 如果一个客户端被打上了水印,那么,不管客户端删除了cookie、缓存,抑或是切换了各种不同的浏览器,其水印都不会变化。 换句话来说,就是常规的清除上网痕迹的措施,都无法清除掉anehta给客户端打上的水印。 这看起来好像跟变魔术一样,其实揭穿了很简单,我是通过Flash的shared objects来做的,也可以称作flash cookie。 其实这种技术在比较早的时候foundstone的paper中就提到过了,同时还有提到的是一个IE persistence Data,会保存在IE临时文件的一个index.dat中,也是光删cookie或缓存删不掉的。但是这个持久数据在IE7中已经不能再这么利用了,所以我这里仅仅使用了flash的cookie。 而yahoo和alibaba都使用了这种技术用在登录入口来防范钓鱼的威胁。 正常来说,flash cookie保存在系统的如下位置: C:\Documents and Settings\yourusername\Application Data\Macromedia\Flash Player\#SharedObjects\ 在这个目录下可以看到非常多的网站目录,里面都有他们的flash留下的痕迹。 ![]() 由于一般删除cookie、删除缓存等不会来清理这里,所以就给客户端打上了一个水印。 可以看到,这个位置是和windows的系统用户相关的,所以如果切换了一个windows的系统用户,也会导致水印发生改变。 由于flash是跨浏览器的,所以水印也就可以跨浏览器了,显得很神奇:换到了firefox上,还能被认出来! 然而更神奇的是,只要flash的加载位置不变,那么就算从A站切换到了B站,也照样能识别出来。 这是因为flash cookie是记录在flash加载域名下的,所以,如果有如下情景: www.a.com 上加载了 www.c.com/flash.swf ,打上了一个水印 用户之后访问 www.b.com,这个站上也加载了一个 www.c.com/flash.swf 这时候,用户的水印还是没有变。 也就是说,如果同时针对两个不同域实施了XSS攻击,加载水印后,在不同域能够识别到同一个用户。 这种同机识别技术能够给我们提供很大的便利,Anehta使用它后,能够准确识别到用户,从而进行进一步的统计分析。 这种技术不光在攻击中能用,在实际网站应用中也是非常有用的。 下面看看我是如何在Anehta中实现这一个过程的。 首先需要提到 Flash 的ExternalInterface , 它是在Flash 8中加入的支持,使用之后,就可以和外部的js通信了。具体使用方法请参考手册。 以下是Flash 中的 AS部分 首先,在flash中取shard objects so = SharedObject.getLocal("anehtaWatermark", "/"); 可以把SharedObject理解为一个数组,它的存储是以二进制文件形式存储在本地的,我在这里定义了一个名为 anehtaWatermark 的共享对象,范围是所有子目录。 对应的我的文件名就是 anehtaWatermark.sol ,和上面图片里的一致。 然后需要设定什么域能访问Flash的资源,即设定 allowDomain System.security.allowDomain(_root.domainAllowed); 这里我是从客户端传回来一个参数,这样就是动态定制了。 然后是比较核心的部分,怎样 读、写 flash cookie: // 这个是读, 获取so里的名为 watermark的字段的值 function getWatermark(){ ret = ""; ret = so.data.watermark; return ret; } // 这是写,往名为watermark的字段里写内容 function setWatermark(obj) { if (!( obj == null)){ so.data.watermark = obj; } return so.flush() } 最后再通过 ExternalInterface添加两个 call back , 和外部的js之间通信 ExternalInterface.addCallback("setWatermark", this, setWatermark); ExternalInterface.addCallback("getWatermark", this, getWatermark); 这样,当外部的js 调用这个flash的 setWatermark 和 getWatermark 的函数时,对应的就调用flash内部 AS脚本里的 这两个函数。 flash 完成后,我编译好放在了 /module/flash/anehtaWatermark.swf 里 然后再是js部分,前面说了, Flash 8才有 ExternalInterface, 所以首先要判断浏览器是否支持flash,是否高于版本8. 如果一起都满足了, 就插入flash, 然后写flash cookie,或是读flash cookie。 flash cookie设置好后,最后还是需要传递给cookie,从而让服务器识别。 整个过程逻辑有点复杂,描述如下: ![]() // Using Flash Shared Object to Store Watermark anehta.core.setWatermark = function(flashID, o){ return document.getElementById(flashID).setWatermark(o); } // Get the info from flash anehta.core.getWatermark = function(flashID){ return document.getElementById(flashID).getWatermark(); } 注意在插入flash的时候,需要设置 flash 的 allowScriptAccess 为always,不然跨域插入flash将访问不到当前域的一些DOM对象 anehta.inject.injectFlash = function(flashId, flashSrc, flashParam) { //flashParam = '?' + flashParam; document.write('<object type="application/x-shockwave-flash" data="' + flashSrc + '" width="0" height="0" id="' + flashId + '"><param name="allowScriptAccess" value="always" /> ' + '<param name="movie" value="' + flashSrc + '" />' + '<PARAM NAME=FlashVars VALUE="domainAllowed=' + flashParam + '" />' + '<param name="bgColor" value="#fff" /> </object>'); } 最后,实现上面那个图里的复杂逻辑过程是在 clx.js 里,代码如下: // 以下是正常加载clx过程 var ts = new Date(); ts = ts.getTime(); // 随机数,作为水印,只加载一次 var watermarkvalue = "FirstCatch:"+document.domain+"|"+ts; //alert(watermarkvalue); if (anehta.dom.checkCookie("anehtaWatermark") == false){ // cookie中没有水印 if (anehta.detect.flash('8') == true){ // 检测是否有flash // 插入水印flash; 加载flash需要时间 anehta.inject.injectFlash("anehtaWatermark", watermarkflash, document.domain); //alert(1); setTimeout(function(){ if ( anehta.core.getWatermark("anehtaWatermark") == undefined ){ // flash cache 中没有记录,需要设置一个 anehta.core.setWatermark("anehtaWatermark", watermarkvalue); } // 读取水印并写进cookie anehta.dom.addCookie("anehtaWatermark", anehta.core.getWatermark("anehtaWatermark")); anehta.dom.persistCookie("anehtaWatermark"); // 让水印不过期 // 记录当前cookie anehta.logger.logCookie(); }, 500); } else { // 不支持flash // 在cookie里添加水印 anehta.dom.addCookie("anehtaWatermark", watermarkvalue); anehta.dom.persistCookie("anehtaWatermark"); // 让水印不过期 anehta.logger.logCookie(); } } else { // cookie 中有水印, 不需要重复记录cookie了 //检查flashcache中是否有水印,如果没有,则把cookie里的水印写入flashcache if (anehta.detect.flash('8') == true){ // 检测是否有flash // 插入水印flash; 加载flash需要时间 anehta.inject.injectFlash("anehtaWatermark", watermarkflash, document.domain); setTimeout(function(){if ( anehta.core.getWatermark("anehtaWatermark") == undefined ){ // flash cache 中没有记录,需要设置一个 anehta.core.setWatermark("anehtaWatermark", anehta.dom.getCookie("anehtaWatermark")); } }, 500); } else { // 不支持flash // do nothing } } |