hmac sha256安全吗_【附近的人】系列之API安全(一)

我是老李,大家好,众所周知【附近的人】系列有一段时间没有更新了,搞得好像太监了似的,然而并没有...这一系列只是周期不太稳定、不太调而已,原因说来倒也简单:

  • 一来是毕竟我有我自己的安排(主要是懒)

  • 二来毕竟是免费的(不收钱,说话就是硬气!)

当然了,上述两条都是扯,主要是我想告诉你们的是:自己的事情一旦想好了,节奏自己一定要自己把握,你可以听取别人的意见,但绝不能被其他影响到自己节奏把控甚至最后改变想法。其实,我还是很勤奋的,不信看github,不能说每天一点绿吧,至少一周还是有两三点绿的...

涡轮增压短期猛搞乃一时躁,大排自吸长期持久为永恒王道

其实很久(大概六个月)之前,本篇标题里的内容我就简单讨论过一些关于API安全的内容。只不过,毕时过境迁、如白驹过隙、任时光荏苒、怎物是人非,任由岁月在我稚嫩的脸庞上留下时光的痕迹,自那琐碎如捧在手里的水从间隙中流走,就在这个时候一旁边打游戏边撇我写文章的永强说[ 你TM这两天看看多了吧,给人投稿呢? ]听到他的这句话后的我立马停止了这种文风回归到以前,(承接上文)如今正好再来一波儿放到【附近的人】服务系列文章中由其自成一章,并与尼古拉斯永强的安全系列文章环环相扣,想必定能承上启下、承前启后、画龙点睛[ 就在这个时候永强又撇了过来说了一句:你TM这是在你们公司和他们玩成语接龙玩石乐志么? ]于是我就停了不再继续写成语了...

首先,在山人看来,API安全主要是指两个方面:

  • 一来是API本身尽可能低概率的被滥用:这里主要是指被人给扒裤衩了恶意调用了,curl随便搞,脚本嘻唰唰,甚至第三方客户端

  • 二来是API和端飞数据尽可能低概率被其他人窥探到:这里主要是说飞过来的参数抑或飞回去的处理结果,能加密最好加密

  • 三是防刷限流,不过我打算把限流单独拿出去说下

请注意,前两条里我都用到了【尽可能低概率】这几个字,而不是类似于【三招教你杜绝xxx】的口吻。

在正式开始之前,我觉得我们还是有必要聊一聊token。API年代,不再像以前MVC套模板那种万金油套路了,讲究的是前后端分离,不分不高端,不分不舒服斯基。

