在应用程序开发过程中,API是一个会被经常提及的东西,它的全称是Application Programming Interface(应用程序接口),一般指的是Web API,即:采用HTTP通信协议的API或者是Web应用程序对外提供的API。
API从狭义上可以理解为是一种服务能力,调用方可以利用API很便捷的得到一组相关数据,但却不需要理解其内部的工作机制。
而在大型的软件系统中,API还可以作为不同应用程序之间的一种契约,通过它可以将应用程序进行组装,从而实现业务逻辑更为复杂的功能。
所以,API的设计将变得至关重要,合理规范化的设计,不但能够降低应用程序集成成本,提升软件系统开发效能,还能够增加API的可读性,有助于API的管理维护。
通常API设计思路会遵循如下几点
设计风格 | 统一设计理念,适配应用架构设计,提升开放性 |
能力封装 | 对能力进行封装,而不是对数据库操作进行封装 |
单一职责 | 尽量做一件事情,通过能力原子化,提升复用性 |
版本控制 | 确保所有变化,不能影响原有调用方的正常运行 |
命名规范 | 合理命名访问地址以及参数,用于降低理解成本 |
至今,在软件开发领域中,API所能带来的价值已经显而易见,甚至它是驱动创新的基本要素,但要知道,世界上任何事物,都有两面性,API也不例外。
在互联网的世界里,“安全”和“开放”是一个永恒的话题,但“开放创新”的前提必然是“安全可控”,也就是说没有安全的API,创新是不可能的。
那些不安全的API势必将成为攻击者的主要目标,他们不但可以从中窃取高价值的数据,还能破坏应用程序的正常运作。由此可见,API的设计中应当更加注重安全性。
什么是不安全的API
先来介绍一下OWASP这个组织,全称是Open Web Application Security Project,它是一个开源的全球性安全组织,致力于应用软件的安全研究。
它的使命是使应用软件更加安全,使企业和组织能够对应用安全风险做出更清晰的决策,同时推动安全标准、安全测试工具、安全指导手册等应用安全技术的发展。
在OWASP的众多项目中,可以发现有一个有关于API的安全项目,它不同于更广泛的Web应用程序安全项目,API安全项目更专注于理解和减轻与API相关的独特漏洞和安全风险。
OWASP在2019年颁布了API安全TOP10的第一版,其中列出了常见的API安全风险,如下是API安全TOP10的中文翻译概览。
它的主要读者对象可以是开发人员、架构师,管理人员,甚至是企业或组织,当你读完第一遍时,不知会有怎样的感觉,如果没有感觉,那就请你再读一遍。
TOP10的安全风险排名采用了风险评级方法,分别从可利用性、弱点普遍性、弱点可检测性、技术影响4个方面进行评级,有兴趣的同学可以在OWASP官网,查阅到API安全TOP10中每一项评级结果及内容说明。
但请注意,这个风险评估方法,并不会考虑威胁来源的可能性以及实际的业务影响,它仅用于企业内部根据自身情况,来评估接受多少应用程序以及API引入的安全风险。
API安全风险的治理思路
在充分了解API安全设计的重要性,以及API安全风险所带来的影响范围后,你必定会反问道,是否有快速增强API安全设计的捷径。
很显然,世界上就从来没有捷径,但OWASP提供了一套较为完整的治理思路,包括教育、安全要求、安全架构、标准的安全控件、软件安全开发生命周期五个方面,同时还配套提供大量免费的资源,来全面治理API安全风险。
看到这里估计你会意识到,API安全风险治理将是一个复杂工程,如果你的企业中没有一些同时擅长应用和安全技术能力的人,恐怕这项工作将会很难推动,即便是有,其工作量和难度也非比寻常。
当面对复杂问题时,采用分而治之的办法,是一种比较高效的方法。换言之,可以考虑将API安全风险治理这个复杂问题,拆分为若干个子问题,然后再根据当下的资源及能力逐一攻克。
那么,就拿API安全TOP10来看,可以考虑优先选择某一条进行治理。它可以是企业内部评估安全风险较大的一条,或者也可以直接参考API安全TOP10的排名,比如:TOP10的第一条,我想这可能是大部分企业最优先治理的一条。
另外,在API安全风险治理的探索过程中,还需要不断寻求规律及沉淀经验,以及求证治理方法的合理性及有效性,从而逐渐形成适用于企业自身的治理方法论,及API安全架构与设计规范。
不过,仅一份API安全架构与设计规范,并不能起到根治的效果,企业内部还得配套制定API安全管控流程,开发API安全风险识别工具才行,俗称:API安全风险治理三板斧。
API安全架构的设计思路
API安全架构与设计规范,可以通过定期宣导和培训进行传播,但实际效果并不会特别乐观。主要的问题根源还是在于有些开发人员,较为缺乏API安全开发的意识。
他们甚至连花费10分钟的时间,来阅读API安全架构与设计规范都不乐意,何况是让他们能够主动去改善,而在庞大的应用架构中,还存在着成千上万个应用程序或API,想要所有的开发人员,都能在安全意识上自驱更是难上加难。
那么,是否有一种API架构设计,能够尽可能减少对开发人员的依赖性,但又能起到部分API的安全访问能力。引入API网关可能是一个不错的选择,即:所有应用程序的API都将通过API网关对外进行暴露,特别是那些暴露至公网的API。
为了便于读者快速理解,绘制了这张API架构示意图。总体结构从内到外共分为三个部分,分别是API资产池、API网关、API调用方。接下来就分别对它们进行简单介绍。
API资产池
可以理解为相关性紧密的API集合,相关性可以根据业务领域、组织架构或其他等维度进行识别。而将API资产化纯粹是为了凸显它的价值性。
API调用方
可以理解为API的服务对象,或者为调用API的用户或应用程序,服务对象基本可以分为合作机构、外部用户、内部用户、内部应用四种类型。
API网关
可以理解为用于连接API资产池与API调用方的通道,它将作为API对外进行暴露的唯一入口,而面向不同的API调用方,可以设计不同场景的API网关。
综上,笔者将这种API架构称之为“护城河”架构,将API网关比喻为“护城河”,除API资产池内部互访及特殊场景下,其余API访问必须经过“护城河”,且大部分情况下“护城河”仅对持有通行证的用户或应用程序放行。
注:特殊场景可能为应用程序对内暴露的部分管理功能,例如:指标监控、健康检查等。
而不同场景的API网关对API调用方的安全要求等级也会不同,通常对外网的安全要求会比内网要高。最终可以形成如下这张API资产访问清单。
服务对象 | 使用环境 | 网关场景 | 安全要求 |
合作机构 | 外网 | 2B-API | 高 |
外部用户 | 外网 | 2C-API | 高 |
内部用户 | 外网/内网 | 2G-API | 中 |
内部应用 | 内网 | 2S-API | 中 |
通过这种API架构设计,可以将安全访问控制能力,从应用程序中剥离至API网关,一方面可以让应用程序更专注于实现业务服务能力,另一方面可以对API访问入口进行集中收口,规避因开发人员安全意识不足而注入的访问控制安全问题。
API安全治理的实践思路
用过API网关的同学都知道,它的核心能力主要是流量路由转发及分流。所以,缺省情况下,它并不会启用安全控制能力,因此,你需要根据实际使用场景,选择开启不同的安全控制能力。
API网关一般都支持如下安全控制能力
控制能力 | 防范场景 |
请求限流 | 防止攻击者频繁请求API,导致资源耗尽无法正常响应 |
签名验签 | 防止攻击者伪造身份请求API,或篡改请求报文数据 |
加密解密 | 防止攻击者通过非法手段拦截报文后,获取敏感数据 |
防重请求 | 防止攻击者通过非法手段获取报文后,重复请求API |
用户鉴权 | 防止攻击者在匿名情况下请求API,获取数据或攻击程序 |
访问约束 | 防止攻击者利用漏洞,采用不安全的方法或跨域请求API |
前文有提到过,不同场景的API网关对API调用方的安全要求也会不同。所以,并不是说所有API网关都要实现或开启以上控制能力,根据实践使用经验,参考如下
控制能力 | 2B网关 | 2C网关 | 2G网关 | 2S网关 |
请求限流 | 必须 | 必须 | 可选 | 可选 |
签名验签 | 必须 | *无需 | *无需 | 可选 |
加密解密 | 必须 | *可选 | *可选 | 可选 |
防重请求 | 必须 | *可选 | *可选 | 可选 |
用户鉴权 | 必须 | 必须 | 必须 | 可选 |
访问约束 | 必须 | 必须 | 必须 | 必须 |
注:*代表在移动端环境下为必须。
可以发现,面向公网的API安全控制要求要远远高于内网。所以说,在API安全治理的优先级上,建议采取先外后内的实践策略,毕竟面向公网的API处在一个完全开放的环境下,它更容易受到致命性的攻击。
另外,在最新的2021年最新版的OWASP TOP10中可以发现,“失效的访问控制”已从第五位上升到第一位,而如下这组风险因素数据,更能说明访问控制的安全风险,它将会越来越受到攻击者的“青睐”。
在明确API安全治理的实践策略后,就需要制定相应的实践步骤,而对于治理类相关的工作而言,一般主要有三个步骤:定义目标+识别差距+治理问题。
在完成第一轮治理后,重新定义目标并进行新的一轮,通过这种持续不断地加速循环,达到最终的API安全治理目的。
第一步:定义目标
在启动API安全治理前,最重要的任务,就是定义目标并明确范围,从而牵引工作始终朝着正确的方向推进。
API安全治理所涉及的范围非常大,所以初期可以考虑缩小治理范围,并确定一个可达的目标。例如:可以将公网API访问控制治理作为第一个目标。
在公网API访问控制方面,暂且列举了可能存在问题的两种主要现象,其中第一种,应该很容易理解,但是第二种,会觉得有点奇怪,这里谈论的是公网API访问控制,与内部API有什么关系。
但令人恐怖的是,有些开发人员在不知情的情况下,误将内部API直接暴露到公网上,而内部API安全控制可能相对较弱,甚至没有任何安全控制。
请试想一下,如果一个活动秒杀类商品库存可以被外部攻击者任意修改,将是一种怎么样的体验,我想可能不是秒杀商品,而是秒杀企业。
所以,不管是外部API还是内部API都将会在这个治理范围内,只不过治理目标有所区别,对于外部API需要增加安全控制手段,而对于内部API需要杜绝暴露至公网。
第二步:识别差距
在定义目标并明确范围后,可能还不能真正启动治理工作,还需要识别当前API现状与治理目标之间的差距,这样才能明确治理的方式和手段,而在识别差距前,还得收集当前API清单及现状。
如果你的企业中,API有专门的管理平台进行管理维护,那么收集API清单可能相对容易,但有些企业可能还在使用电子文档管理API,那么这项工作将会变得十分困难。
所以,这时可以考虑开发一个API扫描工具,自动获取应用程序中对外暴露的API,对于主流的Java应用程序而言,你的第一反应可能会想到,可以在源代码中,通过检测@Restcontroller或@Controller注解来识别API。
这种方式确实可以扫描到部分API,但可能并不完整,一是对外暴露的API不一定都采用注解的方式,二是应用程序引用的第三方的包中,也对外暴露了API,这部分可能是扫描不到的。所以,需要换一种方式,来获取应用程序对外暴露的全量API。
如果你的Java应用采用了Spring框架,那么可以考虑采用如下这个方法,在应用程序启动初始化后,可以从handlerMappings获取所有对外暴露的API信息。
org.springframework.web.servlet.DispatcherServlet的代码片段如下
private void initHandlerMappings(ApplicationContext context) {
this.handlerMappings = null;
if (this.detectAllHandlerMappings) {
// Find all HandlerMappings in the ApplicationContext, including ancestor contexts.
Map
matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false); if (!matchingBeans.isEmpty()) {
this.handlerMappings = new ArrayList<>(matchingBeans.values());
// We keep HandlerMappings in sorted order.
AnnotationAwareOrderComparator.sort(this.handlerMappings);
}
}
但要声明的是,API扫描工具并不是万能的,它仅仅只是提供了另一种手段,帮助你解决80%以上的问题,对于还在使用电子文档管理API的开发人员,还是建议对扫描后的结果进行查漏补缺。
另外,除了API扫描工具外,还可以抓取网络代理组件中的访问日志进行识别,最终结合扫描工具、自查上报、访问日志三部分的信息,形成一份较为完整的API清单,虽然它可能并不是完整的,但在条件有限的情况下,这可能是最好的结果。
有了API清单后,接下来需要对它们分门别类及现状分析,如下简单设计了API分类字段,你可以根据自身需要再进行调整或补充。
分类字段 | 可选项 |
API访问地址 | 通过扫描结果/自查上报/访问日志形成 |
API服务对象 | 合作机构/外部用户/内部用户/内部应用 |
API作用类型 | 公开信息类/用户鉴权类/业务功能类/非业务功能类 |
API读写类型 | 读/写 |
API用户鉴权 | 是/否 |
含有个人信息 | 是/否 |
允许公网访问 | 是/否 |
访问请求限流 | 是/否 |
报文签名验签 | 是/否 |
报文加密解密 | 是/否 |
控制重复请求 | 是/否 |
方法请求配置 | GET/POST/PUT/DELETE/HEAD/无/其他 |
跨域请求配置 | Access-Control开头相关参数 |
根据第一步定义的目标,可以先将所有允许公网访问的API全部筛选出来,随后分别对这些API进行现状分析及识别差距,例举如下:
方法请求配置原则上仅允许GET/POST;
跨域请求配置原则上默认不允许,需单独申请批准;
API服务对象原则上不允许为内部应用;
API服务对象若为合作机构,安全要求如下;
安全要求 | 公开信息类 | 用户鉴权类 | 业务功能类 | 非业务功能类 |
用户鉴权 | 无需 | 无需 | 必须 | 必须 |
请求限流 | 必须 | 必须 | 必须 | 必须 |
签名验签 | 必须 | 必须 | 必须 | 必须 |
加密解密 | 无需 | 必须 | *可选 | *可选 |
防重请求 | 必须 | 必须 | 必须 | 必须 |
注:*代表API是A2类时为必须。A1/A2分类可参考《商业银行应用程序接口安全管理规范》
API服务对象若为外部用户/内部用户,安全要求如下;
安全要求 | 公开信息类 | 用户鉴权类 | 业务功能类 | 非业务功能类 |
用户鉴权 | 无需 | 无需 | 必须 | 必须 |
请求限流 | 必须 | 必须 | 必须 | 必须 |
签名验签 | *可选 | *可选 | *可选 | *可选 |
加密解密 | *可选 | *可选 | *可选 | *可选 |
防重请求 | *可选 | *可选 | *可选 | *可选 |
注:*代表在移动端环境下为必须。
最后,根据针对每一条API所识别出来安全问题进行等级评定,前期为了简化工作难度,在没有安全风险量化指标之前,建议直接根据安全技术人员的经验,标记为高、中、低即可。
第三步:治理问题
当识别出所有API安全问题后,就可以全面启动治理工作,可以分别从API架构设计治理、API安全访问治理、API管控流程治理三个方面共同启动。
在API架构设计治理方面,将对API的暴露方式进行调整,暴露至公网的API将全部通过API网关进行暴露,并在API网关上配置API路由访问策略。
对于存在安全问题的API建议根据等级评定来决定是否立刻整改,剩余其他API建议逐步进行迁移,而对于以后新开发的API要求直接采用这种模式。
在API安全访问治理方面,将API安全控制能力从应用程序中剥离至API网关进行承接,所有应用程序都将由API网关进行安全保护。
当然,应用程序可在有条件的情况下,保留部分安全控制能力,只要不与API网关存在安全控制冲突即可。
在API管控流程治理方面,还需要分别在API发布的事前、事中、事后三个阶段增加必要的控制手段。
事前:API暴露公网提交申请,并在审核通过和必要的渗透测试后才会在API网关上进行注册并对外暴露,从而避免内部API误暴露至公网的情况发生;
事中:API请求增强异常监控,对于发现大量鉴权/验签失败、触发限流/防重等现象后进行预警,并由人工介入排查是否存在安全风险并及时进行拦截;
事后:API请求访问日志分析,对访问日志进行安全风险分析,包括撞库异常行为、数据过度暴露、敏感信息泄漏、上下文行为异常等访问安全数据分析;
因篇幅有限,以上三个步骤仅是围绕公网API访问控制治理进行了简要的实践说明,但这仅仅只是API安全治理的一小部分。
另外,API网关也并不能解决你所有的API安全问题,更多的还是应用程序自身需要进行API安全治理,例如:失效的功能级授权,即:水平越权安全漏洞。
写在最后
本文依次从突出API安全的重要性,到列举什么是不安全的API,及全面治理的思路方法,再到如何通过API架构设计减轻API安全问题,以及最后针对某API安全问题的实践过程分别进行了一一阐述,其中部分截图来自OWASP官网/中国网。
它可能并不全面,或者说并不完全适用于所有情况,但本文的意义,更多的是为了起到抛砖引玉效果,启发对API安全开发的思考,唤醒对API安全开发的意识。
在最后的最后,想说的是,赶在节前输出一篇有关于安全的文章,寓意并祝愿大家安安全全过大年,疫情过后尽开颜。