CSRF攻击原理图:
上图中Browse是浏览器,WebServerA是受信任网站/被攻击网站A,WebServerB是恶意网站/攻击网站B。
一开始用户打开浏览器,访问受信任网站A,输入用户名和密码登陆请求登陆网站A。
网站A验证用户信息,用户信息通过验证后,网站A产生Cookie信息并返回给浏览器。
用户登陆网站A成功后,可以正常请求网站A。
用户未退出网站A之前,在同一浏览器中,打开一个TAB访问网站B。
网站B看到有人方式后,他会返回一些攻击性代码。
-
浏览器在接受到这些攻击性代码后,促使用户不知情的情况下浏览器携带Cookie(包括sessionId)信息,请求网站A。这种请求有可能更新密码,添加用户什么的操作。
从上面CSRF攻击原理可以看出,要完成一次CSRF攻击,需要被攻击者完成两个步骤:
- 登陆受信任网站A,并在本地生成COOKIE
- 在不登出A的情况下,访问危险网站 B。
看到这里,你也许会说:“如果我不满足以上两个条件中的一个,我就不会受到CSRF的攻击”。是的,确实如此,但你不能保证以下情况不会发生:
- 你不能保证你登录了一个网站后,不再打开一个tab页面并访问另外的网站。
- 你不能保证你关闭浏览器了后,你本地的Cookie立刻过期,你上次的会话已经结束。(事实上,关闭浏览器不能结束一个会话,但大多数人都会错误的认为关闭浏览器就等于退出登录/结束会话了……)
- 上图中所谓的攻击网站,可能是一个存在其他漏洞的可信任的经常被人访问的网站。
现在常见的csrf防御是有那么几种方式:
- 一种是在表单加上随机token串,这种能够避免99%的CSRF攻击,还有1%就是首先没有XSS攻击。
- 一种是附加token的基础上加了refer的来源判断,
- 还有一种是XMLHttpRequest的请求的方式,他可以不用在表单里面加header,但是问题来了,如果你的网站以前经常用的时动态加载的数据,也就是ajax方式加载的,那么很容易用XMLHttpRequest在请求体里面加上先前respone返回token
- 每次表单请求都带验证码,对于用户体验这是万万不能的。我相信会让用户疯掉的。
现在我们来看看HTTP api有哪几种认证方式,首先要明白,认证和鉴权是不同的。认证是判定用户的合法性,鉴权是判定用户的权限级别是否可执行后续操作。这里所讲的仅含认证。认证有如下几种方法:
第一个是使用在HTTP规范中的Basic Auth,这个配置也是相当的简单,原理是在每个请求的header中添加用户名和密码的字符串(格式为“username:password”,用base64编码)。但是这种方式安全性较低,就是简单的将用户名和密码base64编码放到header中。 base64编码前:
Basic admin:adminbase64
编码后:Basic YWRtaW46YWRtaW4=
放到Header中:Authorization: Basic YWRtaW46YWRtaW4=
。正是因为是简单的base64编码存储,切记切记在这种方式下一定得注意使用ssl,不然就是裸奔了。 在某些产品中也是基于这种类似方式,只是没有使用apache的basic机制,而是自己写了认证框架,原理还是一样的,在一次请求中base64解码Authorization字段,再和认证信息做校验。很显然这种方式有问题,认证信息相当于明文传输,另外也没有防暴力破解功能。第二个API Key是客户端从服务端签收一个加密的key,然后自己通过一定的算法组合加密数据,服务端会根据你的来源解析key。类似:http://example.com/api?key=dfkaj134
client
Server
client端向服务端注册,服务端给客户端发送响应的api_key以及security_key,注意保存不要泄露,然后客户端根据api_key,secrity_key,timestrap,rest_uri采用hmacsha256算法得到一个hash值sign,构造途中的url发送给服务端。 服务端收到该请求后,首先验证api_key,是否存在,存在则获取该api_key的security_key,接着验证timestrap是否超过时间限制,可依据系统成而定,这样就防止了部分重放攻击,途中的rest_api是从url获取的为/rest/v1/interface/eth0,最后计算sign值,完之后和url中的sign值做校验。这样的设计就防止了数据被篡改。 通过这种API Key的设计方式加了时间戳防止了部分重放,加了校验,防止了数据被篡改,同时避免了传输用户名和密码,当然了也会有一定的开销。
3 第三种是OAuth(或者OAuth2)。OAuth协议适用于为外部应用授权访问本站资源的情况。其中的加密机制与HTTP Digest身份认证相比,安全性更高。使用和配置都比较复杂,这里就不涉及了。
4 第四种就是Token的机制。 在各种客户端上每次都让用户提交用户名和密码,这有些不合理的。 通常的情况是客户端通过一些可靠信息和服务器交换取token,这个token作为客服端再次请求的权限钥匙,当然token也是存在有效时间控制的。 Token通常比密码更加长而且复杂。那么一旦获得了token,在每次调用API的时候都要附加上它。这仍然比直接发送账户和密码更加安全,哪怕是HTTPS。把token想象成一个安全的护照。你在一个安全的前台验证你的身份(通过你的用户名和密码),如果你成功验证了自己,你就可以取得这个。当你走进大楼的时候(试图从调用API获取资源),你会被要求验证你的护照,而不是去前台重新验证。
5 第五种,JWT协议似乎已经应用十分广泛,JSON Web Token——一种基于token的json格式web认证方法。JWT是一段被base64url编码过的字符序列,并用点号分隔。它由三部分组成,头部header、载荷playload与签名sign。 服务端和客户端都可以通过secret_key来识别信息是否被串改过。其基本的原理是,第一次认证通过用户名密码,服务端签发一个json格式的token。后续客户端的请求都携带这个token,服务端仅需要解析这个token,来判别客户端的身份和合法性。在这里JWT是用来取代服务端的Session而非客户端Cookie的方案,当 然对于客户端本地存储,HTML5提供了Cookie之外更多的解决方案(localStorage/sessionStorage),究竟采用哪种存储 方式,其实从Js操作上来看没有本质上的差异,不同的选择更多是出于安全性的考虑。
具体算法如下图:
JWT的主要应用场景
身份认证
在这种场景下,一旦用户完成了登陆,在接下来的每个请求中包含JWT,可以用来验证用户身份以及对路由,服务和资源的访问权限进行验证。由于它的开销非常小,可以轻松的在不同域名的系统中传递,所有目前在单点登录(SSO)中比较广泛的使用了该技术。
信息交换
在通信的双方之间使用JWT对数据进行编码是一种非常安全的方式,由于它的信息是经过签名的,可以确保发送者发送的信息是没有经过伪造的。