前言

现在越来越多的公司以 API 的形式对外提供服务,这些 API 接口大多暴露在公网上,所以安全性就变的很重要了。最直接的风险如下:

  • 非法使用 API 服务。(收费接口非法调用)
  • 恶意攻击和破坏。(数据篡改、DOS)

因此需要设计一些接口安全保护的方式来增强接口安全,在运输层可添加 SSL 证书,上 HTTPS,在应用层主要是通过一些加密逻辑来实现。目前主流的两种是在 HTTP Header 里加认证信息和 API 签名。

HTTP 简单身份认证

在 HTTP 请求的 Header 中添加认证字段例如:

Authorization: 3F2504E04F8911D39A0C0305E82C3301

服务器处理前取出该字段进行校验即可。

Spring Boot 项目直接实现一个拦截器就可进行判断:

String token = request.getHeader("Authorization");
if (!Strings.isNullOrEmpty(token)) {
hasAuth = redisTemplate.hasKey("userToken:" + token);
}

这类方法实现比较简单,可以做基本的身份认证,防君子不防小人,可通过中间人攻击获得 Authorization。使用 HTTPS 安全性会得到提高,但是无法抵御重放攻击造成的影响,例如 DDOS。

API 签名认证

API 签名的方式较前一种要复杂的多,但是可解决的安全问题也更多:

  • 可校验请求者的合法性。
  • 可以校验参数的完整性和是否被篡改。
  • 可以防止重放攻击。

part1:请求端加密

API 使用者会获取到服务器颁发的 ak 和 sk 两个秘钥,ak 为公钥,sk 为私钥。

签名有以下规则:

  1. 约定请求时会携带 ak 作为参数并放入 HTTP Header。
  2. 请求参数处理:
    • GET:取出所有的参数,并根据 key 进行字典排序,拼装成如下格式。
    • POST:如果是 application/x-www-form-urlencoded,直接取出和 GET 参数进行排序拼接,如果是 application/json,则直接将整个 json 串 md5 加密后再 base64。
GET:
strToSign = uri + "\n" + ak=ak&k1=v1&k2=v2&k3=v3
POST:
strToSign = uri + "\n" + ak=ak&k1=v1&k2=v2&k3=v3 + "\n" + base64(md5(json_body))
  1. 使用 HmacSHA256 算法,传入 sk 进行签名计算,sign = base64(HmacSHA256(K,strToSign)),其中K=sk
  2. 组装 HTTP 请求,将 X-Ca-Key=ak,X-Ca-Signature=sign 添加到 HTTP Header 中进行请求。

一个简单实例如下图所示:

part2:服务端解密

同样的,服务端获取到请求的 ak 后,查询出对应的 sk,使用相同的规则进行签名计算,计算出的 sign 和传入的 sign 比对,就能够知道参数是否被篡改。

经过这样签名方式后,可以保证上文提到的,校验请求者的合法性和校验参数的完整性和是否被篡改。但是如果有人施加一个中间人攻击,就可以获取到请求报文,即便攻击者无法破译出签名规则,也可以将请求重放,也就是原封不动提交给服务器,那么如果发起恶意大规模攻击,就会使服务器产生拒绝服务。

更进一步

如果需要的安全性更高,可以采用 timestamp 和 nonce 来解决这个问题。

  • timestamp:很好理解,调用者传入,服务端判断,一段时间失效。
  • nonce:在信息安全中,nonce 是一个在加密通信只能使用一次的数字。

单一使用 timestamp,可以一定程度上减少重放攻击的频率,但是无法完全遏制。

单一使用 nonce,咋一眼看可以保证请求的唯一性,但实际上服务端,随着时间推移服务端无法存储大量的 nonce,需要进入淘汰环节,一旦旧的 nonce 被淘汰,那么攻击者依旧可以卷土重来进行重放攻击。

因此,将两者结合一起来就是最终的方案,服务端首先验证 nonce 是否存在,再校验时间戳是否在规定的期限内。如果旧 nonce 被清理,也有时间戳进行把关,使得请求无法被重放。

part1:请求端生成 timestamp 和 nonce

生成时需要保证短时间内生成 nonce 的唯一性。

将 timestamp 和 nonce 写入 HTTP Header 中。

part3:服务端校验

  1. 数据库查询请求带上的 nonce 是否存在(推荐使用Redis,自带TTL功能)。
  2. 如果不存在,且请求时间有效则为合法请求,同时将 nonce 写入,并记录时间;如果不存在,且请求时间超出规定时限,判断为恶意请求。
  3. 如果已经存在,判断为恶意请求。

做足以上这几部基本上已经可以保证一定的安全性。当然还有更复杂的,可以阅读阿里的 Open API 签名文档,根据项目自身对于安全性的需求可以适当进行简化。本文讲到的基本逻辑就是根据阿里的来的。

API 签名与 HTTPS

这边还想提一下 HTTPS。之前看到一则知乎上的提问:使用了 https 后,还有必要对数据进行签名来确保数据没有被篡改吗?

总结一下就是:

  • API 签名保证的是应用的数据安全和防篡改,并且可以作为业务的参数校验和处理重放攻击。

  • HTTPS 保证的是运输层的加密传输,但是无法防御重放攻击。

换句话说,HTTPS 保证通过中间人攻击抓到的报文是密文,无法或者说很难破解。但仍然可以将报文重发,形成 DDOS。同时,如果不签名,只用 HTTP 简单认证,通过抓包,直接可以获取到 Authorization,就可以随意发起请求了。因此最安全的方法就是结合 HTTPS 和 API 签名。

