当今,越来越多的数据通过API进行获取,API的安全性不再是一个事后才需要考虑的事情。关于API的麻烦之处在于它能直接访问大量数据而无需浏览器的检查。与其担忧SQL注入和XSS问题,你更应该关注能够对客户记录及其数据进行分页的那些坏家伙。
像Captchas和浏览器指纹这类的典型预防机制是没用的,因为在设计的时候就要求一个客户能够处理大量的API访问。因此我们该怎么办呢?首先,就是要像黑客一样思考,然后让你的API能够检测和阻止常见攻击以及那些API不曾听闻的未知攻击(如零时差攻击)。有一些攻击列在了OWASP安全API列表中,但不是全部。
1、不安全的分页和资源限制
大多数的API提供了类似于/users或/widgets的方法来访问资源列表。这样,客户(如浏览器)通常会对这个列表进行过滤和分页来限制返回给客户端的资源数目,如下所示:
First Call: GET /items?skip=0&take=10 Second Call: GET /items?skip=10&take=10
但是,如果那个实体包含了个人验证信息(PII)或其他信息,那么黑客就可能破坏掉这个endpoint以获取数据库中所有的实体信息。如果这些资源实体恰好披露了PII或其他敏感数据,那么这是最危险的事情。其实,开放你企业的资源使用统计情况给竞争对手或给骗子提供一份email列表依然是很危险的行为。
一个简单的防护机制是检查调用次数。一旦超过100或1000那么就抛出错误。这个方案的问题有两个:
- 对于数据类的API,合法用户可能需要通过cron job来获取和同步大量的数据记录。相对较小的分页能够强制令你的API被调用得非常频繁从而降低了整体的吞吐量。最大数据量的限制只是为了确保内存和可伸缩性被满足(同时阻止DDoS攻击),而不是为了保障安全性。
- 无法阻止攻击脚本通过随机休眠的方式来令频率检查失效,比如这样:
skip = 0 while True: response = requests.post('https://api.acmeinc.com/widgets?take=10&skip=' + skip),
headers={'Authorization': 'Bearer' + ' ' + sys.argv[1]}) print("Fetched 10 items") sleep(randint(100,1000)) skip += 10
如何预防?
如果要预防分页攻击,你应该记录单个资源中有多少资源子项在单位时间内被单个用户或API访问,而不能只在请求这个层级上统计。通过记录用户层面上的API资源访问情况,你就能阻止一个用户或API,比如当它们在一个小时内接触到了1百万个资源子项时禁止它们。这依赖于你的API使用模式。这会延缓黑客盗用你API的速率。如果他们必须手动创建一个新的用户账户的话,那么其实这和Catcha是类似的原理。
2、不安全的API秘钥生产
大多数API受某种API密钥或JWT(JSON Web令牌)保护。由于API安全工具可以检测到异常的API行为并自动阻止对API密钥的访问,因此这提供了一种自然的方式来跟踪和保护你的API。但是,黑客希望通过从大量用户生成和使用大量API密钥来超越这些机制,就像网络黑客会使用大量IP地址来规避DDoS保护一样。
如何预防?
抵御此类攻击的最简单方法是要求人员注册你的服务并生成API密钥。可以使用Captcha和双因子身份验证来阻止机器人流量。除非有合法的商业案例,否则注册你服务的新用户不应具有以编程方式生成API密钥的能力。相反,只有受信任的客户才应该具有以编程方式生成API密钥的能力。更进一步,确保在用户和帐户级别(不仅针对每个API密钥)对异常行为进行任何异常检测。
意外秘钥暴露
使用API会增加Credentials泄漏的可能性:
- API被持续地访问,这会增加黑客获得未过期的有效API密钥的可能性。你可以将该API密钥保存在服务器环境变量中,而不必理会。这与用户登录到交互式网站的情况形成了鲜明的对比,在该网站中,会话在短时间后到期。
- API使用者直接访问credentials,例如通过Postman或CURL。开发人员可能将包含API密钥的CURL命令意外地复制/粘贴到公共论坛(如GitHub Issues或Stack Overflow中)。
- API密钥通常是承载令牌,不需要任何其他标识信息。 API无法利用一次性使用令牌或双要素身份验证之类的东西
如果由于用户错误而暴露了密钥,你作为API提供者将要承担批评。但是,安全性的精华在于减少表面积和风险。像对待你自己的客户数据一样对待你客户的数据,并通过添加防止意外键暴露的保护措施来帮助他们。
如何预防?
防止密钥暴露的最简单方法是利用两个令牌而不是一个。刷新令牌存储为环境变量,并且只能用于生成短期访问令牌。与刷新令牌不同,这些短暂的令牌可以访问资源,但是有时间限制,例如数小时或数天。
客户将存储刷新令牌和其他API密钥。 然后,您的SDK将在SDK初始化时或最后一个访问令牌到期时生成访问令牌。 如果将CURL命令粘贴到GitHub问题中,那么黑客将需要在数小时内使用它以减少攻击向量(除非这是实际的刷新令牌,可能性很小)。
3、暴露于DDoS攻击
API开辟了全新的业务模型,客户以编程方式访问API平台。但是,这会使DDoS保护变得棘手。大多数DDoS保护旨在在DDoS攻击期间吸收和拒绝不良行为者发出的大量请求,但仍然需要让好人通过。这需要对HTTP请求进行指纹识别,以检查机器人流量看起来像什么。对于API产品而言,这要困难得多,因为所有流量都看起来像漫游器流量,而不是来自存在Cookie之类的浏览器。
停止DDoS攻击
关于API的神奇之处在于,几乎每个访问都需要一个API密钥。如果请求没有API密钥,则API可以自动拒绝它,这是很轻量级的。那么,你如何处理经过身份验证的请求?最简单的方法是利用每个API密钥的速率限制计数器,例如每分钟处理X个请求,并使用429 HTTP响应拒绝超过阈值的那些请求。有多种算法可以做到这一点,例如漏斗和固定窗口计数器。
4、服务器安全性不正确
如果服务器网络条件良好的话,API与Web服务器没有什么不同。 数据可能由于错误配置的SSL证书或允许非HTTPS流量而泄漏。 对于现代应用程序,几乎没有理由接受非HTTPS请求,但是客户可能会错误地从其应用程序或暴露API密钥的CURL发出非HTTP请求。 API没有浏览器的保护,因此HSTS或重定向到HTTPS之类的东西就起不到任何保护作用。
如何确保正确的SSL
通过Qualys SSL Test或类似工具测试你的SSL实现。 同时,你应该阻止所有可以在负载均衡器中完成的非HTTP请求以及删除所有HTTP标头,以清除任何泄漏实现细节的错误消息。 如果你的API仅由你自己的应用使用,或者只能在服务器端访问,请查看REST API跨域资源共享权威指南。
5、不正确的缓存头
使用API,您可以访问每个API密钥范围内的动态数据。 任何缓存实现都应具有作用于API密钥的范围,以防止交叉污染。 即使你不在基础架构中缓存任何内容,也可能使客户面临安全漏洞。 如果具有代理服务器的客户正在使用多个API密钥,例如一个用于开发,一个用于生产,则他们可以看到交叉授粉的数据。
这有多严重? 看看Twitter在数据安全事件发生后披露账单信息泄漏的问题
如何确保不缓存
你应该确保正确配置了Cache-Control标头。API的一大难题是,许多API并未使用标准的Authorization标头,而是使用诸如X-Api-Key之类的自定义标头。缓存服务器不知道此请求已通过身份验证,因此选择缓存该请求。