本文主要讲一些标准的规范使我们的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的用法
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)这个比较少见的方案
以上方式各有使用场景,具体实现请看知乎-关于前端跨域的整理
如何阻止跨源访问
阻止跨域写操作,只要检测请求中的一个不可测的标记(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。
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 端口 的尝试。
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设置一个随机的字符串,这个随机字符串不能为 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,看到这篇文章。
如果您正在使用一个服务器端脚本(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头,它都会更新这个网站的过期时间,所以网站可以刷新这些信息,防止过期发生。
Chrome、Firefox等浏览器里,当您尝试访问该域名下的内容时,会产生一个307
Internal Redirect(内部跳转),自动跳转到HTTPS请求。
语法
Strict-Transport-Security: max-age=<expire-time>
Strict-Transport-Security: max-age=<expire-time>; includeSubDomains
Strict-Transport-Security: max-age=<expire-time>; preload
跨域资源共享(CORS)
CORS属于HTTP访问控制特性,以下内容大多针对于XMLHttpRequest,有些并不适用于 Fetch 。
当一个资源从与该资源本身所在的服务器不同的域或端口请求一个资源时,资源会发起一个跨域 HTTP 请求。
比如,站点 http://domain-a.com 的某 HTML 页面通过 img 的 src 请求 http://domain-b.com/image.jpg。网络上的许多页面都会加载来自不同域的CSS样式表,图像和脚本等资源。
出于安全原因,浏览器限制从脚本内发起的跨源HTTP请求或者返回结果被浏览器拦截了。
例如,XMLHttpRequest和Fetch API遵循同源策略。 这意味着使用这些API的Web应用程序只能从加载应用程序的同一个域请求HTTP资源,除非使用CORS头文件。
PS:跨域并非不一定是浏览器限制了发起跨站请求,也可能是跨站请求可以正常发起,但是返回结果被浏览器拦截了。最好的例子是 CSRF 跨站攻击原理,请求是发送到了后端服务器无论是否跨域!注意:有些浏览器不允许从 HTTPS 的域跨域访问 HTTP,比如 Chrome 和 Firefox,这些浏览器在请求还未发出的时候就会拦截请求,这是一个特例。)
跨域资源共享(
CORS )机制允许 Web 应用服务器进行跨域访问控制,从而使跨域数据传输得以安全进行。浏览器支持在 API 容器中(例如 XMLHttpRequest 或 Fetch )使用 CORS,以降低跨域 HTTP 请求所带来的风险。
CORS分类
跨域资源共享标准新增了一组 HTTP 首部字段,允许服务器声明哪些源站有权限访问哪些资源。另外,规范要求,对那些可能对服务器数据产生副作用的 HTTP 请求方法(特别是 GET 以外的 HTTP 请求,或者搭配某些 MIME 类型的 POST 请求),浏览器必须首先使用 OPTIONS 方法发起一个预检请求(preflight request),从而获知服务端是否允许该跨域请求。服务器确认允许之后,才发起实际的 HTTP 请求。在预检请求的返回中,服务器端也可以通知客户端,是否需要携带身份凭证(包括 Cookies 和 HTTP 认证相关数据)。
简单请求
对于简单请求,浏览器直接发出CORS请求。具体来说,就是在头信息之中,增加一个Origin字段。
如果Origin指定的源,不在许可范围内,服务器会返回一个正常的HTTP回应。浏览器发现,这个回应的头信息没有包含Access-Control-Allow-Origin字段(详见下文),就知道出错了,从而抛出一个错误,被XMLHttpRequest的onerror回调函数捕获。注意,这种错误无法通过状态码识别,因为HTTP回应的状态码有可能是200。
如果Origin指定的域名在许可范围内,服务器返回的响应,会多出几个头信息字段。
相关请求头
Origin: http://api.bob.com
PS:上面的头信息中,Origin字段用来说明,本次请求来自哪个源(协议 + 域名 + 端口)。服务器根据这个值,决定是否同意这次请求。
相关响应头
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字段的值,要么是一个*,表示接受任意域名的请求。
(2)Access-Control-Allow-Credentials
该字段可选。它的值是一个布尔值,表示是否允许发送Cookie。默认情况下,Cookie不包括在CORS请求之中。设为true,即表示服务器明确许可,Cookie可以包含在请求中,一起发给服务器。这个值也只能设为true,如果服务器不要浏览器发送Cookie,删除该字段即可。
(3)Access-Control-Expose-Headers
该字段可选。CORS请求时,XMLHttpRequest对象的getResponseHeader()方法只能拿到6个基本字段:Cache-Control、Content-Language、Content-Type、Expires、Last-Modified、Pragma。如果想拿到其他字段,就必须在Access-Control-Expose-Headers里面指定。上面的例子指定,getResponseHeader(‘FooBar’)可以返回FooBar字段的值。
非简单请求
与前述简单请求不同,“需预检的请求”要求必须首先使用 OPTIONS 方法发起一个预检请求到服务器,以获知服务器是否允许该实际请求。”预检请求“的使用,可以避免跨域请求对服务器的用户数据产生未预期的影响。
整个过程(预检请求/响应->正常请求/响应)预检请求
浏览器先询问服务器,当前网页所在的域名是否在服务器的许可名单之中,以及可以使用哪些HTTP动词和头信息字段。只有得到肯定答复,浏览器才会发出正式的XMLHttpRequest请求,否则就报错。
相关请求头
Origin: http://api.bob.com
Access-Control-Request-Method: PUT
Access-Control-Request-Headers: X-Custom-Header
PS:
(1)Origin代表请求源。
(2)Access-Control-Request-Method
该字段是必须的,用来列出浏览器的CORS请求会用到哪些HTTP方法,上例是PUT。
(3)Access-Control-Request-Headers
该字段是一个逗号分隔的字符串,指定浏览器CORS请求会额外发送的头信息字段,上例是X-Custom-Header。
相关响应头
服务器收到”预检”请求以后,检查了Origin、Access-Control-Request-Method和Access-Control-Request-Headers字段以后,确认允许跨源请求,就可以做出回应。
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
该字段必需,它的值是逗号分隔的一个字符串,表明服务器支持的所有跨域请求的方法。注意,返回的是所有支持的方法,而不单是浏览器请求的那个方法。这是为了避免多次”预检”请求。
(2)Access-Control-Allow-Headers
如果浏览器请求包括Access-Control-Request-Headers字段,则Access-Control-Allow-Headers字段是必需的。它也是一个逗号分隔的字符串,表明服务器支持的所有头信息字段,不限于浏览器在”预检”中请求的字段。
(3)Access-Control-Allow-Credentials
该字段与简单请求时的含义相同。
(4)Access-Control-Max-Age
该字段可选,用来指定本次预检请求的有效期,单位为秒。上面结果中,有效期是20天(1728000秒),即允许缓存该条回应1728000秒(即20天),在此期间,不用发出另一条预检请求。正常请求
预检请求成功后就可以进行正常请求了,相应的请求头和响应头与简单请求类似。
相关请求头和响应头总结
请求头
Origin
语法:Origin:
作用:表明预检请求或实际请求的源站。
Access-Control-Request-Method
语法:Access-Control-Request-Method:
作用:用于预检请求。其作用是,将实际请求所使用的 HTTP 方法告诉服务器。
Access-Control-Request-Headers
语法:Access-Control-Request-Headers:
[, ]
作用:用于预检请求。其作用是,将实际请求所携带的首部字段告诉服务器。响应头
Access-Control-Allow-Origin
语法:Access-Control-Allow-Origin: origin | `作用:允许访问该资源的外域 URI。
Access-Control-Expose-Headers
语法:Access-Control-Expose-Headers: X-My-Custom-Header, X-Another-Custom-Header
作用:在跨域访问时,XMLHttpRequest对象的getResponseHeader()方法只能拿到一些最基本的响应头,Cache-Control、Content-Language、Content-Type、Expires、Last-Modified、Pragma,如果要访问其他头,则需要服务器设置本响应头。
Access-Control-Max-Age
语法:Access-Control-Max-Age: delta-seconds
作用:Access-Control-Max-Age 头指定了预检请求的结果能够被缓存多久,请参考本文在前面提到的preflight例子。
Access-Control-Allow-Credentials
语法:Access-Control-Allow-Credentials: true
作用:指定了当浏览器的credentials设置为true时是否允许浏览器读取response的内容。
Access-Control-Allow-Methods
语法:Access-Control-Allow-Methods: method[, method]作用:用于预检请求的响应。其指明了实际请求所允许使用的 HTTP 方法。
Access-Control-Allow-Headers
语法:Access-Control-Allow-Headers: field-name [, field-name]`
作用:用于预检请求的响应。其指明了实际请求中允许携带的首部字段。
X-Frame-Options 响应头
X-Frame-Options HTTP 响应头是用来给浏览器指示允许一个页面可否在 frame(已废弃), iframe 或者 object 中展现的标记。网站可以使用此功能,来确保自己网站的内容没有被嵌到别人的网站中去,也从而避免了点击劫持 (clickjacking) 的攻击。
使用 X-Frame-Options
X-Frame-Options 有三个值:
DENY
表示该页面不允许在 frame 中展示,即便是在相同域名的页面中嵌套也不允许。
SAMEORIGIN
表示该页面可以在相同域名页面的 frame 中展示。
ALLOW-FROM uri
表示该页面可以在指定来源的 frame 中展示。
PS:如果设置为 DENY,不光在别人的网站 frame 嵌入时会无法加载,在同域名页面中同样会无法加载。另一方面,如果设置为 SAMEORIGIN,那么页面就可以在同域名页面的
frame 中嵌套。
服务器配置
配置 Apache
配置 Apache 在所有页面上发送 X-Frame-Options 响应头,需要把下面这行添加到 ‘site’ 的配置中:Header always append X-Frame-Options SAMEORIGIN
配置 nginx
配置 nginx 发送 X-Frame-Options 响应头,把下面这行添加到 ‘http’, ‘server’ 或者 ‘location’ 的配置中:add_header X-Frame-Options SAMEORIGIN;
配置 IIS
配置 IIS 发送 X-Frame-Options 响应头,添加下面的配置到 Web.config 文件中:
<system.webServer>
...
<httpProtocol>
<customHeaders>
<add name="X-Frame-Options" value="SAMEORIGIN" />
</customHeaders>
</httpProtocol>
...
</system.webServer>
PS:注意: CSP Level 2 规范中的 frame-ancestors 指令会替代这个非标准的 header。CSP 的 frame-ancestors 会在 Gecko 4.0 中支持,但是并不会被所有浏览器支持。然而 X-Frame-Options 是个已广泛支持的非官方标准,可以和 CSP 结合使用。
除了通过服务器配置防止自己网页被iframe形式嵌入,还可以使用js处理。
子资源完整性(SRI)
子资源完整性(Subresource Integrity)是允许浏览器检查其获得的资源(例如从 CDN 获得的)是否被篡改的一项安全特性。它通过验证获取文件的哈希值是否和你提供的哈希值一样来判断资源是否被篡改。
子资源完整性 (SRI) 是一种安全功能,允许浏览器验证所获取的文件 (比如,从一个 CDN 内容分发网络) 是无意外操作而交付的。它的工作原理是允许你提供一个获取文件必须匹配的加密哈希。
SRI 如何工作
使用 内容分发网络 (CDN) 在多个站点之间共享脚本和样式表等文件可以提高站点性能并节省带宽。然而,使用CDN也存在风险,如果攻击者获得对 CDN 的控制权,则可以将任意恶意内容注入到 CDN 上的文件中 (或完全替换掉文件)),因此可能潜在地攻击所有从该 CDN 获取文件的站点。
子资源完整性通过确保 Web 应用程序获得的文件未经第三方注入或其他任何形式的修改来降低这种攻击的风险。
如何使用 SRI
将使用 base64 编码过后的文件哈希值写入你所引用的 \ 或 \ 标签的 integrigy 属性值中即可启用子资源完整性功能。
PS:integrity 值分成两个部分,第一部分指定哈希值的生成算法(目前支持 sha256、sha384 及 sha512),第二部分是经过 base64 编码的实际哈希值,两者之间通过一个短横(-)分割。
integrity 值可以包含多个由空格分隔的哈希值,只要文件匹配其中任意一个哈希值,就可以通过校验并加载该资源。
内容安全策略(CSP)和子资源完整性(SRI)共同使用
你可以根据内容安全策略来配置你的服务器使得指定类型的文件遵守 SRI。这是通过在 CSP 头部添加 require-sri-for 指令实现的:
// 这条指令规定了所有 JavaScript 都要有 integrity 属性,且通过验证才能被加载。
Content-Security-Policy: require-sri-for script;
// 你也可以指定所有样式表也要通过 SRI 验证:
Content-Security-Policy: require-sri-for style;
你也可以对两者都加上验证。
生成 SRI 哈希的工具
openssl 在命令行
你可以用 openssl 在命令行中执行如下命令来生成 SRI 哈希值:cat FILENAME.js | openssl dgst -sha384 -binary | openssl enc -base64 -A
在线生成 SRI 哈希值的工具
下面以知乎的一个js文件为例
// 原js文件地址
https://unpkg.zhimg.com/za-js-sdk@2.8.3/dist/zap.js
// 生成的脚本标签
<script src="https://unpkg.zhimg.com/za-js-sdk@2.8.3/dist/zap.js" integrity="sha384-B4YDh2AljLezOmNwiezobW8FJbJQfyZxm1SksT7THfKULK6SVxN+dRNSvLxEmXtA" crossorigin="anonymous"></script>
shasum 在命令行
shasum -b -a 384 FILENAME.js | xxd -r -p | base64
用户密码是泄漏的原因?
HTTPS 协议旨在保护用户数据在网络上不被窃听(机密性) 和不被篡改(完整性)。处理用户数据的网站应该使用 HTTPS 协议保护他们的用户不受黑客的侵害。如果网站使用 HTTP 协议而不是 HTTPS 协议,窃取用户信息(比如他们的登录凭证)将会轻而易举。这曾经被 Firesheep 很好地演示过。
这里罗列出密码所牵涉到的安全问题:
- 在HTTP之上运行登录表单. 即使表单的action对象是HTTPS链接,用户的登录表单信息也会受到威胁,因为攻击者能够通过用户修改用户接收到的页面(例如,攻击者插入键盘记录脚本来盗取用户输入的密码.他们还能改变表单目的页从而将敏感信息传递到受他们控制的服务器).
- 在表单的action链接中使用HTTP链接.在这种情况下,用户输入的任何信息都将以明文方式通过网络传递.这样,从密码离开用户的电脑到密码到达服务器过程中,用户的密码将清楚地展现在任何嗅探用户网络的人眼前.
- 在网页iframe中递交登录表单(或是嵌入在HTTP frame中的HTTPS frame).即使最上层页面是HTTPS,但在HTTP iframe中包含密码域和在HTTP页面中包含密码域是没有区别的.攻击者同样能够修改这个页面以及偷取用户信息.
- 有时网页需要用户名及密码,但实际上却没有存储这些敏感的信息.例如,一个新闻页面可能存储一个用户想要再次阅读的文章,却没有存储任何关于这位用户的其他信息.这个新闻站点的网页开发者可能没有动力去对于提高他的网站的安全性以及保护他们用户的信息.不幸的是,密码重用也是一个大问题.用户可能在不同的站点使用相同的密码(新闻网页,社交网络,电子邮箱及其银行).因此即使通过用户名及密码登陆你的网页对你来说不是很大的问题,对于重复使用相同用户名及密码来登陆他们银行账户的用户来说却是一个极大的威胁.网络攻击者正变得越来越聪明.他们在一个网站同时盗取用户名及密码然后在另一个可能能给他们带来金钱的网站上使用这些密码.
弱签名算法
在签署数字证书时,哈希算法的完整性是决定证书安全性的关键因素。哈希算法的弱点可能导致攻击者在某些情况下能够获得伪造的证书。由于技术的升级和已知的新型攻击,此类攻击的可行性已经大为提升。因此,不推荐使用旧算法,对于旧算法的支持最终也会停止。
MD5
对基于MD5的签名的支持已在2012年初停止。
SHA-1
基于SHA-1的签名非常普遍;截至2015年5月,大约45%的数字证书皆使用此算法。但是,SHA-1已经过时因而不再推荐使用。
SHA-1的证书将从2017开始不再被主流浏览器厂商视为安全的。
SHA-2(推荐使用)
SHA-2是一个哈希算法家族,其中包括SHA-256和SHA-512。截至2015年,SHA-2家族被认为足够安全强大。许多证书颁发机构颁发新的证书使用SHA-256。
参考文档:
浏览器的同源策略
CSP 策略指令
X-Frame-Options
PUBLIC SUFFIX LIST
CORS
X-Frame-Options
SRI Hash Generator
HTTPS
Let’s Encrypt,免费好用的 HTTPS 证书