-
Notifications
You must be signed in to change notification settings - Fork 20
Description
安全问题的本质
安全问题的本质是信任的问题。数据从高等级的信任域流向低等级的信任域,是不需要经过安全检查的;数据从低等级的信任域流向高等级的信任域,则需要经过信任边界的安全检查。
比如火车站、飞机场都会有一个必要的程序:安全检查。机场会扫描乘客的行旅箱,检查乘客身上是否携带了打火机、可燃液体等危险物品,目的主要是过滤掉有害的、危险的东西。但是当我们从候机厅出来,是不需要做检查的。
安全方案的原则
1. Secure By Default 原则
“Secure By Default”原则,也可以归纳为白名单、黑名单的思想。
如果更多地使用白名单,那么系统就会变得更安全。因为黑名单需要防范很多未知的情况,而我们不一定能考虑周全。
比如,如果网站只提供Web服务,那么正确的做法是只允许网站服务器的80和443端口对外提供服务。又比如,在网站的生产环境服务器上,应该限制随意安装软件,而需要制定统一的软件版本规范,这个规范的制定,最好是选择白名单的思想来实现。
还有一个最小权限原则,要求系统只授予主体必要的权限,而不要过渡授权,这样能有效地减少系统、网络、应用、数据库出错的机会。
比如,在Linux系统中,一种良好的操作习惯就是使用普通账号登录,在执行需要root权限的操作时,再通过sudo命令完成。
2. 纵深防御原则
纵深防御(Defense in Depth)原则包含两层含义:
首先,要在各个不同层面、不同方面实施安全方案,避免出现疏漏,不同安全方案之间需要相互配合,构成一个整体;其次,要在正确的地方做正确的事情。
第一层含义不是同一个安全方案要做两遍或多边,而是要从不同的层面、不同的角度对系统做出整体的解决方案。
第二城含义,要把防御方案放到最适合的地方去解决。比如XSS防御技术里,不应该粗暴地把尖括号过滤掉,而是在瓶装HTML时处理特殊字符。
3. 数据与代码分离原则
这一原则广泛适用于各种由于“注入”而引发安全问题的场景。
在Web安全中,由“注入”引起的问题比比皆是,如SXX、SQL Injection、CRLF Injection、X-Path Injection等。此类问题均可以根据“数据与代码分离原则”设计出真正安全的解决方案,因为这个原则抓住了漏洞形成的本质原因。
4. 不可预测性原则
不可预测型(Unpredictable),能有效地对抗基于篡改、伪造的攻击,可以巧妙地用在一些敏感数据上。
比如在CSRF的防御技术中,通常会使用一个token来进行有效防御。这个token能成功防御CSRF,就是因为攻击者在实施CSRF攻击的过程中,是无法提前预知这个token值的,因此要求token足够复杂,不能被攻击者猜测到。
客户端脚本安全
1. 浏览器安全
浏览器作为一个客户端,是需要自带一些安全功能,这不仅能让它在浏览器市场中脱颖而出,还可以保证用户的部分信息安全。
同源策略
同源策略(Same Origin Policy)是第一种约定,它是浏览器最核心也是最基本的安全功能,如果缺少了同源策略,则浏览器的正常功能可能会受到影响。可以说Web是构建在同源策略的基础之上的,浏览器知识针对同源策略的一种实现。
有关同源策略和跨域请求可以看我的另一篇文章:《同源策略与跨域请求》
浏览器沙箱
对于浏览器来说,采用Sandbox(沙箱)技术,可以让不受信任的网页代码、JavaScript代码运行在一个受到限制的环境中,从而保护本地桌面系统的安全。
现代浏览器的渲染引擎由Sandbox隔离,网页代码要与浏览器内核进程通信、与操作系统通信都需要通过IPC channel,在其中会进行一些安全检查。
恶意网址拦截
为了保护用户安全,浏览器厂商纷纷推出了各自的拦截恶意网址功能。大部分浏览器的拦截恶意网址的功能都是基于“黑名单”的。
现在的浏览器多是与专业的安全厂商展开合作,由安全厂商或机构提供恶意网址黑名单。
除了恶意网址黑名单拦截功能外,主流浏览器都开始支持EV SSL证书,以增强对安全网站的识别,这部分功能是基于“白名单”的。
其它安全策略
为了在安全领域获得竞争力,微软率先在IE8中推出了XSS Filter功能,用以对抗反射型XSS。
Firefox 4推出了Content Security Policy(CSP),CSP 的主要目标是减少和报告 XSS 攻击,通过指定有效域——即浏览器认可的可执行脚本的有效来源——使服务器管理者有能力减少或消除XSS攻击所依赖的载体。一个CSP兼容的浏览器将会仅执行从白名单域获取到的脚本文件,忽略所有的其他脚本 (包括内联脚本和HTML的事件处理属性)。
CSP可以放在响应头里,也可以放在HTML的meta标签里。
X-Frame-Options,主要用于预防点击劫持。
Cookie的samesite属性,主要用于预防CSRF。
2. 跨站脚本攻击(XSS)
跨站脚本攻击,通常指黑客通过“HTML注入”篡改了网页,插入了恶意的脚本,从而在用户浏览网页时,控制用户浏览器的一种攻击。
XSS的关键是脚本而是跨站,它的类型有:
- 反射型XSS,把用户输入的数据“反射”给浏览器
- 存储型XSS,把用户输入的数据“存储”在服务器端
- DOM Based XSS,从效果上来说也是反射型XSS,通过JS修改页面的DOM节点形成的XSS
反射型XSS,一般要求攻击者诱使用户点击一个包含XSS代码的URL连接,服务器在渲染页面的时候会使用URL的部分数据,从而导致攻击发生。而存储型XSS,只需让用户查看一个正常的URL链接,但是网页里有XSS漏洞就会导致用户被攻击,比如论坛等有大量用户交互留言的场景。
XSS攻击场景:
- Cookie劫持,这可以通过cookie的httpOnly来防止攻击
- 构造Get和Post请求,如果cookie无法被劫持,也可以发起攻击,因为浏览器在发送同域名请求时会带上这个域名保存的cookie
- XSS 蠕虫攻击,一般来说,用户之间发生交互行为的页面,如果存在存储型XSS,则比较容易发起XSS 蠕虫攻击
正确预防XSS,需要针对特定的场景:
- 在HTML元素标签中填入用户数据,使用HTMLEncode
- 在HTML元素属性中填入用户数据,使用HTMLEncode
- 在<script>标签中填入用户数据,使用JavaScriptEncode
- 在事件中填入用户数据,使用JavaScriptEncode
- 在CSS中填入用户数据,在CSS和style、style attribute中形成XSS的方式非常多样化,尽可能禁止在CSS中填入用户数据
- 在地址中填入用户数据,使用URLEncode
- 富文本,使用白名单(避免使用黑名单)过滤标签,并且禁止自定义的CSS与style
开源社区有很多XSS解决方案,如果你使用的是JS,推荐下面npm上的这个库:xss,它可以按照白名单的方式过滤标签和属性,对于服务器渲染方案还是不错的。
鉴于现在大多数网站都使用react、vue之类的浏览器渲染方案,可以使用比较简单转义方案:
const htmlEscapeMap = {
"&": "&",
"<": "<",
">": ">",
'"': """,
"'": "'",
"`": "`",
};3. 跨站点请求伪造(CSRF)
CSRF主要是依托浏览器发送cookie的策略进行的,不同的浏览器可能策略不太一样。当之前请求过一个域名并保存了cookie,那么下次再访问这个域名的资源就会带上保存的cookie。
CSRF攻击之所以可以成功,本质是因为重要操作的所有参数都是可以被攻击者猜测到的。
CSRF的防御策略:
- 验证码,强制用户与应用进行交互,只能作为防御CSRF的一种辅助手段,因为不能给所有的操作都加上验证码
- Referer Check,检查资源来源,也不能作为CSRF的主要手段,因为有些浏览器出于隐式保护的考虑,限制了Referer的发送
- Anti CSRF Token,token是随机生成的,用以校验本次请求的合法性,它会传递给客户端(作为表单的一部分),然后保留一份在session(或者作为cookie发送给客户端),当提交表单的时候会带上这个token,服务器会校验session中的token(或cookie中的token)和表单里的token是否一致
CSRF的Token仅仅用于对抗CSRF攻击,当网站还同时存在XSS漏洞时,这个方案就会变得无效,因为XSS可以模拟客户端浏览器执行任意操作。在XSS攻击下,攻击者完成可以请求页面后,读出页面内容的token值,然后再构造出一个合法的请求。这个过程可以称之为XSRF,和CSRF区分。
现代浏览器的cookie有samesite属性,对CSRF防御有些许帮助,samesite有如下属性:
- Strict,仅允许第一方请求携带 Cookie,即浏览器将只发送相同站点请求的 Cookie,即当前网页 URL 与请求目标 URL 完全一致
- Lax,允许部分第三方请求携带 Cookie,同站发送和第三方get请求会发送
- None,无论是否跨站都会发送 Cookie
4. 点击劫持(ClickJacking)
点击劫持是一种视觉上的欺骗手段。攻击者使用一个透明的、不可见的iframe,覆盖在一个网页上,然后诱使用户在该网页上进行操作,此时用户将在不知情的情况下点击透明的iframe页面。通过调整iframe页面的位置,可以诱使用户恰好点击在iframe页面的一些功能性按钮上。
在CSRF攻击的过程中,如果出现用户交互的页面,则攻击可能会无法顺利完成。与之相反,点击劫持没有这个顾虑,它利用的就是与用户产生交互的页面。
攻击方式:
- 图片覆盖攻击,css(style) + 图片点击
- 拖拽劫持与数据窃取,iframe + 拖拽事件的
evet.dataTransfer.getData('Text')
ClickJacking的防御策略:
- frame busting,通过JS代码进制iframe的嵌套,但是因为是JS写的,控制能力并不是特别强,有很多方法可以绕过它
- X-Frame-Options,是一个http响应头,指示浏览器是否允许一个页面在
<frame>, <iframe>, <embed>,<object>中展现的标记
X-Frame-Options 是个已广泛支持的非官方标准,可以和 CSP 结合使用。 X-Frame-Options 有三个可能的值:
- deny,表示该页面不允许在 frame 中展示,即便是在相同域名的页面中嵌套也不允许
- sameorigin,表示该页面可以在相同域名页面的 frame 中展示
- allow-from {uri},表示该页面可以在指定来源的 frame 中展示
服务器端应用安全
如果你有使用Node作为服务器的经历,那么要小心避开一些安全漏洞。
注入攻击
注入攻击的本质,是把用户输入的数据当做代码执行,违背了“数据与代码分离原则”。这里有两个关键条件,第一个是用户能够控制输入;第二个是原本程序要执行的代码,拼接了用户输入的数据。
1. SQL注入
SQL注入就是在原来的执行语义上添加了额外的语义,导致攻击的发生。
发现SQL注入漏洞的方法有:
- 盲注(Blind Injection),使用假命题比如
and 1=2并观察返回值来判断是否有攻击漏洞 - Timing Attack,通过执行测试函数性能的函数的时间长短来判断是否有攻击漏洞
攻击方式有:
- 常见的攻击方式,就是执行额外的语句、系统命令
- 攻击存储过程,存储过程本身也可能存在注入漏洞的
- 编码问题,基于字符集的攻击
- SQL Column Truncation,利用非严格模式下超长值发生截断但是会被插入的漏洞
注入的防御策略:
- 使用预编译语句,绑定变量
- 使用安全的存储过程,避免在存储过程内使用动态的SQL语句,如果无法避免,应该使用严格的输入过滤或者使编码函数来处理用户的输入数据
- 检查数据类型
- 使用安全函数,很多数据库厂商都提供了安全的编码函数
- 编码问题的解决方案,统一数据库、操作系统、Web应用所使用的字符集,比如UTF-8
2. XML注入
XML注入和HTML注入是相似的,对语言自身的保留字符转移即可。
如:'、"、/、&、<、>
3. 代码注入
如果使用一些可以执行用户输入作为代码的函数,需要谨慎。或者尽量避免这样做。
4. CRLF注入
CR是Carriage Return(ASCII 13, \r),LF是Line Feed(ASCII 10, \n)。
凡是使用CRLF作为分隔符的地方都可能存在CRLF注入。最典型的就是HTTP头部。
对抗CRLF的方法非常简单,只需要处理好"\r"、"\n"两个保留字符即可。
参考
《白帽子讲Web安全》
Web 安全专题
