RESTful API安全性初探

RESTful API

0x00 秀一下某些不安全的RESTful接口

写过一些REST接口,也用过别人的REST接口。老实说,包括自己写的接口在内很多接口都是不安全的。

随手截取了一个“不安全”的restful api接口范例:

Bad API 1

👆这是一套某直播平台的API。然后截取下来用来做一个微信小应用,真是极好的👇

Bad API 2

事实上,类似于这种不安全的接口是普遍存在的,包括我个人之前写过的接口。

Bad API 3

对于这种丧心病狂的,没有任何安全措施的接口,如果你不是对大众提供服务,着实是不应当存在的!

1
2
3
4
RESTFul API是基于HTTP的,http本身即是一个无连接、无状态的存在————每次的请求都会被断开、每一次的请求与上一次请求无关。

当然,Keep-Alive是为了解决无连接的,而Cookie和Session分别是通过客户端和服务器端解决无状态的。

由于是HTTP请求,因此我们可以从客户端、服务器端以及传输过程当中来解决(避免)RESTful的不安全性问题。

0x01 HTTP请求接口的规范化

我们知道HTTP请求是有getpostputdeleteoptionsconnectheadpatch的。事实上,这几种request method是有各自的使用场景的。

正确的使用请求方式,不但可以提高代码、场景的可读性,同时也是安全保障的基本点之一。一个简单的例子,用户登录时,我们大多时候大概是不会使用get方式的、而是使用post的,因为我们知道get传输的数据都是明文展现在请求的网址当中的。

实际生产环境当中,我们的环境远不止登录注册这样简单。一句话原则,我们设计的RESTful接口应当遵循幂等性原则。

1
2
3
4
5
密等性表示,不论多少次请求,服务器表现是一样的。
比如,
不论多少次请求,同一资源get得到的结果应该是一致的(并非一样);
无论多少次请求,http delete /article/23 始终是删除ID为23的文档
不论多少次请求,http put /article/98 始终表示创建或者更新资源ID为98的文档

0x02 使用SSL安全传输

简单的说是使用https替代http。

我们知道,http在传输的过程当中是明文的,http请求很容易被劫持破解甚至于篡改。而使用https之后数据则会被加密传输,数据无法被轻易篡改、甚至无法轻易被识别。

对于码农来说,这个大概是体力成本最低的。当然,HTTPS方式会比HTTP消耗更多的服务器资源和带宽,但是当前的服务器和网络环境,这些都不是问题。

SSL需要客户端证书方可访问服务器资源,这种证书获取可以是通过第三方机构,也可以是自签。
第三方机构签发的证书是可以被广泛接受的,而自签的则需要自己手动在客户端安装证书、同时信任该证书。建议使用第三方证书机构签发的证书,尤其是如果RESTful接口是提供给大众使用的时候。

0x03 传输数据的加密

无论是在客户端还是服务器端,对传输数据的加密,都是一个不错的措施。一个简单的例子,在传输数据的时候按照一定的顺序加密传输表单的内容,同时服务器也可以向客户端传输可解密的加密数据,实现双向加密。

例子一:

1
2
3
4
5
6
7
8
9
10
http post /v1/article

{
title: 'titleValue',
summary: 'summaryValue',
content: 'contentValue'
sign: md5(titleValue+summaryValue+contentValue)
}

服务器校验数据的时候会按照客户端相同的方式md5加密各项value形成服务器的sign,然后匹配sign,从而达到数据被篡改的目的。

例子二:

1
2
3
4
5
6
http post /v1/user/login
Encypt(jsonString("username:abc,password:md5(abc)"))

其中encypt是一个可解密的加密方法。
数据传输到服务器之后,服务器会对内容进行解密,然后json序列化出表单数据。
这种方式的安全性绝壁是高于例子一的。

但是,不论是例子一还是例子二,都存在兼容性问题。如果我们的接口提供给android、iOS使用是绝壁没有问题的,因为不论是java、objective-C还是swift,甚至于kotlin,都是可以自定义实现加解密方法。然而,在传统的web前端当中,javascript做加解密可能略显复杂,而且即便实现了,代码还是可以看到。

如果做到在javascript当中也能使用加解密?要么,使用javascript实现,同时对加解密函数进行js加密;要么,服务器提供单独的接口对数据进行加解密。

0x04 限定请求头

限定请求头,简单的说就是只开放固定HTTP头的request访问资源。这里包括:

对标准的http请求头进行限定,比如限制UA(user-agent);

a. 添加自定义的请求头来做校验。

b. 自定义请求头是一个很棒的方案,当前市面上的很多公开的接口/服务提供商就是采用的这种方案。方案可以在一定程度上防止攻击。

然而,对于限定请求头的请求,我们是可以通过抓包获取并且破解的。当然,我们还可以对请求头的某些字段进行加密传输(整体使用ssl/给字段采用动态生成的方式,即每次都有不同的自定义字段值)

0x05 使用HTTP Basic Auth加密

http基本认证wiki允许Web浏览器或其他客户端程序在请求时提供用户名和口令形式的身份凭证的一种登录验证方式。

采用基本认证方式,需要我们在服务器端(apache/nginx等)配置好认证数据,然后在每次请求时,在请求头中添加认证字段:

1
2
简单的格式:
Authorization: Basic base64("username: password")

事实上,即便服务器端没有使用基本认证,我们也可以通过自定义头信息的方式来完成类似的功能。确保用户名、密码匹配正确方可放行请求。

0x06 限定请求来源

说到请求来源,大多都可以通过HTTP头来识别。限定指定的请求来源可访问资源,可以限制request的请求头,限定user-agent、限定指定的头字段等都属于其中的方案。

在这个基础上,我们可以限制访问者IP,限制访问者refer来源。当然,限定IP和限定来源大多集中在web应用上比较多。

0x07 限定请求频率

如果一个接口、一秒钟、同一个用户访问量达到5000次,那么在正常的应用当中大概是不会存在的。限定单接口、单用户单位时间内的访问次数,可以大大减少被攻击的可能。

0x08 黑白名单

不管是端应用还是web应用,我们都还是可以通过黑白名单的机制解决一些问题的。如果RESTful受众固定,并且比较狭窄,我们可以通过白名单机制;对于异常的请求,我们还可以开启黑名单。

黑白名单的方式,不但可以用在代码层、同时可以用在服务器层(结合apache、nginx甚至于iptables)。

0x09 采用OAuth2方案

这种方案,应用超级广泛。主要用在跨站的用户数据共享上,国外Google、twitter、Facebook,国内的BAT的不少开放产品都有使用。

对于跨站应用可以采用基于OAuth的鉴权方式鉴权。