只不过PC年代用户登录后,用户的登录凭证一般都是利用session/cookie(这里把session和cookie放到一起了,因为绝大多数情况下session需要依赖cookie得以实现)这种方式,因为客户端大多都是浏览器,而session/cookie可以说是为浏览器而生;而到了移动互联网年代,一般都是前后分离的纯API,在C/S里还要继续使用cookie承载用户登录凭证不太合适,这会儿大家都会用token来代替session/cookie...当然了,非要在APP端里用cookie/session。。。。。。(没太搞明白session机制的同学有兴趣可以看下本次推送的第二篇文章或者链接https://t.ti-node.com/thread/6445811931751120897)

a3d9c29bdbf20232065f7447e4c9041f.png


先聊聊TOKEN

我觉得在此有必要先聊一聊token的常规使用流程,其实token本质上就是我们又重新发明了一个自定义版的session,这句话你们好好琢磨琢磨。当前市面上,由于JWT的诞生出现,使得几乎在所有讨论有关token的地方都会自动分成两大派:

  • JWT,token压根不用单独存储处理的,直接用,不香吗?

  • token和用户信息(主要是uid)自己生成token,以kv方式存储到kv介质中去,对token掌控更多,总之就是自己发明的野路子

这两派人士往往会在出现token这种技术方案的任何场所中进行友好的磋商和探讨,使得智慧的火花在碰撞中不断产生:

24087ef7530a5c3a07d58d27aaec8755.gif

正如上图所示:讨论将会在一篇祥和的氛围中结束,双方一起总结过去token方案的成就与不足,展望未来一年token方案的方向和目标!

所以,我就干脆两派的方案都尬聊一下得了...

先说下JWT的简单构成:header + body(专业说法叫payload) + (header+body的消息认证码),按理说就是这么简单,实际上也可能真的就是这么简单。只不过关于消息认证码,这个我非常非常强烈建议你们抽空看下《我赵永强又回来了:单散、认证与数签(五上)》,永强在这篇里用非常粗暴方式快速介绍了消息认证码,这里我就不在当复读机了。

根据jwt的构成原理,我这儿用PHP码了一坨简单的demo代码,你们先复制粘体走感受下:

<?php $hash_alg = 'sha256';$password = 'wahaha';function jwt_encode( $data ) {  return rtrim( strtr( base64_encode( $data ), '+/', '-_' ), '=' );}function jwt_decode( $data ) {  return base64_decode( str_pad( strtr( $data, '-_', '+/' ), strlen($data) % 4, '=', STR_PAD_RIGHT ) );}/*    ------------ JWT header ---------------- */$jwt_header_arr = array(  'alg' => $hash_alg,  'typ' => 'JWT',);/*    ------------ JWT body ----------------    其中,前七个都是jwt官网规定必须规范字段    最后一个uid是我自己搞的撒... */$jwt_body_arr = array(  'iss' => 'admin',  'exp' => time() + 600,  'sub' => 'test',  'aud' => 'every',  'nbf' => time(),  'iat' => time(),  'jti' => 10001,  'uid' => 32142353484,);$jwt_header = jwt_encode( json_encode( $jwt_header_arr ) );$jwt_body   = jwt_encode( json_encode( $jwt_body_arr ) );// 利用hash_hmac计算出jwt_header与jwt_body的消息认证码$jwt_sign   = hash_hmac( $hash_alg, $jwt_header.'.'.$jwt_body, $password );$jwt_token  = $jwt_header.'.'.$jwt_body.'.'.$jwt_sign;echo "下面是生成的jwt_token".PHP_EOL;echo $jwt_token.PHP_EOL;echo "下面解析并校验jwt_token".PHP_EOL;$jwt_token_arr = explode( '.', $jwt_token );$jwt_body_json = jwt_decode( $jwt_token_arr[ 1 ] );echo $jwt_body_json.PHP_EOL;

运行一下出了个结果,你们感受下,感觉和jwt挺相似的,应该是成功了:

a92dc96540ad7ca9a3491ab97dee13ee.png

所以用的时候咋用?就按照上述代码相反的算法来一波儿就行,确切说就是先base64_decode一下,然后在json_decode,但是这就可以了?当然不是,因为还需要利用最后的消息认证码来对数据完整性进行确认(注意上述demo代码里并没有实现对数据完整性进行校验的代码,你们自己补一下吧...),其实你可以粗暴理解为【验签】~

用法就是:

  • 客户端通过用户名密码登录,获取到服务端生成的jwt

  • 客户端把jwt存起来,访问API的时候带过去,一般说来不成文的规定就是放到http header中去,当然你扔到http body体甚至query string里都没人管你

  • 服务端收到请求后,取出jwt,然后decode并校验jwt数据完整性,然后从jwt_body里可以拿到uid或者是你们规定的其他信息,顺利识别出用户是谁

听起来好像还不赖,反正能用...那么,在此之前或者说与jwt并不完全一样的token思路,比如有这么实现的:就是利用AES加密(对AES加密需要简单了解下的同学请在公众号中抠“加密”两个字)。我写个简单的demo你们复制粘贴走感受下,主要是思路(请务必仔细看注释),你不一定也非要用imei和uid,你可以用任何【非敏感】数据来实现这个大致思路:

<?php /*  (imei和token都假设在http header中)  思路就是拿设备imei当作aes加密密钥,给uid+imei进行加密,结果当token  用户登录成功后,把这个token返回给客户端  客户端保存好token,访问接下来api带上这个token以及imei  API收到token后,利用imei进行解密,如果【能解密成功并且解密后的imei等于  API访问提交上来imei】注意这里要当两个条件同时并列成立,就算是有效token*/// 采集到的设备id$imei = 'MD101C4MX30I95ZX6A';// 用户uid $uid = 4123456767;// aes加密采用具体分组算法$aes_method = 'aes-256-cbc';// 密钥$key  = $imei;$data = $imei.$uid;// aes-128-cbc分组加密算法需要的iv向量的长度$iv_length = openssl_cipher_iv_length( $aes_method );// 根据长度生成相应iv$iv = openssl_random_pseudo_bytes( $iv_length, $cstrong );echo "明文:".$imei.$uid.PHP_EOL;$enc_data = base64_encode( openssl_encrypt( $data, $aes_method, $key, OPENSSL_RAW_DATA, $iv ) );echo "秘文:".$enc_data.PHP_EOL;$dec_data = openssl_decrypt( base64_decode( $enc_data ), $aes_method, $key, OPENSSL_RAW_DATA, $iv );echo "解密:".$dec_data.PHP_EOL;// 注意!⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️// 我偷懒了,依然没有写校验部分,很简单你们自己补充下哈...

保存一下,运行结果就是这样shai儿:

35b5d6f2c65e99a1fd8d8e75557ba96e.png

这个思路和jwt不一样的就是:jwt的payload部分实际本质上是明文,一次base64-decode你就可以看到明文是什么,所以你一定要通过hash_hmac系列函数来对中间的payload进行数据完整性校验,从而保证payload被篡改后立马就会无效;而加密的方案相当于对payload进行aes加密,如果payload被篡改,解密会失败且校验无法通过,如果说token泄露被copy走了,那么由于imei发生变化,也会导致token校验失败从而杜绝信息泄露,除非这家伙猜到了我们用imei参与了token运算。

除此之外,还有一些方案就是将token和用户信息以[ key : value ]的方式存入到redis或memcache中去,API收到token后会从redis或memcache中获取到用户信息...

在token的生成方式以及使用方式上叨叨这么多,其实就是想说明一个问题:没有对和错,方法多种多样,你可以博采众长,最终能用就行...(如果你们有什么好的想法或者指出我的错误,请后台留言我会在下篇贴出来的)

8925eba7807f8dbb5d87e4eff575e835.png

未完待续...

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值