随着计算机、互联网技术的飞速发展,信息安全已然是一个全民关心的问题,也是各大企业非常重视的问题。企业一般会从多个层次着手保障信息安全,如:物理安全、网络安全、系统安全(主机和操作系统)、应用安全等。
对于应用程序安全,需要在应用架构、代码、运维、管理等多个角度进行安全性评估,在整个应用程序生命周期中,软件工程师们则主要负责身份验证、访问授权、进程间通信安全、代码安全、安全的管理与审计这五方面的方案落地。这五个方面中,前三个侧重于技术实现,代码安全、管理与审计则更需要规范的管理和执行,本文将着重对认证、授权、通信等技术相关内容重点介绍,管理规范相关内容仅做简单说明。
文中以采用了微服务架构的应用程序为背景进行描述,但多数的应用程序的安全方案与是否采用微服务架构并没有强关联,如有差异的地方,文中会提出来。文中描述过程中会涉及到一些微服务架构中相关的概念名词,说明如下:
微服务架构中安全访问相关的简化版的运行视图如下:
运行视图
图中星号*标注的位置就是服务调用过程中安全访问过程中的一些需要认证鉴权的关键位置,如:内外部访问认证、令牌验证与授权、内外网通信协议等。后续章节将对这部分展开分析。
多数业务功能类的应用的首要任务就是需要做身份认证。对于数据公开或新闻发布类的门户网站类应用不需要考虑这一点,他们更关注的是数据开放之前的管理和审批。
身份认证或身份验证(Authentication),顾名思义就是对应用程序的"访问者"的身份进行验证识别。访问者分两类。
不论是用户还是API客户端,在访问应用之前,均需要先到认证管理系统IAM进行注册,以创建其的身份凭证(用户账号和密码、客户端ID和密码)。有了身份凭证,才能通过认证服务的验证。
统一认证管理系统IAM(Identity and Access Management )负责身份识别和访问管理,其核心业务是应用系统的访问者的注册、账号密码凭证、访问者基本信息的管理、对已注册的访问者进行合法性认证和访问的初级授权(获取访问者自身数据)等等。
有些企业还会将组织机构、角色甚至业务功能权限数据也一并归入IAM系统管理。IAM对权限管理范围可大可小,不同企业的管理需求和力度也不一样,需要集中进行功能授权管理还是下放到业务系统中自治各有优缺点,选择合适自己的方式就好。
传统WEB应用对于用户登录访问,采用会话状态在服务端保存的方案,用户请求通常采用会话粘滞(Sticky session)或会话复制(Replication session)策略,来保持客户端和服务端的会话。为了会话共享而不得不将会话信息写入公共缓存或数据库,导致微服务应用之间产生了耦合性。
微服务架构中不推荐采用服务端保存会话的方式,如果引入状态管理不是必要的,那么应用尽量保持无状态运行。推荐使用另外一种基于访问令牌的模式,这种模式下应用中不需要保存会话状态,并且API客户端和基于登录的客户端均方便使用访问令牌。微服务架构推荐使用OAuth2.0 授权协议来搭建IAM系统。Spring 体系可以基于Spring Security OAuth实现授权服务器和客户端。
在本文的上下文中,面向的是企业基于微服务的总体架构进行方案设计,企业整体架构中,默认认证体系方案为企业统一认证而非业务系统各自认证(此方案的前提条件)。OAuth2.0本身是为三方授权而设计的,而在本方案中讨论的是企业内部应用的整体认证和授权,不存在第三方。因此本方案中基于OAuth2.0实现的授权服务可以简单理解为仅为IAM统一认证管理系统中的“账号管理应用资源提供者”做授权,并且默认实现为认证通过自动授予已登录账号数据的读写权限,不在登录通过后与用户交互确认是否同意授权。其他业务系统作为资源提供者的授权则是系统管理员预置好的授权,也不需要由用户登录时决定是否授权。这个OAuth2.0的使用场景可能与其他OAuth2.0相关资料或授权框架默认实现有所不同,请大家注意区分。
OAuth协议中定义了四种角色:
能够许可对受保护资源的访问权限的实体。当资源所有者是个人时,它被称为最终用户。
托管受保护资源的服务器,能够接收和响应使用访问令牌对受保护资源的请求。
使用资源所有者的授权代表资源所有者发起对受保护资源的请求的应用程序。术语“客户端”并非特指任何特定的的实现特点(例如:应用程序是否是在服务器、台式机或其他设备上执行)。
在成功验证资源所有者且获得授权后颁发访问令牌给客户端的服务器。
角色分析:
在OAuth2.0授权协议中,主要定义了四种许可类型:授权码许可、简单许可、密码凭据许可和客户端凭据许可,详细请参见规范内容:rfc6749 - The OAuth 2.0 Authorization Framework
(https://tools.ietf.org/html/rfc6749)
下面我们根据访问者类型,分别推荐合适的授权方案。
典型的API客户端如批量调度系统、物联网设备程序等,通常不需要用户登录授权就可以自动运行。使用客户端凭证许可类型比较适合。
客户端凭证
上图为OAuth2.0规范标准流程图,结合此场景中,对应OAuth2.0中的角色,API客户端作为OAuth2.0的客户端、IAM则为授权服务器。
其他说明:
OAuth2.0 协议中提出前端单页Web应用可以用简单许可模式,但简单许可模式有些局限性,令牌到期就需要重新登录授权,不支持令牌刷新,用户体验较差。很多使用简单授权的应用为了改善用户体验会颁发一个长期的令牌几天甚至几周。
如果有条件使用授权码模式,支持刷新令牌则是一个更好的选择。对访问令牌时间较短如2分钟,刷新令牌为一次性令牌有效期略长如30分,如果存在已作废的刷新令牌换取访问令牌的请求,授权端点也能够及时发现做出相应入侵处理,如注销该用户的所有刷新令牌。在保持良好用户体验的同时还兼顾了部分安全性。
本场景以微服务架构中常见的前后端分离Web应用作为示例,前端是单页应用,网关作为Web后台是服务提供端应用功能入口,也可作为OAuth2.0的客户端,让前端Web应用能借助网关实现授权码交换。因此在微服务架构中,即便是纯前端单页应用类的Web应用,仍可以用基于网关交互的授权码模式获取访问令牌。其他非前后端分离的混合Web应用自身就是客户端,不需要借助网关交换访问令牌。
授权码
上图为OAuth2.0规范标准流程图,结合此场景对应OAuth2.0中的角色,用户是资源所有者、浏览器为用户代理、网关作为被授权的客户端、IAM则为授权服务器。
其他说明:
个人移动设备上安装的原生App本身具备实现授权码流程的能力,不需要借助网关实现授权码流程。认证通过后网关仅负责校验访问令牌即可。
授权码
移动App实现登录重定向通常可以采用如下方式:
移动端App的运行环境是不可信的,容易被恶意App入侵的风险,包含如下两种情况:
基于上述风险和问题,移动App基于授权码获取访问令牌的流程需要进行优化解决,rfc规范中建议的实现方案是移动App授权流程采用使用带有PKCE支持的授权码模式。PKCE, 全称Proof Key for Code Exchange,即保护授权代码授权。这其实是通过一种密码学手段确保恶意第三方即使截获Authorization Code或者其他密钥,也无法向认证服务器交换Access Token。
经过PKCE改进的授权码、访问令牌交换过程示意图如下:
PKCE
上图为PKCE模式下授权码申请和交换访问令牌的过程,说明如下:
第三方无法根据code_challenge推导出code_verifier,因为code_challenge采用了不可逆加密方式。只有移动App客户端自己才知道这两个值。因此即使恶意App截获了code_challenge和授权码,也无法换取访问令牌避免了安全问题。要实现这种移动App的PKCE授权码模式,出移动App自身外,还需要IAM的授权服务器基于标准的授权码流程扩展配合实现。
关于移动App安全认证的详细内容请参考官方规范:
(https://tools.ietf.org/html/rfc825)
(https://tools.ietf.org/html/rfc7636)
用户密码凭据
上图为OAuth2.0规范标准流程图,结合此场景中,对应OAuth2.0中的角色,用户是资源拥有者、特权应用是客户端、IAM提供授权服务器
其他说明:
访问者能够访问的接口通常是两类:身份认证API、应用功能类API。
用户登录认证由IAM授权服务器配合用户资源服务负责。认证成功后,IAM访问者颁发访问令牌。后续对应用功能的访问过程中,均须携带访问令牌以表明访问者的身份。
网关作为业务系统的API入口,当面向外网的访问者时网关还是内外网的分界,访问令牌验证理应由网关负责,不应该将令牌验证的事情交给服务提供者。网关负责验证既能避免未经验证的请求进入内网,又能够简化服务提供端的代码,服务提供端无需处理不同类型客户端的验证。实际上好处还不只这些,在网关可以统一做流控无需应用端重复建设类似功能;系统内部调试、变更敏捷,减少了跨组织交互。
网关验证访问令牌有两种方案:网关委托认证服务验证、网关直接验证,说明如下:
网关委托IAM校验令牌
网关直接校验令牌
上述两方案中,方案一的令牌是无业务含义的身份标识字符串,每次收到请求网关都去IAM认证,对IAM认证服务的性能压力较大。方案二中IAM颁发的令牌中包含部分客户端或用户信息,使用JWT加密,IAM将验证方式或SDK提供给了负责认证的网关。对于IAM来说,减少了每次请求令牌认证带来的通信次数,减轻了IAM的压力。
推荐采用方案二实现令牌检查,需要注意的是方案二中的JWT令牌中仅包含必要的信息即可,不要放太多的角色权限信息。后续功能中需要额外的信息时,可以根据令牌再去IAM中获取。如果令牌中存放了很多的权限数据,一旦后台的授权数据发生变化,令牌中的权限数据与实际IAM的权限会存在不一致的问题,只能强制用户下线重新登录。
JWT令牌是防篡改的,但并不加密,如需要存储到浏览器存储中,建议采用JWT+JWE方式进行令牌加密。令牌中存放必要少量数据即可,避免滥用。多数服务器通常会对Http header、cookie长度做限制。
我的答案是不需要,否则太麻烦了。通常网关是独立团队负责,API变更发布、内部联调验证还得跨团队协调实在不可行。推荐系统内直通不走网关,系统之间访问必须走网关。要做到这一点,应用也需要实别请求来源进行客户端认证,这种认证方案没必要太复杂,应用只应该允许信任的网关和系统内部应用程序访问其服务,不允许系统外部请求绕过网关直接调用,因此,需要在网关和系统内部应用之间这个小范围内建立信任,常见方案有两种:
方案一优点是实现简单,缺点是安全级别略低,常见的企业架构中,网关和业务系统会是不同团队甚至不同的厂商负责开发维护,内部令牌共享给了其他团队负责的网关,存在一定的风险。方案二相比方案一略复杂一点,安全性更高,系统内互通用内部令牌,系统和网关认证使用了网关提供的安全令牌检查方式。两种方案可根据实际需求选择。
通过认证的API客户端能够访问网关开发的所有API吗?通过认证的用户能够调用所有API吗?通过认证的用户允许调用修改订单的接口,那么他能修改所有人的订单吗?
很显然绝大多数场景下上述三个问题答案都是"不能"。在绝大多数业务场景中除了对访问者的身份认证之外,我们还需要再进一步控制权限。
如果访问者是API客户端时,API调用的权限需由网关进行控制。建议采用先订阅再访问的授权模式,网关应该仅允许API客户端访问其订阅过的API 。具体实现方法就是绝大多数网关都会提供的基于API Key控制API访问的方式。
需要注意的是,仅使用API key的访问控制是不够的。API Key是在网关订阅API时生成的一串唯一编号,并不具备识别客户端身份的能力。就好比以前买火车票是不实名的,谁拿到火车票,都可以乘坐对应车次。火车票实名制之后,首先需要核验身份证,核验通过后才能购票乘车。如果证票不符,则不允许乘车。
将客户端认证和API Key配合进行访问认证和权限校验才是个更安全的方案。
API权限控制
上图为访问令牌结合API Key的认证鉴权示意图,说明如下:
用户访问的功能权限或数据权限不要交给网关管控,原因是网关仅能支持API Path授权,而实际需要控制的用户权限有很多,如菜单、API、数据等。如果由网关控制用户权限,管少了不满足需求,管多了就要耦合太多应用数据。
因此推荐用户权限由业务系统自行管理维护和控制。每个业务系统内部如果需要控制用户权限,可以建设一个基础权限框架,负责管理权限数据,并提供访问请求拦截和权限检查的SDK给其他应用。
也有部分企业权限管理要求较高,将系统内部的基础权限框架抽取为独立的权限管理服务,由独立团队维护该服务,采用分布式部署+权限缓存的方式保障性能。这样做的好处是权限模型统一、容易对权限变更进行审批控制和审计。缺点是跨团队交互,变更流程复杂。
关于权限管理,是业务系统自治或是集中管控,根据企业自身的需求特点决定即可。
通信安全的方案就是基于传输过程加密的方式,常见的选择就是使用Https协议通信。
微服务架构体系中,逻辑层面上外部请求接入都是通过网关作为入口,网关作为内外网的分界,实际部署上,网关本身也是多实例分布式的高可用部署形态,前面架设有一个负载均衡F5或nginx,用来对外提供Https协议接入和路由转发,而网关内部就是企业内网,默认是可信任的,内网的系统之间的通信会采用更轻量级的HTTP协议。此方案中微服务换成SOA,把网关换成ESB,就是传统的SOA架构中的安全通信方案,本质上没有区别。
示意图如下:
内外网通信协议
为什么用了https就能保证通信安全呢?https是http+ssl,采用密码学手段对通信报文做了加密,使得报文无法被篡改,做到了安全传输,从而保障了通信安全。关于https原理和负载均衡器证书配置相关资料网络上有很多,请大家即用即查。
敏感配置加密:上述各种服务安全场景和方案聊了那么多,大家发现保存好令牌、密钥、密码是一切安全的前提。这些东西千万不能外泄。要保证密码不泄露的办法就是做好敏感数据保密,技术手段上则要求存储密码、凭证的地方(配置文件和数据库表)需要加密存储。如:配置文件中的数据库口令、数据表中存放的密码数据等
代码质量管理:建议在开发期对于编码规范进行制定,还可以通过工具进行辅助检查和控制,如开源的代码质量管理工具Sonar,可以支持多种程序语言,方便的与编译构建工具集成如Maven,在代码进入正式提交对应分支前就将一些安全问题在前期预防,如SQL注入等。
运行时安全扫描:测试阶段,可以通过安全扫描软件,如AppScan 等工具对业务系统的前后端功能进行扫描,检查系统漏洞并即时修复。尽量减小在上线运行后出现安全事故的风险。
运维管理安全方面,根据安全需求,要有安全相关的管理规范和工具支撑,对系统管理、权限分配和关键数据进行严格管控,并做好操作审计日志记录。
比如很多安全级别高的行业或企业中如军工类,对于业务系统的修改、权限管理审计做了严格的流程规范和功能支撑。如典型的三员管理,采用三权分立、互相制约的思路,包含系统管理员、安全管理员、安全审计员三个角色,互相能看到对方的信息,将业务过程分成不同的段,每段由对应人员负责,不让任何人掌控全局。
审计工作是非常重要的一环,没有任何系统和流程是绝对安全的。关键的操作、数据变化等审计日志需要完整记录。一旦发生问题,可以通过审计日志排查分析追踪。常见内容举例如下:
以上这些审计方面的工作中,如果是基于API相关的审计信息记录,如边界交互报文数据,建议基于统一的技术框架进行记录管理。一些内部实现方法,则可以采用接口、方法上加注解,AOP拦截后记录的方案。其他情况可根据实际需求设计审计数据存储的方案。