服务器间API通信时的身份验证

在企业级应用中,将服务拆分解耦是很常见的,所以也就有了服务器间调用API的场景。

一般会将提供基础能力的服务独立部署,然后前端业务应用通过API去调用这些基础能力。由于前端业务应用和基础服务一般是多对一的关系,故在调用API的时候,前端业务应用需要标识身份,以便基础服务能够针对性地提供服务。

设定个场景

先具象化的设定一个场景,后面比较容易说清楚:

服务S提供了一个短信发送的API,即调用此服务可以实现给指定号码发送短信。有A、B、C业务应用会使用这个服务,且服务S需要知道哪些业务调用了它。

这个服务的API调用方式是通过HTTP的GET方式(不要吐槽这个,这是确实可行的)

http://service.domain.com/sms?
  number=17012345678&
  content=helloworld

简单的方式

如果A、B、C和S在同一个私网内,且API访问仅限此网内,A、B、C也均可信可控,那么根本不用麻烦,只要加上一个标识参数告知S即可。看起来就像这样:

http://service.domain.com/sms?
  number=17012345678&
  content=helloworld&
  appId=appNameA

使用Token

如果业务部门比较分散,导致A、B、C并不完全可信,不排除会出现B使用A的appId的这类冒名的情况。

那么S可以给A、B、C分别预先生成一个Token,要求在请求时一并发送,并会校验appId和token是否匹配。看起来就像这样:

http://service.domain.com/sms?
  number=17012345678&
  content=helloworld&
  appId=appNameA&
  token=0UW2m6Cpu9JdrM4muXHVBTOQMb4MG9nJ

这样,各业务就不能冒用标识了。

使用Signature (签名)

Token相当于是一个密码,那么上述的方式等于将密码明文传输了,不是太妥当。所以可以再改进一下:

  • 将appId和token作为字符串连接,进行一次SHA1计算(MD5也行),生成一个signature;
  • 不再传输token,而是传输appId和signature;
  • S收到请求后,通过appId和token的作同样计算,校验signature是否一致。

所以请求就变成这样:(这里用了SHA1计算)

http://service.domain.com/sms?
  number=17012345678&
  content=helloworld&
  appId=appNameA&
  signature=6f3db6934eeb685cfdb2295c35856f00ebea29a3

加入时间戳

如果私网内存在不可控的服务器或者干脆就是在公网上通信,那目前实际上仍然不是非常安全,因为上述的appId和signature在每次调用的时候是不变的,如果被非法调用者得知,仍然可以冒名。再进一步改进:

  • API增加一个必选参数timestamp,即当前时间的Unix时间戳,单位到秒;
  • 同时,要求使用appId、timestamp、token三者相接计算signature;
  • S收到请求后,不仅校验signature是否一致,还校验时间是否为当前时间。(由于各服务器时间存在误差,所以这里实际是比较时间戳和当前时间是否在一个范围内,在此设为1分钟)

请求就进化为:

http://service.domain.com/sms?
  number=17012345678&
  content=helloworld&
  appId=appNameA&
  timestamp=1502610966&
  signature=ff0447ab272947edd965df6d2ef19576eabb3fe9

这样,即使签名被非法得知,也仅仅能在设定的几分钟范围内被调用,大大降低了风险。

限制Signature重复

当然,最好的情况是完全杜绝被非法调用。可以进一步处理:

  • 在S暂存每次请求的signature,保证不能重复使用。每个signature暂存时间为之前设定的时间范围1分钟。
  • 如果是低频API,每秒调用最多调用一次的,不受影响。
  • 如果是高频API,则需要保证每次的签名不同,不然在同一秒的请求会被受限;可以再增加一个noise的字段,值为随机字符串(一般为4位字符),并加入到signature计算中。

所以,像这样请求:

http://service.domain.com/sms?
  number=17012345678&
  content=helloworld&
  appId=appNameA&
  timestamp=1502610966&
  noise=xWk2&
  signature=c2b7e467a7bd14bf2ef768702be1c7f6f95a2d09

再加上S上做的限制signature重复使用,可以保证signature泄露的时候不会造成非法调用。

参数防篡改

还有一种更糟的情况,就是A、B、C发往S的请求被劫持,劫持者修改了手机号码和短信内容,再发往S。这样,signature是不会重复使用的,仍然能够通过校验。

所以更好的办法是,把业务参数即number和content的值也加入到signature计算中。这里需要注意的是,为了更通用以及确保字符串连接的顺序一致,须按照参数名对除signature以外的所有参数(包括token)进行一个排序,然后将其值连接。

拿例子来说,排序好是这样:

http://service.domain.com/sms?
  appId=appNameA&
  content=helloworld&
  noise=xWk2
  number=17012345678&
  timestamp=1502610966&
  (token=0UW2m6Cpu9JdrM4muXHVBTOQMb4MG9nJ)

然后将appNamehelloworldxWk21701234567815026109660UW2m6Cpu9JdrM4muXHVBTOQMb4MG9nJ这样一个字符串做SHA1计算,得到最终的请求:

http://service.domain.com/sms?
  appId=appNameA&
  content=helloworld&
  noise=xWk2
  number=17012345678&
  timestamp=1502610966&
  signature=76168273fd018b89df674d5275a6c16f3daf9b10

大杀器

如果A、B、C三者的网路环境不复杂,可以固定IP的话,在S上通过IP来验证即可。轻松加愉快。

上述内容中一些计算方法:(NodeJS)

//计算token和noise
function generateToken(len, radix) {
  var chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'.split('');
  var token = [], i;
  radix = radix || chars.length;
  if(!len) len = 32;
  for (i = 0; i < len; i++) token[i] = chars[0 | Math.random()*radix];
  return token.join('');
}

//计算签名
const crypto = require('crypto');
function sha1(input){
  return crypto.createHash('sha1').update(input).digest('hex')
}

本文同步自本人博客:https://phxsun.com/post/authentication-between-servers

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 229,406评论 6 538
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 99,034评论 3 423
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 177,413评论 0 382
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 63,449评论 1 316
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 72,165评论 6 410
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 55,559评论 1 325
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 43,606评论 3 444
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 42,781评论 0 289
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 49,327评论 1 335
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 41,084评论 3 356
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 43,278评论 1 371
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 38,849评论 5 362
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 44,495评论 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 34,927评论 0 28
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 36,172评论 1 291
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 52,010评论 3 396
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 48,241评论 2 375

推荐阅读更多精彩内容

  • iOS网络架构讨论梳理整理中。。。 其实如果没有APIManager这一层是没法使用delegate的,毕竟多个单...
    yhtang阅读 5,231评论 1 23
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,807评论 18 139
  • 1. 微服务架构介绍 1.1 什么是微服务架构? 形像一点来说,微服务架构就像搭积木,每个微服务都是一个零件,并使...
    静修佛缘阅读 6,663评论 0 39
  • 1、开启公众号开发者模式 公众平台的技术文档目的为了简明扼要的交代接口的使用,语句难免苦涩难懂,甚至对于不同的读者...
    good7758阅读 1,538评论 0 1
  • 一、概念 二、代码
    liyuhong阅读 190评论 0 0