总结

本文主要介绍了当前主流 API 签名方式,可以根据项目场景去挑选组合合适的方案。

博客还附带实现了一个根据上文规则描述的工具类,可以用于API签名,可见下面源码链接。如果需要使用 timestamp 和 nonce 的可在此基础上将这两个字段添加到 sortedMap 中一起拼接,并集成Redis。

源码

参考文献

聊聊 API 签名方式的更多相关文章

  1. spring jwt springboot RESTful API认证方式

    RESTful API认证方式 一般来讲,对于RESTful API都会有认证(Authentication)和授权(Authorization)过程,保证API的安全性. Authenticatio ...

  2. 阿里云视频直播API签名机制源码

    阿里云视频直播API签名机制源码 本文展示:通过代码实现下阿里视频直播签名处理规则 阿里云视频直播签名机制,官方文档链接:https://help.aliyun.com/document_detail ...

  3. 阿里云 API 签名机制的 Python 实现

    在调用阿里云 API 的时候,最让人头疼的就是 API 的签名(Signature)机制,阿里云在通用文档中也有专项说明,但是仅仅有基于 Java 的实现代码示例.所以这里基于 Python 来分析下 ...

  4. 聊聊API网关的作用

    p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 10.5px "Trebuchet MS" } p.p2 { margin: 0.0px ...

  5. ClickOnce 发布WinForm应用程序(非签名方式)

    ClickOnce IIS7发布WinForm应用程序,非签名方式(不勾选签名中的"为ClickOnce清单签名") 一.在D盘上建一个文件夹”MyAppPath”.      该 ...

  6. 配置Struts2及Struts2访问servlet api的方式

    Struts2的起源与背景 在很长的一段时间内,在所有的MVC框架中,Struts1处于绝对的统治地位,无论是从市场的普及范围,还是具体的使用者数量. 其他MVC框架都无 法与其相比,作为一一款优秀的 ...

  7. 访问API的方式为:localhost/api/customers, 创建自定义JSON格式化器

    注意的是,访问API的方式为:localhost/api/customers,在实际中将要根据情况替换合适的端口,默认所有的WEB API都是通过/api根目录的方式访问的 创建自定义JSON格式化器 ...

  8. Android 查看Apk签名方式V1和V2

    Android 查看Apk签名方式V1和V2 java -jar apksigner.jar verify -v my.apk -- Verifies Verified using v1 scheme ...

  9. 如何设计提高服务API的安全性(二)API密钥方式详解

    在上文已经讲述了基础介绍,这篇文章详细讲解API密钥方式. 利用何种加密方式呢? 经过上面加密算法的理解,单向加密不仅性能高,而且有压缩性,即长度一致,有效减少网络传输过程中的字节大小.适合我们这种调 ...

随机推荐

  1. openssl使用

    一. 加密方法 dsaffdfd fgggg 1.对称加密: 加密算法 + 口令 加密算法: DES(56bits),3DES(用des加密反复加密三次),AES(128bits),Blowfish ...

  2. 自动发现项目中的url

    def check_url_exclude(url): """ 判断url是否需要自动被发现,如果不是则移除 :param url: 自动发现的url :return: ...

  3. Reinforcement Learning for Self Organization and Power Control of Two-Tier Heterogeneous Networks

    R. Amiri, M. A. Almasi, J. G. Andrews and H. Mehrpouyan, "Reinforcement Learning for Self Organ ...

  4. UI自动化通过文字、父子元素,兄弟元素定位

    在百度首页,通过文字,父子元素,兄弟元素进行定位 一.文字定位: 通过界面上的文字进行定位,注意如果同一个页面上存在多个同样的文字的情况,返回的值会是多个,建议只存在一个情况下才用这方法. 如:定位百 ...

  5. [BZOJ2438]杀人游戏

    Description 一位冷血的杀手潜入 Na-wiat,并假装成平民.警察希望能在 N 个人里面,查出谁是杀手.警察能够对每一个人 进行查证,假如查证的对象是平民,他会告诉警察,他认识的人, 谁是 ...

  6. python——列表操作函数和方法

    1.添加新元素 1.1 append()函数 描述:append() 方法用于在列表末尾添加新的对象. 语法:list.append(obj) 参数:obj -- 添加到列表末尾的对象. 返回值:该方 ...

  7. 【学习总结】Python-3-Python数字运算与数学函数

    菜鸟教程-Python3-Python数字 注:这一节链接中的内容比较多,表格中的具体函数耐心点进去看看 1-变量在使用前必须先"定义"(即赋予变量一个值),否则会出现错误 2-不 ...

  8. java.net.ProtocolException: Exceeded stated content-length of: '13824' bytes

    转自:https://blog.csdn.net/z69183787/article/details/18967927 1. 原因: 因为weblogic会向response中写东西造成的,解决方式是 ...

  9. Java中的关键字--synchronized

    在并发编程中,synchronized关键字是常出现的角色.之前我们都称呼synchronized关键字为重量锁,但是在JDK1.6中对synchronized进行了优化,引入了偏向锁.轻量锁.本篇介 ...

  10. 2018-2-13-win10-uwp-分治法

    title author date CreateTime categories win10 uwp 分治法 lindexi 2018-2-13 17:23:3 +0800 2018-2-13 17:2 ...