前端知识笔记 - 安全
前端知识笔记 - 安全
本文章总结 https://github.com/thedaviddias/Front-End-Checklist
内部,非原创性内容。
https
- 尽量使用 Https,个人网站可使用 Let's Encrypt
- 使用 Strict-Transport-Security 返回头。解决的问题是https://www.owasp.org/index.php/HTTP_Strict_Transport_Security_Cheat_Sheet:
- 使用书签或手动输入网址访问 http 网址
- https 页面中不经意使用了 https 的资源(链接、内容等)
- 中间攻击人让用户信任证书,设置返回头后,浏览器不接受不受信任的证书
设置方式如下:
max-age 表示在该有效时间内应该使用 https 访问,includeSubDomains 表示该域名的所有子域名适用于该策略,preload 表示预加载时不会使用 http 链接。
三个参数必须都设置才更安全。
示例:
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
Secure Server Design 安全服务设计
地址:https://www.owasp.org/index.php/Transport_Layer_Protection_Cheat_Sheet
-
在任意地方使用 TLS 或其他更强的加密服务 (Use TLS or Other Strong Transport Everywhere)
内部网络也可被黑客攻击到,登录页必须使用 HTTPS,否则表单和 session 都有被盗取的可能
不支持 TLS 的服务 SEO 权重会降低 -
涉及安全的页面必须使用 TLS(Do Not Provide Non-TLS Pages for Secure Content)
-
TLS 下不要混用非 TLS 内容(Do Not Mix TLS and Non-TLS Content)
非 TLS 内容可被篡改 -
cookie 使用 secure 标识( Use "Secure" Cookie Flag)
设置 httpOnly 可以禁止页面访问该 cookie,设置 secure 可以设置 cookie 只在 https 下传输 -
敏感数据不使用 URL 传输(Keep Sensitive Data Out of the URL)
URL 中不能传输数据,即使使用了 HTTPS。有两种可能泄漏:
- URL 会被浏览器缓存,也就是该电脑上其他人可以看到该解码后的数据
- 页面上点击其他网站,其他网站会取到 referral 字段,从而泄漏
- 不要缓存敏感数据(Prevent Caching of Sensitive Data)
TLS 只能保证传输安全,客户端和代理部分泄漏数据是无法避免的,所以需要设置敏感数据不缓存
Cache-Control: no-cache, no-store"
"Expires: 0"
-
使用 Strict-Transport-Security (Use HTTP Strict Transport Security)
确保用户不会访问 HTTP 页面 -
Pinning 技术(Use Public Key Pinning)
地址: https://www.owasp.org/index.php/Certificate_and_Public_Key_Pinning
这一部分主要是讲使用双向验证的方式来解决掉中间人攻击的问题。简单来说就是客户端存储服务端的 public key,
通过验证服务端下发的公钥和当前存储的公钥是否相同,或者 hash 值是否相同。这种方式可以使用自签发证书,不需要使用 ca 签发的证书。
在客户端中使用该技术比较多, web 端同样可以使用:
Public-Key-Pins: max-age=2592000;
pin-sha256="E9CZ9INDbd+2eRQozYqqbQ2yXLVKB9+xcprMF+44U1g=";
pin-sha256="LPJNul+wow4m6DsqxbninhsWHlwfp0JecwQzYpOLmCQ=";
report-uri="http://example.com/pkp-report"
设置 key 的 hash 值,在指定的过期时间之前,服务端使用的 public key hash 不会变化,否则不应该信任。
服务端证书相关
-
使用加密更强的 key 和安全存储(Use Strong Keys & Protect Them)
目前安全最低位为 2048 位 -
使用包含当前必须域名的证书(Use a Certificate That Supports Required Domain Names)
使用一致的域名或主机名,不要使用过期证书。有多域名,需要证书中指定多域名,即Subject Alternate Names (SANs) -
使用正确的名称(Use Fully Qualified Names in Certificates)
不要使用 localhost 或者 ip 或者 www 等 -
不要使用通配符证书(Do Not Use Wildcard Certificates)
-
不要使用 RFC 1918 地址(Do Not Use RFC 1918 Addresses in Certificates)
比如:192.168/16, 172.16/12, and 10/8. -
根据用户基数设计合适的证书认证方式(Use an Appropriate Certification Authority for the Application's User Base)
内部应用可使用内部证书,所有用户应该安装该证书,其他情况请使用公用证书服务商,他们的证书已经内置于当前应用中。
不允许使用自签发证书 -
请提供所有需要的证书(Always Provide All Needed Certificates)
证书必须可被回溯到信任的根证书。证书路径可有多级,所以需要证书提供到根证书之前的所有证书 -
SHA1 证书已废弃(Be aware of and have a plan for the SHA-1 deprecation plan)
服务端协议和加密配置
-
仅支持强协议(Only Support Strong Protocols)
尽量提供支持 TLS 1.1 和 TLS 1.2 的 -
使用短暂密钥(Prefer Ephemeral Key Exchanges)
-
只支持强加密算法(Only Support Strong Cryptographic Ciphers)
-
(Support TLS-PSK and TLS-SRP for Mutual Authentication)
-
只支持安全的重新协商(Only Support Secure Renegotiations)
-
禁用压缩(Disable Compression)
-
更新加密库(Update your Crypto Libraries)
客户端配置
web 服务是由浏览器进行认证的,不受应用控制。其他情况下需要进行配置:
- 服务端应用以 TLS 方式连接其他的 web 服务或者交换数据。
- 重客户端应用 TLS 连接服务器
配置是否正确一定要验证
其他
-
EV 证书(Extended Validation Certificates),目的是的费用是合法的实体,给用户更多的保证,注意,它本身不提供更好的加密方式,
只是增加用户信心 -
客户端证书,也就是“双向 TLS”,即服务端同样需要验证客户端的身份,由于证书生成复杂,分发安全性,客户端配置,证书吊销和重发行,
这类证书用法只在高价值的连接且用户量比较少的场景 -
证书和公钥的 pinning(Certificate and Public Key Pinning)。Pinning 是将 host(比如服务器)和认证内容(证书或者公钥),
应用对已经存在的关系进行认证。意思是如果从服务端下载的证书和本地存在的证书不匹配,不建立连接。通常在 Hybrid 和Native 应用中使用。
而 web 浏览器的应用无法实现。
最后后端服务也需要进行加密,包括内网服务。
Cross Site Request Forgery (CSRF)
https://www.owasp.org/index.php/Cross-Site_Request_Forgery_(CSRF)_Prevention_Cheat_Sheet
简单翻译下:跨域请求伪造,一个恶意的网站、邮件、博客或者实时信息,通过浏览器针对信任的网站发起请求(用户已登录情况下)。
需要保护的内容包括:
- form 使用 POST
- Ajax/Xhr 调用
注意:不要使用 GET 请求执行状态改变操作,如果不得不,需要保护该方法。
CSRF 保护总结:建议使用基于 token 减少 CSRF 攻击。在高度敏感操作中,使用交互式保护,使用二次认证或者一次性 TOKEN。
深度防护参照后面
相关文章见:https://seclab.stanford.edu/websec/csrf/csrf.pdf
CSRF 推荐防护方式
主要方式:迁移 TOKEN 方式(Token Based Mitigation)
注:需要使用强加密或者 HMAC 方法,推荐使用 AES256-GCM 或者 SHA256/512 方式
严格的加密 KEY 轮换和 token 生命周期策略
- 令牌同步模式(Synchronizer Token Pattern,STP),写操作需要添加安全的随机 token,该 token 可以放在 headers 或者参数中(ajax)、URL (GET),与服务端的 token 进行对比。该方案本来用于防止表单重复提交,由服务端进行是否存在和是否正确进行验证,开发者可以更好的验证用户的提交意图,从而避免 CSRF 攻击。注意的是: token 不能像 cookie 一样由浏览器自动发出,同时如果使用基于请求的 token 比基于用户 session 的 token 安全性更高,但可用性较低,可能造成正常访问失效的问题。不要使用 GET 请求进行敏感操作,在 referer 字段中会暴露 csrf token,
- 令牌加密模式(Encryption based Token Pattern),即无状态模式。服务端基于用户 ID,时间戳,一次性数字使用唯一的 KEY 对 token 进行加密,返回给客户端,这种情况下重放攻击和修改攻击失效。主要用于无状态应用。
- 令牌HMAC模式(HMAC Based Token Pattern),即使用HMAC(hash-based message authentication code,哈希运算消息认证码)加密方式。它与令牌加密模式类似,有两点不同:1. 使用强 HMAC 函数而不是使用加密函数;2. 包括额外的字段"operation" 表明操作的目的。同样包含用户 ID,时间戳和 nouce,operation 字段
深度防护
- 验证标准 headers 中的 origin 字段(Verifying origin with standard headers)。
有两个步骤:1. 验证请求来源(source origin. 可通过 Origin/Referer header),2. 验证请求要去哪里(target origin)。之所有使用标准字段,是因为使用 javascript 的 XSS 攻击无法修改这两个字段(forbidden headers,只能由浏览器设置)。
- 验证来源 origin。其中 Referer 在从 https 跳转至 http 中会丢失,但会保留 Origin 字段,同协议之间跳转会保留 Referer. 一定要强验证,如 site.com 和 site.com.attacker.com. 如果两个字段都不存在,建议 block 掉该请求,或者记录请求,后续确认后进行拦截。
- 验证目标 origin。如果用户直接访问服务,可使用 URL 验证目标 origin; 如果你的服务在代理后面,1. 设置服务为已知的 origin,如果存在多域,可考虑使用集中的配置,实例从中获取;2. 使用 host 请求头的值,如果位于 proxy 后面,host 值会变化。3. 使用 X-Forwarded-Host 的值,该值的上的是包括初始的请求 host,大多数 proxy 会将 Host 值传过来。
通常情况下验证 header 没有问题,但特殊情况下可能不存在该 headers。比如
-
IE11 在跨域信任区时不带 Origin 字段
-
302 重定向跨域请求不带 Origin
-
一些隐私情况下 Origin 会为 null
-
Origin 在跨域请求中全部携带,在同域时只包括 post/delete/put 方法。
-
Referer 也同样有问题,部分情况下会被省略,同时负载均衡、代理、嵌入式设置也会因隐私的原因移除该字段。
所以要使用验证需要先观察一段时间,然后将例外添加至代码中。该方式有两个问题:依赖于浏览器行为,需要更新规则,同时存在 XSS 攻击时,防范效果有限。
-
再次提交 cookie 策略(Double Submit Cookie)
如果不方便使用 csrf token,可以使用该方式,通过比较请求带的随机值和 cookie 中随机值是否相等来判断。该方式基于用户无法读取 cookie 或者修改该 cookie 值,在跨域情况下也会出现写 cookie 的情况,比如写 cookie 可以指定为父域,则该域下的 cookie 都不安全,或者中间人攻击将 https 转换为 http。所以使用该方式要保证子域都安全,且只接受 HTTPS 请求。该方式的另一种变体是 header 中带有 加密的 token,请求中再带该 token。 -
同站 Cookie 属性(SameSite Cookie Attribute)
由 google 提出,阻止浏览器跨域发送 cookie。设置如下:
Set-Cookie: JSESSIONID=xxxxx; SameSite=Strict
Set-Cookie: JSESSIONID=xxxxx; SameSite=Lax
浏览器全局生效,但要注意,该技术目前处于草案状态,实现的并不完全。不推荐作为主要的防御方式
-
使用自定义请求头(Use of Custom Request Headers)
自定义请求头则只限于同域,在跨域时需要服务端指定其他携带的 Headers。该方式可保护 ajax 请求,不能保护 form 请求,跨域 CORS 设置 -
用户交互式防御(User Interaction Based CSRF Defense)
用户交互式会更为方便,比如使用
- 二次认证(密码或者更强)
- 一次性 token
- 验证码
仅限于高度敏感的场景
-
登录 SCRF(Login CSRF)
登录页面也会产生 CSRF 攻击,在用户已经登录的情况下,防范方式是未登录前产生一个pre-session(可使用以上三种技术)
其他:三次提交验证,使用 content-type 的 pre-flight
Cross Site Scripting (XSS)
本文地址: https://www.owasp.org/index.php/XSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet
攻击方式: Strored, Reflected 和 DOM XSS。
Stored: 将注入攻击脚本存储在目标服务器上,比如数据库,消息论坛,访客日志,评论等,受害者从服务器获取存储的信息时得到了恶意脚本, 为 Type-I 攻击
Reflected: 反射攻击的脚本不在 web 服务器上,主要是在错误提示,搜索结果,或者其他返回信息有一部分输入。反射攻击在其他路由上,比如邮件或者其他网站,当用户点恶意链接,提交了钓鱼表单或者只是浏览了恶意站点,恶意代码会被带到受攻击的网站。Type-II 攻击
DOM XSS 会在后面讲到。
注意,请使用已经成熟的一些库来实现,避免遗漏。通常实现 rule2 和3 即可满足大部分情况的需求。
XSS 预防规则
- 不在未允许的位置插入不信任的数据(Never Insert Untrusted Data Except in Allowed Locations)
- 插入 HTML 片段前需要进行转义(HTML Escape Before Inserting Untrusted Data into HTML Element Content)
,包括一些执行的上下文,如 script,style,事件. 使用 16 进制的实体 - HTML 普通属性转义(Attribute Escape Before Inserting Untrusted Data into HTML Common Attributes),仅限于普通属性如w idth,name,value等,属性设置不能用于复杂属性如 href, src, style 或者其他事件处理 onmouseover。
- js 代码在插入js 数据值时转义(JavaScript Escape Before Inserting Untrusted Data into JavaScript Data Values), setInterval 等函数是无法转为可靠数据的。HTML 中的 JSON 字符串同样需要转义,使用 JSON.parse 而不是 eval
- css 转义并在插入style 属性值前严格验证(CSS Escape And Strictly Validate Before Inserting Untrusted Data into HTML Style Property Values), url,behavior,custom 不能使用。例如:
{ background-url : "javascript:alert(1)"; } // and all other URLs
{ text-size: "expression(alert('XSS'))"; } // only in IE
- 插入 URL 参数时对URL 进行转义 (URL Escape Before Inserting Untrusted Data into HTML URL Parameter Values)
- 使用专用库来过滤 HTML 标签(Sanitize HTML Markup with a Library Designed for the Job)
- 阻止基于 DOM 的 XSS 攻击(Prevent DOM-based XSS)
其他预防方式
- 使用 httpOnly 的 cookie,防止cookie 被读取
- 实现 Content Security Policy. 这是解决 XSS 攻击的一个好方式,即创建来源白名单,如果 javascript,css,image 等。
Content-Security-Policy: default-src: 'self'; script-src: 'self' static.domain.tld
- 使用自动转换模板系统(Use an Auto-Escaping Template System)
- 使用 X-XSS-Protection 返回头(Use the X-XSS-Protection Response Header)
总结请参考 https://www.owasp.org/index.php/XSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet
数据类型 | 上下文 | 样例 | 防御方式 |
---|---|---|---|
String | HTML Body | <span>UNTRUSTED DATA</span> |
HTML 实体转义 |
String | 安全的 HTML 属性 | <input type="text" name="fname" value="UNTRUSTED DATA"> |
1. 侵入式转换为 > 256 为&#xHH; 格式. 2. 只在安全白名单内的属性添加不信任的数据 3. 针对不安全的属性如 background,id , name 进行严格验证 |
String | GET 参数 | <a href="/site/search?value=UNTRUSTED DATA">clickme</a> |
使用 %HH 格式对 GET 中参数进行转义 |
String | src 或 href 中不受信任的 URL | <a href="UNTRUSTED URL">clickme</a> <iframe src="UNTRUSTED URL" /> |
1. 规范化输入 2. URL 验证 3. 安全 URL 核实 4. 只使用安全白名单内的 HTTP 或 HTTPS 地址,避免使用 JS 协议打开新窗口 5. 属性转义 |
String | Css 值 | <div style="width: UNTRUSTED DATA;">Selection</div> |
1. 使用严格的结构验证 2. CSS 16进制转义 3. 设计良好的 CSS 特性 |
String | javascript 变量 | <script>var currentValue='UNTRUSTED DATA';</script> <script>someFunction('UNTRUSTED DATA');</script> |
1. 确保 js 变量使用引号起起来 2. js 16 进制转义 3. js Unicode 转义 4. 避免 \" ,\' ,\\ 的转义 |
HTML | HTML Body | <div>UNTRUSTED HTML</div> |
对 HTML 的标签等进行验证 |
String | DOM XSS | <script>document.write("UNTRUSTED INPUT: " + document.location.hash);<script/> |
参见 DOM 的 XSS 攻击 |
DOM XSS 防范
DOM XSS 和 stored reflected 攻击的主要区别在于应用中攻击注入的位置。 relected 和 stored 都是服务端注入,而 DOM 注入则是客户端注入。XSS 攻击总是在客户端执行的。本部分中HTML 等攻击都是由 Javascript 执行的,所以称为子上下文。
本部分摘自: https://www.owasp.org/index.php/DOM_based_XSS_Prevention_Cheat_Sheet
防范规则
- 插入 HTML 子上下文未受信任的数据时时需要先对 HTML 转义然后是 JavaScript 转义(HTML Escape then JavaScript Escape Before Inserting Untrusted Data into HTML Subcontext within the Execution Context)
- 在为 HTML 属性插入不受信任的数据时需要进行 javascript 转义(JavaScript Escape Before Inserting Untrusted Data into HTML Attribute Subcontext within the Execution Context)
- 在向事件监听和 js 代码插入未知数据时一定要小心(Be Careful when Inserting Untrusted Data into the Event Handler and JavaScript code Subcontexts within an Execution Context)
- 对要插入的 CSS 属性值进行 JS 转义(JavaScript Escape Before Inserting Untrusted Data into the CSS Attribute Subcontext within the Execution Context)
- 对要插入的 URL 属性进行 URL 转义(URL Escape then JavaScript Escape Before Inserting Untrusted Data into URL Attribute Subcontext within the Execution Context)
- 添加 DOM 时使用安全的函数或方法(Populate the DOM using safe JavaScript functions or properties),比如使用 textContent
- 修复 DOM 跨域脚本攻击(Fixing DOM Cross-site Scripting Vulnerabilities)
XSS 攻击难防范是由于攻击面广且浏览器之间有差异。下面是推荐实践:
- 不受信任的数据应该只作为展示的文本
- 使用模板的时候总是对 js 代码进行转义并将数据限定在引号之内
- 使用
document.createElement("xx")
,element.setAttribute()
,element.appendChild
等来构建动态的界面 - 避免 InnerHtml 等 HTML 渲染方法使用不受信任的数据
- 避免使用隐式
eval
的函数, 比如 setTimeout等。转义的正确方式是服务转换外部的的数据,客户端只转义子上下文比如 dom 方法的数据。 - 限制未知数据在正确的操作下使用,即不要使意料之外的生效
- URL 转义时要注意字符集的问题
- 限制 object[x] 使用的范围
- 让代码运行在 es5 下或者沙盒内,增加 API 解码难度
- 不要使用
evel
来解析 JSON 对象
推荐的转义库:esapi, Apache Commons String Utils,Jtidy, 其他自己公司的实现
最后,innerText 在某些标签下仍旧可被执行,火狐也不支持该方法。
Content Type 选项
主要是为了阻止 Chrome 和 IE 对服务端返回的类型进行文本 mime 类型嗅探。
减少在上传或下载文件时由于用户设置其他名称会导致文件后缀变为其他类型,比如可执行文件。
即设置 X-Content-Type-Options
为 nosniff
, 比如nginx
add_header X-Content-Type-Options "nosniff" always;
X-Frame-Options(XFO)
保护用户避免点击劫持, 主要指浮层或者链接篡改。该信息可以设置为 DENY(表明当前站点不可以被 iframe 嵌入) 或者 SAMEORIGIN(同一来源),ALLOW-FROM https://e.com
指定站点
nginx 设置:
add_header X-Frame-Options "SAMEORIGIN" always;
Content Security Policy
该属性表示网站内容如何载入且在哪里允许载入,也可以用于解决点击劫持的问题。
这个属性可设置内容比较多,解决一些 CSRF,XSS 问题。 CSP 设置决定哪些内容是属于白名单之内的。
Content-Security-Policy: script-src 'self'
参见:https://content-security-policy.com/
该属性可保护的内容包括:
default-src: 所有资源可允许的路径,主要是用于 fallback
script-src: 定义 script 可被执行的来源
object-src: 定义可以加载插件的的来源, 针对<object>, <embed> or <applet>
style-src: 定义css 来源
Img-src: 图片来源
media-src: 定义 video 和 audio 加载 来源
frame-src: 定义哪个来源可以加载 frame
frame-ancestors: 可引用当前页面的配置,设置为 none 几乎等同于 X-Frame-Options: DENY
font-src: 定义宝体来源
connect-src: 哪个来源可以加载 script 接口,针对的是 AJAX WEBSOCKET 和 EventSource
form-action: 哪个来源可以用于加载form 元素
sandbox: 哪个来源使用沙盒
script-none: script 元素加载哪个形式的的脚本
plugin-types:
reflected-xss:
report-uri: 报告冲突的网址,即策略拦截的后上报地址
Content-Security-Policy: default-src https:
允许加载 https 任何域
Content-Security-Policy: default-src https://scotthelme.co.uk:443
限定了加载地址
Content-Security-Policy: default-src https://scotthelme.co.uk:*
可任意端口
none
阻止加载此类资源
self
只加载当前来源
unsafe-inline
允许使用行内的 js 和 css
unsafe-eval
允许使用 eval
CSP 包含的内容还是比较多,使用的时候建议去阅读原文