阅读 14
收藏 0
2018-05-14
本文主要讲一些标准的规范使我们的 web 更加安全, 常见的 Web 安全问题将会在后面介绍(XSS,CSRF,MITM,SQL 注入等), 如有理解不当, 还望指出, 在此感谢.
文章略长, 主体线索为首先介绍浏览器 源 Origin 相关知识, 它是 Web 安全的基石, 后面的小节大多依赖该小节.
同源策略(Same-origin policy)
同源策略限制了一个源 (origin) 中加载的文档或脚本与其他源 (origin) 中的资源交互的方式. 这是一种用来隔离潜在恶意文档的关键安全机制.
一个源的定义
如果两个页面的协议, 域名, 端口 (如果有指定) 和都相同, 则两个页面具有相同的源.
源的继承
data:URLs 获得一个新的, 空的安全上下文.
在页面中用 about:blank 或 javascript: URL 执行的脚本会继承打开该 URL 的文档的源, 因为这些类型的 URLs 没有明确包含有关原始服务器的信息.
例如, about:blank 通常作为父脚本写入内容的新的空白弹出窗口的 URL(例如, 通过 Window.open() 机制). 如果此弹出窗口也包含代码, 则该代码将继承与创建它的脚本相同的源. data: URL 会得到一个新的空的安全上下文.
值得一提的时 IE 例外, 当涉及到同源策略时, Internet Explorer 有两个主要的不同点授信范围(Trust Zones): 两个相互之间高度互信的域名, 如公司域名(corporate domains), 不遵守同源策略的限制.
端口: IE 未将端口号加入到同源策略的组成部分之中, 因此
http://company.com:81/index.html
和
http://company.com/index.html
属于同源并且不受任何限制.
源的更改
页面可能会因某些限制而改变他的源. 脚本可以将 document.domain 的值设置为其当前域或其当前域的超级域. 如果将其设置为其当前域的超级域, 则较短的域将用于后续源检查.
假设
http://store.company.com/dir/other.html
文档中的一个脚本执行以下语句:
document.domain = "company.com";
这条语句执行之后, 页面将会成功地通过对
http://company.com/dir/page.html
的同源检测(假设
http://company.com/dir/page.html
将其 document.domain 设置为 "company.com"). 然而, company.com 不能设置 document.domain 为 othercompany.com, 因为它不是 company.com 的超级域.
PS: 在根域范围内, 浏览器允许你把 domain 属性的值设置为它的上一级域. 例如, 在 cicada.alipay.com 域内, 可以把 domain 设置为 alipay.com 但不能设置为 alipay.org 或者 com.
查看 document.domain 的用法 https://developer.mozilla.org/zh-CN/docs/Web/API/Document/domain
PS: 浏览器单独保存端口号. 任何的赋值操作, 包括 document.domain = document.domain 都会导致端口号被重写为 null . 因此 company.com:8080 不能仅通过设置 document.domain = company.com 来与 company.com 通信, 还必须在他们双方中都进行赋值, 以确保端口号都为 null .
使用 document.domain 来允许子域安全访问其父域时, 您需要在父域和子域中设置 document.domain 为相同的值. 这是必要的, 即使这样做只是将父域设置回其原始值. 不这样做可能会导致权限错误.
跨源网络访问
同源策略控制了不同源之间的交互, 例如在使用 XMLHttpRequest 或 img 标签时则会受到同源策略的约束
这些交互通常分为三类:
通常允许跨域写操作(Cross-origin writes). 例如链接(links), 重定向, 表单提交.
通常允许跨域资源嵌入(Cross-origin embedding).
通常不允许跨域读操作(Cross-origin reads). 但常可以通过内嵌资源来巧妙的进行读取访问. 例如可以读取嵌入图片的高度和宽度, 调用内嵌脚本的方法等.
以下是可能嵌入跨源的资源的一些示例:
script 标签嵌入跨域脚本. 语法错误信息只能在同源脚本中捕捉到.
link 标签嵌入 CSS. 由于 CSS 的松散的语法规则, CSS 的跨域需要一个设置正确的 Content-Type 消息头.
img 嵌入图片. 支持的图片格式包括 PNG,JPEG,GIF,BMP,SVG,...
video 和 audio 嵌入多媒体资源.
object, embed 和 applet 的插件.
@font-face 引入的字体. 一些浏览器允许跨域字体( cross-origin fonts), 一些需要同源字体(same-origin fonts).
frame(已废弃) 和 iframe 载入的任何资源. 站点可以使用 X-Frame-Options 消息头来阻止这种形式的跨域交互.
允许 / 阻止跨域访问
如何允许跨源访问
CORS,JSONP,document.domain,window.name,window.postMessage,CSST (CSS Text Transformation)这个比较少见的方案
以上方式各有使用场景, 具体实现请看知乎 - 关于前端跨域的整理 https://zhuanlan.zhihu.com/p/24198444
如何阻止跨源访问
阻止跨域写操作, 只要检测请求中的一个不可测的标记 (CSRF token) 即可, 这个标记被称为 Cross-Site Request Forgery (CSRF) 标记. 必须使用这个标记来阻止页面的跨站读操作.
阻止资源的跨站读取, 需要保证该资源是不可嵌入的. 阻止嵌入行为是必须的, 因为嵌入资源通常向其暴露信息.
阻止跨站嵌入, 需要确保你的资源不能是以上列出的可嵌入资源格式. 多数情况下浏览器都不会遵守 Conten-Type 消息头. 例如, 如果您在 HTML 文档中指定 <script\> 标记, 则浏览器将尝试将 HTML 解析为 JavaScript. 当你的资源不是你网站的入口点时, 你还可以使用 CSRF 令牌来防止嵌入.
跨源脚本 API 访问
不同浏览器可能有差异, 下面以标准为准.
Javascript 的 APIs 中, 如 iframe.contentWindow, window.parent, window.open 和 window.opener 允许文档间直接相互引用. 当两个文档的源不同时, 这些引用方式将对 Window 和 Location 对象的访问添加限制.
PS: 为了在不同源中文档进一步交流, 可以使用 window.postMessage https://developer.mozilla.org/zh-CN/docs/Web/API/Window/postMessage .
- window(方法和属性)
- window.blur
- window.close
- window.focus
- window.postMessage
window.closed 只读.
window.frames 只读.
window.length 只读.
window.location 读 / 写.
window.opener 只读.
window.parent 只读.
window.self 只读.
window.top 只读.
window.window 只读.
- location
- location.replace
location.href 只写.
跨源数据存储访问
存储在浏览器中的数据, 如 localStorage 和 IndexedDB, 以源进行分割. 每个源都拥有自己单独的存储空间, 一个源中的 Javascript 脚本不能对属于其它源的数据进行读写操作.
Cookies 使用不同的源定义方式. 一个页面可以为本域和任何父域设置 cookie, 只要是父域不是公共后缀 (public suffix) 即可.
PS:Public Suffix 为互联网名称与数字地址分配机构)提供的 TLD(Top Level Domain, 顶级域名)列表, 如 com,net,org 等都属于这个列表.
CSP (内容安全策略)
内容安全策略 (CSP, Content Security Policy) 是一个附加的安全层, 用于帮助检测和缓解某些类型的攻击, 包括跨站脚本 (XSS) 和数据注入等攻击. 这些攻击可用于实现从数据窃取到网站破坏或作为恶意软件分发版本等用途.
其实 CSP 的本质是以白名单的机制对网站加载或执行的资源起作用.
适用方式
可以通过配置你的网络服务器返回 Content-Security-Policy HTTP 头部 ( 有时你会看到一些关于 X-Content-Security-Policy 头部, 它是旧版本).
在 html 页面中 meta 元素中使用, 如下
<meta http-equiv="Content-Security-Policy" content="default-src'self'; img-src https://*; child-src'none';">
支持的策略指令
base-uri
base-uri 指令定义了 URI, 它可以作为文档的基准 URL.
connect-src
connect-src 指令定义了请求, XMLHttpRequest,WebSocket 和 EventSource(Server-Sent Events(简称 SSE)) 的连接来源.
default-src
default-src 指令定义了那些没有被更精确指令指定的 (默认) 安全策略.
该指令包含了以下指令: connect-src,font-src,img-src,media-src,object-src,script-src,style-src
font-src
font-src 指令定义了通过 @font-face 加载字体的有效源.
form-action
指定 form 提交的源.
frame-ancestors
frame,iframe 的 src 属性源.
img-src
img 元素的 src 属性源.
media-src
audio,video 的加载源.
object-src
object,embed,applet 的加载源.
plugin-types
plugin 源.
- sandbox
- script-src
script 源, 且禁用内联脚本和 eval().
style-src
style 的源.
内容源
源列表
源列表是一个字符串, 指定了一个或多个互联网主机(通过主机名或 IP 地址), 和可选的 URL 协议和 / 或端口号.
http://*.foo.com
匹配所有使用 http: 协议加载 foo.com 任何子域名的尝试.
mail.foo.com:443
匹配所有访问 mail.foo.com 的 443 端口 的尝试.
https://store.foo.com
匹配所有使用 https: 协议访问 store.foo.com 的尝试.
如果端口号没有被指定, 浏览器会使用指定协议的默认端口号. 如果协议没有被指定, 浏览器会使用访问该文档时的协议.
关键字
'none'
代表空集; 即不匹配任何 URL.
'self'
代表和文档同源, 包括相同的 URL 协议和端口号.
'unsafe-inline'
允许使用内联资源, 如内联的 script 元素, javascript: URL, 内联的事件处理函数和内联的 style 元素.
'unsafe-eval'
允许使用 eval() 等通过字符串创建代码的方法.
安全环境
当浏览器满足安全的最低要求时将进入一种安全环境. 安全环境允许浏览器暴露那些只有在被安全地传输给用户时才被允许的 APIs.
作用: 安全上下文的主要目标是防止攻击者访问功能强大的 api, 可进一步妥协的攻击的受害者.
检测环境是否安全
你可以使用特征检测来判断上下文是否处于安全的上下文之中通过使用在全局作用域下公共的 isSecureContext 返回的布尔值.
- // 在安全环境下使用 serviceWorker 执行 offline-worker.js
- if (window.isSecureContext) {
- // 页面是是个安全的上下文, 服务可以正常使用.
- navigator.serviceWorker.register("/offline-worker.js").then(function () {
- ...
- });
- }
如何保护我们站点
用户信息安全
如何关闭表单的自动补全功能
许多浏览器表单字段支持自动补全功能; 因此他们的值可以被推荐和下一次用户访问你的网站时自动恢复. 某一类型的数据, 你可能希望禁止这个功能.
默认情况下, 浏览器会记录用户网页上提交的输入框的信息. 这样浏览器便能够做到自动完成 (在用户开始输入的时候给用户提供可能的内容) 和自动填充 (在加载的时候预先填充某些字段) 功能.
如何禁用自动填充?
为整个表单设置
<form method="post" action="/form" autocomplete="off">
[...]
</form>
为单个字段设置
- <form method="post" action="/form">
- [...]
- <label>name:
- <input type="text" id="cc" name="cc" autocomplete="off">
- </label>
- </form>
在这里设置 autocomplete="off" 会有两种效果:
它会阻止浏览器为了以后自动完成类似的表单来自动保存表单数据, 但是浏览器是不一样的.
它会阻止浏览器历史记录缓存中的表单数据. 当表单数据来自缓存的时候, 当用户点击返回按钮来返回的时候, 用户填写的信息是可见的.
PS: 在某些情况下, 即使浏览器的自动填充设置为 off, 浏览器依然会继续提示自动完成的值. 这可能会让开发者百思不得其解. 强制浏览器不自动填充的方法是为 autocomplete 设置一个随机的字符串, 这个随机字符串不能为 https://developer.mozilla.org/zh-CN/docs/Web/HTML/Element/input#attr-autocomplete 属性的可选值.
自动填充属性和登录
现代浏览器继承了密码管理: 当用户使用用户名和密码在一个网站上登录了, 浏览器会替用户记住它(用户名和密码). 当用户再次访问站点你的时候, 浏览器会自动使用存储的数据自动填充登录模块.
由于这个原因, 许多现代浏览器都不支持在登录模块中设置 autocomplete="off" :
如果一个网站为表单设置了 autocomplete="off", 如果表单包含了用户名和密码, 浏览器依然会记住登录信息, 并且如果用户同意, 浏览器会在下一次用户访问网站的时候自动填充信息.
如果网站给用户名和密码的输入框设置了 autocomplete="off", 浏览器依然会记住这次登录, 如果用户同意, 浏览器将会在用户下次访问的时候自动填充登录信息.
如果想要阻止在用户管理页面中填写密码字段, 用户可以为自己以外的人指定新的密码, 虽然所有的浏览器都还不支持, 但是 autocomplete = "new-password" 应该被指定.
隐私与: visited 选择器
为什么说: visited 选择器暴露了用户隐私?
曾经, CSS 选择器 :visited 被网站用来查看用户的浏览记录. 通过使用 getComputedStyle() 或其他方法扫描用户的浏览记录来获取用户访问了哪些网站. 这很容易实现, 不仅能够判断用户是否曾经访问过这个页面, 还能猜测出大量的用户身份信息.
浏览器的处理
浏览器会在某些情况下对网页程序撒谎, 尤其是 getComputedStyle() 和类似的功能, 比如 element.querySelector() 总是返回值表示用户从未访问过网页上的任何一个链接.
另外, 如果用到了兄弟选择器, 如 :visited + span,\ 显示为未访问的样式.
而且, 在极少的情况下, 如果用到了嵌套链接元素并且这个匹配的元素与历史记录中的不同, 这个链接也以未访问的样式绘制.
对已访问链接样式的限制
你仍然可以给已访问链接设置视觉样式, 但是对可用样式作出了限制. 只有下列的属性才能被应用到已访问链接:
- color
- background-color
- border-color (and its sub-properties)
- outline-color
fill 和 stroke 属性的颜色部分(SVG 中使用)
内容安全
正确配置服务器的 MIME Types
什么是 MIME Types?
MIME 类型描述内容的媒体类型在电子邮件或由 web 服务器或 web 应用程序, 旨在帮助指导 web 浏览器如何处理并显示内容.
常用的 MIME Types?
- text/html for normal web pages
- text/plain for plain text
- text/css for Cascading Style Sheets
- text/javascript for scripts
- application/octet-stream meaning "download this file"
- application/x-java-applet for Java applets
- application/pdf for PDF documents
为什么是正确的 MIME 类型?
如果 web 服务器或应用程序设置了的不正确的 MIME 类型, 一个 web 浏览器没有方法通过 HTTP 规范从而知道作者想要怎样处理它.
大多数浏览器允许错误配置 MIME Types 的 web 服务器和应用程序可以通过猜测其 MIME Types.
服务内容使用正确的 MIME 类型也可以是重要的出于安全原因; 恶意内容可能影响用户的计算机, 冒充一个安全的类型的文档.
如何设置您的服务器发送正确的 MIME 类型?
如果你使用 Apache web 服务器, 只需将这个示例. htaccess 文件复制到目录, 其中包含您想要发送的文件正确的 MIME 类型. 如果你有一个完整的子目录的文件, 就把文件在父目录; 你不需要把它在每个子目录.
如果你使用 Microsoft IIS, 看到这篇文章 https://blog.csdn.net/xcymorningsun/article/details/53195521 .
如果您正在使用一个服务器端脚本 (Perl, PHP, ASP, or Java) 生成内容, 通常可以添加一行脚本的顶部. 只需要设置正确的 Content-Type.
严格使用 HTTPS(HSTS)
HTTP Strict Transport Security(通常简称为 HSTS)是一个安全功能, 它告诉浏览器只能通过 HTTPS 访问当前资源, 而不是 HTTP.
示例场景
你连接到一个免费 WiFi 接入点, 然后开始浏览网站, 访问你的网上银行, 查看你的支出, 并且支付一些订单. 很不幸, 你接入的 WiFi 实际上是黑客的笔记本热点, 他们拦截了你最初的 HTTP 请求, 然后跳转到一个你银行网站一模一样的钓鱼网站. 现在, 你的隐私数据暴露给黑客了.
Strict Transport Security 解决了这个问题; 只要你通过 HTTPS 请求访问银行网站, 并且银行网站配置好 Strict Transport Security, 你的浏览器知道自动使用 HTTPS 请求, 这可以阻止黑客的中间人攻击的把戏.
浏览器如何处理?
你的网站第一次通过 HTTPS 请求, 服务器响应 Strict-Transport-Security 头, 浏览器记录下这些信息, 然后后面尝试访问这个网站的请求都会自动把 HTTP 替换为 HTTPS.
当 HSTS 头设置的过期时间到了, 后面通过 HTTP 的访问恢复到正常模式, 不会再自动跳转到 HTTPS.
- Strict-Transport-Security: max-age=<expire-time>
- Strict-Transport-Security: max-age=<expire-time>; includeSubDomains
- Strict-Transport-Security: max-age=<expire-time>; preload
- Access-Control-Allow-Origin: http://api.bob.com
- Access-Control-Allow-Credentials: true
- Access-Control-Expose-Headers: FooBar
- PS:
- (1)Access-Control-Allow-Origin
- Origin: http://api.bob.com
- Access-Control-Request-Method: PUT
- Access-Control-Request-Headers: X-Custom-Header
- PS:
- Access-Control-Allow-Origin: http://api.bob.com
- Access-Control-Allow-Methods: GET, POST, PUT
- Access-Control-Allow-Headers: X-Custom-Header
- Access-Control-Allow-Credentials: true
- Access-Control-Max-Age: 1728000
- PS:
- (1)Access-Control-Allow-Methods
- <system.webServer>
- ...
- <httpProtocol>
- <customHeaders>
- <add name="X-Frame-Options" value="SAMEORIGIN" />
- </customHeaders>
- </httpProtocol>
- ...
- </system.webServer>
- // 这条指令规定了所有 JavaScript 都要有 integrity 属性, 且通过验证才能被加载.
- Content-Security-Policy: require-sri-for script;
- // 你也可以指定所有样式表也要通过 SRI 验证:
- Content-Security-Policy: require-sri-for style;
- // 原 js 文件地址
- https://unpkg.zhimg.com/za-js-sdk@2.8.3/dist/zap.js
- https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options
- PUBLIC SUFFIX LIST https://publicsuffix.org/
- https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Access_control_CORS
- https://developer.mozilla.org/zh-CN/docs/Web/HTTP/X-Frame-Options
- SRI Hash Generator https://www.srihash.org/
- https://en.wikipedia.org/wiki/HTTPS
来源: https://juejin.im/entry/5af84efbf265da0ba2671541