前后端分离和系统安全
前后端分离和系统安全
又有系统爆出安全性问题了....正好,找了个契机,说说前后端分离和系统安全的话题.
常见的前后端分离模式

简单总结一下常见的前后端分离模式的开发和部署情况.
- 前端小组,负责开发模块1,模块2的页面.开发结束以后,直接发布到NG,就能生效.
- 后台人员,一般使用java语言,开发后续的子系统1,子系统2...
- 网关一般使用springCloud Gateway或者Zuul实现,如果需要登录的请求,网关集成验证token功能
在常见的内管系统里面,其实是没有太多问题的,一般安全问题也考虑的比较少.但是,问题就是后面的但是.
如果是一个第三方的公司开发的产品,要部署进内网,一般的公司就要进行安全漏洞的扫描.这种部署结构的玩法一定存在安全问题,无法通过最简单的安全漏洞扫描.
典型的问题
我们举个典型的例子,来具体说明为什么这种模式下面,无法通过安全漏洞扫描.
为了防杠,我们假设有个服务是"删除我的联系人",按照服务模式开发的话,开发的接口很有可能是这样的
public boolean removeContactor(userId)通过上述部署模式暴露出去,方便外部访问,所以最终浏览器传入后台的数据是这样的
url: /xxx/removeContractor
header: token1
requestData: {userId:a1}"删除我的联系人",看意思也是知道,这个接口是需要登录的,所以请求中的token1,就是登录授权后获得的.在上述部署系统中,网关就会检查token1是否合法,如果合法就转发请求到后面的服务,如果没有或者不合法,就跳转登录页面.
userId一般是通过登录以后,在登录用户信息中获取.而且发送请求的是浏览器,请求本身存在被篡改的可能.
假设,a1用户登录的token1是匹配的,那么我们可以保留token1,把userId的数据篡改为a2.请求变更为如下
url: /xxx/removeContractor
header: token1
requestData: {userId:a2}如果按照最上面的部署结构,网关在检查token的时候使不会检查userId的数据和a2是否匹配的.这个请求的token是有效的,可以通过网关的token验证,转发到后台服务,执行的却是删除a2用户的联系人.同理,通过这种篡改,我们可以删除系统内任意用户的联系人.
这个就是常见的提交数据篡改方式,也是漏洞安全扫描的基本检查内容.
这种类型的篡改,直接影响到系统内所有"我的xx"的功能,功能越多,影响越大,修复难度越高.
问题的修复
在应用层面修复,就是修改每个"我的xxx"的功能,将userId不再作为暴露的入参,直接去掉,从token中获取.
因为影响面很大,而且功能单一,是不是可以通过某个地方修改,而全局就修复了呢??
不好意思,没有这种方式.
无法解决的地方不是在于token无法翻译为userId,而在于userId在服务入参中...
这个userId在入参中,会出现一下2种情况,基本上无解
- 命名问题,假设后台服务的调用参数解析是通过参数名解析的,在这个接口内部命名为userId,会不会再另外的接口中被命名为uid,而这种命名的变化需要网关调用服务的时候,报文发生变化
- 因为是入参,没有人说入参的userId是在最外层,会不会userId被一个bean的封装以后,作为属性存在,这种层级的变化,除非有明确的开发规范,并且大家都遵守,否则无从谈起
提示
我也见过在网关上写参数转换逻辑的,这个转换逻辑一般配合规则来使用,而规则的粒度通常需要定位到具体的服务接口.
话再说回来,如果我们真的在应用中,所有的userId字段都不暴露,接口声明变换成如下方式
public boolean removeContactor()
{
userId = findUser(request.getToken);
删除联系人
}如果我们的代码实现换成上述方式,直接在实现中对token获取userId的行为,又有什么问题??
单从业务和功能上是没问题的,也确实修复了安全漏洞.但是,还是那个但是,本来我们认为,通过网关转发的请求,具备一定的通用性,可以通过其他的方式也可以访问.
但是仔细看看这个实现,如果userId去掉,在接口实现里面获取请求头的token,再使用userId进行业务操作,这样的接口还具备通用性么??如果我们再提升一个高度,通过网关转发的接口,可能会具备通用性,所以我们才这样抽象和写业务逻辑,但是一旦加入token机制,这个接口实现就从一个"无状态服务"变成一个"有状态服务",这个服务没有入参,但是会根据请求头的token,具有不一样的返回,已经不能直接被其他应用使用了.
这个又和一般的中间业务技术规范扯上关系了.当然,还有很多公司使用的技术框架和技术规范,压根没考虑到上述东西,乱抄作业.所谓的"中台",还是需要很多技术沉淀,确保落地方向来思考问题.
大厂的标准
据我所知,互联网大厂玩法稍微有点变化

原有的网关会抽象为脱敏层,做的还是网关的活,但不是一个网关的中间件了.过敏层可能是由多个子系统构成,因为和前端的关系更加紧密,目前在很多大厂的项目中,都被前端组包圆了,前端使用nodejs,开发部署在服务器端的脱敏层.
这脱敏层,除了做token验证以外,还做一些重要数据的过滤,比如虽然微信有开发者平台,能拿到各种sessionId,但是能拿到userID么?userID就属于应用不看重的敏感数据.
后端使用java/go专心开发业务逻辑服务,还要不要网关,实际上已经不重要了.(常见的网关提供的功能中,已经被NG和脱敏层替换掉了)
一样的"前后端分离"的说法,实质上已经发生了变化.在技术选型和前期设计的时候,乱抄别人的作业,最终会形成无法绕开的问题.
如果是小厂,我还是建议启用springMVC中的controller机制,在原本springMVC的代码分层中,原本controller就是有一项token验证的,至于访问后台的服务,用什么中间件也不重要了.
受苦受累的程序员啊,作业没抄好的技术负责人,应该拉出去弹jj200下......
