写点什么

服务老是被攻击,如何设计一套比较安全的接口访问策略?

  • 2023-03-03
    湖南
  • 本文字数:2152 字

    阅读完需:约 7 分钟

大家好,我是老王,最近接手了一个项目,兴致勃勃的准备大干一场,结果一顿渗透测试下来我都快傻了。什么防重放攻击,请求体篡改,越权攻击,都整上来了,好嘛,我都不清楚这个项目这半年是怎么度过的。


不知道大家公司对接口安全这块是怎么考量的,但是对于面向公网提供服务的产品来说,这个可以说是很致命的了。那么,该如何设计一套比较安全的接口访问策略呢?

一、Token 与签名

一般客户端和服务端的设计过程中,大部分分为有状态和无状态接口。一般用户登录状态下,判断用户是否有权限或者能否请求接口,都是根据用户登录成功后,服务端授予的 token 进行控制的。但并不是说有了 token,请求就是安全的,那么万一 token 泄露了怎么办呢?谁都可以调用我的服务了吗?


就比如去银行取钱,需要用到银行卡。但不能因为你不小心把银行卡弄丢了,捡到的人就可以拿着你的银行卡去银行办理业务了。他们还需要对你的身份进行验证,你也要知道银行卡密码。


所以 token 只是用户权限以及会话的凭证,除了会话的凭证,我们也要校验请求的合法性,以防止 token 泄露而导致客户的损失。


而签名摘要计算则是用来进行请求合法性校验。经常同学们会把这两种搞混。以为使用 token 就是安全的。

二、签名计算设计

签名摘要的计算一般分为签名值和签名的 key,signature 生成方式如下:

signature = Base64(HMAC-SHA256(LOWER(MD5(key)), StringToSign))
复制代码

2.1 签名字符串的设计

设计签名字符串时,我们要想好,我们要针对不同的攻击策略,设计什么样的请求头部


常用的签名字符串设计如下:

StringToSign =  Content-MD5 + "\n"               + CanonicalizedHeaders
复制代码

CanonicalizedHeaders 构建方法如下:

  1. 以 service 为前缀的的 Headers,但是不包括 service-signature,如下:

  • service-nonce: 客户端生成 32 位随机字符串,所有客户端 5 分钟内不能重复,重复时平台回复 nonce 重复,客户端需要重新请求。

  • service-date:请求生成的时间,与服务器本地时间差超过 5 分钟,认为鉴权失败。

  • service-session-id:客户端会话 id,用于本次登录后的所有请求会话标识。

  • service-client:客户端信息,包括客户端类型、客户端版本、操作系统等。

  1. Header 名称全部小写,值前后应不包含空格

  2. Header 的名称和值之间用“:”相隔,组成一个完整的 header

  3. 根据 header 名称的字符顺序,将 header 从小到大进行字典排序,每个 header 之后跟一个“\n”


StringToSign 生成示例:

eB5eJF1ptWaXm4bijSPyxw==\nservice-client:ewogICAgImRhdGEiOiB7CiAgICAgICAgImNsaWVudFR5cGUiOiAieHh4IiwKICAgICAgICAiY2xpZW50VmVyc2lvbiI6ICIzLjAiCiAgICB9Cn0=\nservice-date:2022-07-22T14:43:07Z\nservice-nonce:d36e316282959a9ed4c89851497a717f\nservice-session-id:0123456\n
复制代码

2.3. 签名 key 设计

一般客户端请求的接口类型有三种,分别是有登录状态和无登录状态以及登录这个特殊接口。无登录状态前的接口都是服务为了提供能力而做的一些接口,相比而言都是提供通用能力的。不涉及客户个人信息相关。安全风险较小。用户登录后的接口一般都是涉及到客户信息的接口,隐私泄露风险较大。因此对于每一种状态可采用不同的 key 值设计。讲风险降到最低。

2.3.1. 用户未登录签名 key 值

用户未登录时,平台提供通用能力,调用服务端接口时使用约定好的固定 SK 进行接口签名鉴权即可。固定 SK 由后台提供 16 位的随机字符串。

2.3.2. 用户登录签名 key 值

我们知道,所有在客户端和前端保存的 key 值永远不是最安全的,有可能被拆包而发现对应的加密 SK,从而被不法分子破解,因此用户登录时,传输密码时,如果使用固定 key,有可能 body 体被解开,密码被泄露的风险。


登录时用户会输入密码,而服务端也知道用户加密后的密码,那么使用用户输入的密码当做 key 则是最安全的方式。并且交互过程中不需要将用户密码放在 body 体重传到后端进行校验,只需校验签名的准确性即可。这样就可以极大的增加用户密码的安全性。


用户登录验证时会输入用户的密码,则登录时用户密码作为签名的 key 进行鉴权校验。加密 key 为:SHA256(LOWER(MD5(passwd)),salt),salt 为用户的盐值,可以使用用户的手机号。

2.3.3. 用户登录签名 key 值

用户登录成功之后,所有接口都要有鉴权,为了每个用户安全起见,针对每个用户颁发自己的 SK,登录成功后获取。这样客户端将 sercet 保存到内存中,可以有效防止 SK 泄露。

  1. 用户注册成功后,针对用户账户后台生成 16 位的 sercet

  2. 用户登录成功后,后台返回用户的 sercet

  3. 登录之后的接口鉴权使用 sercet 进行鉴权。

三、接口合法性校验流程

请求合法性校验流程如下:


1、首先对接口的请求头的 nonce 进行 5 分钟内是否重复的校验,可以有效的防止重放攻击

2、然后对时间戳进行校验,防止客户端时间篡改攻击

3、之后对请求的 MD5 进行校验,防止对请求体进行篡改。

4、最后将 nonce,date,md5 以及 session 进行组合签名校验,校验签名值是否成功。可以有效防止上面单一修改验证通过,但签名值校验不通过的问题。


基于以上的校验,基本上可以防止大部分攻击场景。当然为了更加安全,你还可以增加黑白名单限制、接口访问限流、用户常用设备绑定、用户异地登录等保护用户财产的安全性设计。


好了,不知道这篇文章大家对安全的设计是否有了更深刻的理解呢?大家有没有更好地接口安全设计方案,可以一起聊一聊哈。


作者:老王的技术笔记

链接:https://juejin.cn/post/7121168651222253599

来源:稀土掘金

用户头像

还未添加个人签名 2021-07-28 加入

公众号:该用户快成仙了

评论

发布
暂无评论
服务老是被攻击,如何设计一套比较安全的接口访问策略?_做梦都在改BUG_InfoQ写作社区