伴随着移动互联网时代的飞速发展,人手一部智能机连接这个世界已经成为现状,隐私和安全问题也逐渐被越来越多的人重视。从 2021 年某滴安全信息泄露的消息公之于众后,人们对于信息安全的呼声和诉求也越发高涨。
回到正题,不管是 App,还是各个访问终端的入口,作为最终承载交互数据的来源 — API 接口,无疑需要对数据的安全访问提供最终的支撑和保障。接下来,让我们花点时间来聊聊关于 API 接口安全的那些事吧。
经常听人说,某某系统被黑了,某某 app 的短信 1 分钟内被狂刷了几万条,某某应用的系统瘫痪了...诸如此类不一而足。追根溯源,从问题分析的经验来看,不管系统攻击者采用何种手段,多数情况下,最终攻击的入口,或者说最终的受害者还是汇聚到了API 这一层,可见,API 可以说是系统的守卫员,不容有失。
关于 API 的常见安全问题有哪些呢?小编这里结合多年的实践经验,做了如下的一些总结以供参考。
当系统的某些接口被攻击者或别有用心的人恶意调用时,可能有如下表现:
为什么 API 接口会被恶意调用呢?经验来看,无非就那点,树大招风,你的产品被竞争对手盯上了,人家眼红了。
这也是一种比较场景的 API 安全问题,一个常见的场景就是,当你登录某个系统时,你的会话信息,像存在浏览器中的 cookie 信息,被别有用心者劫持了,然后这个人拿着你的信息冒充你的身份去请求 API 的数据,甚至修改你的数据返回给你进行诈骗等目的。使用过 Fiddler 抓包工具的同学可以在自己公司的产品中模拟下这个过程。
通常在 B 端产品中,会对某些 API 返回的部分字段数据进行脱敏,比如手机号,邮箱等,以保证用户的信息隐私。
尽管 API 层面对敏感数据做了脱敏处理,但敏感数据如果未进行加密处理,或加密的强度不够,或者没有安全的存储加密数据,以至于攻击者仍然能够获得敏感信息,进而攻击者可能利用此漏洞对客户端,或服务器发送特殊构造的数据,发出攻击,从而了解后台数据库表等信息,对系统安全构成威胁。这也就是接口敏感数据被窃取了。
深究起来,XSS 攻击更多偏向于前端这一层,但是对于一个能够提供充分安全保障的系统来说,API 的对于参数的安全校验也是非常重要的一环,对于系统来说,应该确保核心 API 的业务对于所有的入参都应该是安全,可信且经过校验之后才能进行数据存储的。这样可以从源头上保障 XSS 攻击影响的范围进一步缩小。
在系统架构设计之初,系统安全一定是一个重要的考量因素被纳入到架构设计规划中,系统安全关乎着既关乎公司的生存,也关乎产品的盈利,更进一步说,更关乎着法律法规对公司的监管合规性依据。下图所示,为一个通用的微服务业务架构图。
从实践经验来看,安全在一个系统的架构设计中占据着举足轻重的地位,从上图来看,可以说,安全考虑在架构设计的每一环都有着落地的目标,拆开来看,具体如下所述。
涉及到前端安全的技术,比如:
防火墙本身具有较强的抗攻击能力,它是提供信息安全服务、实现网络和信息安全的基础设施之一。
防火墙对于一个互联网公司的重要意义毋庸置疑,尤其是金融类,银行类等 B 端产品,防火墙的作用可以说是不可替代的,尽管这个技术已经不是什么新鲜的东西,但基本上所有的软件公司在产品发布到线上环境之后,所有来自外部的请求,都会经过服务器厂商的防火墙,只有通过了防火墙这一层请求才能继续往下进行。
关于防火墙的作用,这里简单列举如下:
关于网关,基本上所有的人都多少有一定的了解,网关在一个安全的系统架构设计中的作用,可以说是承上启下,至关重要,大体来说,从安全的角度来讲,主要体现在如下几个方面:
屏蔽真实的API地址
拿 Nginx 来说,如果后端的接口真实地址是:/API/v2/user/get/1,为了确保接口安全,屏蔽真实的地址,通过nginx 的反向代理之后,接口可能变成这样:
/platform/biz/API/v2/user/get/1。
负载均衡,均衡流量
从系统安全和系统可用性的角度讲,为了确保系统的高可用性,通常应用服务集群部署,这样可以避免单节点压力过大而造成业务高峰时系统不可用,有了网关这一层,就可以通过网关的配置动态实现负载均衡,以达到均衡流量的效果,从而对系统过载形成防护。
拦截恶意请求,定向黑白名单
以 nginx 来说,提供了可编程式的配置,通过编写脚本代码,对经过 nginx 的请求进行监控,尤其是对于那些恶意刷接口的请求,可以很好的进行识别,甚至可以在 nginx 这一层对那些恶意请求的 IP,IP 段进行黑名单的设置,从而对后台的服务进行第一层的安全防护。
限流
对一个系统来说,可用性已然成了系统是否稳定的考量因素的重要标准,当业务高峰期时,不管是外部的恶意请求,还是类似抢单这样的瞬间大流量来说,为了保障系统的整体可用性,必要的限流措施也是确保系统安全的重要手段,而网关作为承载系统流量的入口,在网关这一层做一定的限流管控是很有必要的。
以上从架构设计层面聊了一下常用的安全防护措施,而作为系统对外提供数据来源的 API 接口,也就是系统的核心后台服务,可以说,关于 API 的安全考虑,可以说很多开发者在设计过程中尚未引起足够的重视。这里小编说一个有意思的现象,在小编过往的工作经历中,那些甲方公司最终对项目进行验收时,通常需要对整个源码进行安全审计,其中审计最容易出问题的地方,就是很多 API 接口的安全问题,比如 XSS 攻击,CSRF 攻击,接口有被刷的风险...
接下来,针对上述谈到的问题,以及日常开发中对于 API 安全的一些规范性要求,一起探讨下在 API 安全设计的一些处理措施。
以当下流行的微服务来说,后台提供出去的 API 一定要规范访问边界,哪些接口可以暴露出去,哪些接口一定要通过鉴权才能访问,关于这一点,依照经验来说,做好下面几点即可:
对于某些比较大的平台来说,比如 pass 平台,通常都是十几个甚至几十上百个内部服务组成的,如此庞大的系统统一对外提供服务时,各应用服务之间必然存在着互相的调用,这些可以是 dubbo 调用,或者 http 的调用,通常不同的应用之间调用时,走 rest 接口非常多,同时,不同应用之间互相调用时,场景也是多样,有些需要认证,有些不需要,有些需要严格的认证,有些需要宽容的认证,这该怎么办呢?
针对上面的场景,这里可以考虑定义不同的 API 使用类型,针对不同类型的 API,在调用的时候策略也不一样,具体实践来说,可以参照如下设计原则;
系统外部可调用的 API
这种 API,即内部应用和外部应用都可以调用的 API,这类 API 的设计,通常需要通过一个统一的凭证颁发入口,调用者拿到这个凭证,在调用 API 时传过去,认证通过后,API 给与数据响应,由于凭证是系统内部的服务颁发,可以认为是安全的,同时,如果更进一步的话,可以对凭证做有效期的设定,甚至是加密处理等措施。
系统内部可调用的 API
即平台各个微服务应用之间调用的 API,由于内部的应用在使用之前,都需要一定的注册或者其他的认证措施,对于内部的 API 调用来说,可以在 API 接口中添加关键参数信息识别即可,比如,在请求内部 API 的时候添加一个 appName 这样的标识,一旦 API 校验这个 appName 合法有效,就可以给与响应。
无需认证的 API
比如请求系统的首页,或者上面谈到的请求短信验证码,再就是某些需要对接平台的外部第三方应用,这些第三方应用需要拿到一些非敏感的数据作为业务标识等,这种情况下,针对这样的 API,在设计时,要做好关键参数的校验,接口防刷的处理,以及可信 IP 的识别。
类似于登录,获取用户信息的 API 接口,一定要做加密处理,防止因会话劫持造成数据传输过程中敏感信息的泄露,关于加密,常用的加密方式包括:
一种常见的场景就是,前端请求 API 接口时,需要在请求的 header 中添加后台办法的 token 或者其他的令牌等参数,而请求到达 API 之前(有的在 API 中做),解析并统一校验 API 请求中的 header 是否携带了这个参数,且有效的情况下才予以返回。如果你的 API 安全等级更高,可以考虑在请求 header 中混合其他的定制化参数,这也是一种有效的保护 API 的手段。
对于一个成熟稳定的系统来说,尽管请求在真正到达 API 时有各种防护措施,比如限流,恶意请求 IP 识别等统一的措施,但是作为打通数据后台到前台的最后一关,针对系统中的某些核心业务的 API,还是有必要做针对性的接口防刷机制,具体来说,可参考下面的思路进行实施;
一般来说,从 rest 请求安全的角度来说,post 请求的安全性要好于 get 请求,这是因为大多数情况下,get 请求暴露在请求 url 中,而 post 请求的参数相对来说会隐蔽一些。这里给出的建议是,当查询请求参数超过 5 个时,可以对请求参数进行封装,然后将亲请求类型调整为 post。
为什么这一点放在最后来说,小编认为这是大多数开发者能够想到但在实际工作中又不能做得很好的一点。参数校验可以说是 API 层面最后一道基础但是重要的安全保障了,但是在参数校验这个度的把握上,很多开发人员显得有些不知所措,这里小编提出下面几点建议以供参考:
区分 API 的重要程度
并不是所有的 API 都需要一大堆的参数校验,为什么这么说呢?要知道,参数对于一个系统安全的影响随着链路的传递是逐渐增加的,假如说一段xss的字符作为参数传入到 API 接口中,假如不做处理最终会入库,后面再查询出来渲染到页面时候,这个危害性就大了,影响的不仅是用户的体验,甚至会失去一个潜在的有价值的用户。
说到这里,相信大家能够了解,假如在 save 数据的接口中就能够识别非法参数,从而进行拦截的话,后续所有的麻烦都可以提前避免掉。从这个角度来说,一个通用的做法就是,针对那些需要保存表单的数据,或者是涉及到修改,删除之类的 API,一定要做好参数的校验和控制。
不要过度对参数进行校验
看到过不少开发者在一个存储数据的接口中对参数进行了相当篇幅的代码校验,并不是说这样不好,而是需要区分一个度的问题,举例来说,有人在编写一个保存用户的 API 接口时,对用户名称做校验时,大概有几十行代码,涉及的规则如下:
我就想不通了,一个账户名的校验需要搞那么多花样吗,开个玩笑,这当然不是我的过激行为,我要表达的意思是,API 设计时,在考虑安全性的同时尽可能兼顾使用者的习惯,简化因参数校验带来的交互流程上的麻烦。
安全无小事,这句话应该来说对所有的行业都适用。希望从事开发的伙伴们在日常开发中对系统安全多一份敬畏,从而少一些因安全事故带来的不必要的麻烦。当然,系统的安全性建设是一项长期的工作,需要自顶而下,做好长远的规划,并逐步落实并贯穿到日常的每一个开发、测试、运维以及实施过程中,这样这才是长久之计,与君共勉。