• API:接口安全(权限)验证、JWT


    下午研究了一下JWT,又回顾了了下工作中碰到的一些接口安全性验证,写个小结吧:

    一、后端对后端

    1、IP过滤

    因为很多都是内网项目,直接简单粗暴配置下可访问的IP地址也凑合着够用(局域网没高手潜伏着搞破坏就好。。。),具体就是拦截器里

    通过Web.Config参数判断是否要验证IP或账号密码,直接写死在Web.Config里,各地上线时去配置,基本是配置了内网IP段即可

    public override void OnAuthorization(System.Web.Http.Controllers.HttpActionContext actionContext)
    {
        bool isVerifyIP = Utiliy.Tools.Text.ToBool(ConfigurationManager.AppSettings["IsVerifyIP"]);
        bool isVerifyUser = Utiliy.Tools.Text.ToBool(ConfigurationManager.AppSettings["IsVerifyUser"]);
    
        if (isVerifyIP == false && isVerifyUser == false)
        {
            return;
        }
    
        if (isVerifyIP == true)
        {
            string clientIP = WebApiConfig.GetClientIp(actionContext.Request);
            if (Utiliy.Tools.Text.CheckInChar(ConfigurationManager.AppSettings["AssignIP"], clientIP) == false
                && Utiliy.Tools.Text.VerifyIsLanIp(clientIP) == false)
            {
                throw new HttpResponseException(HttpStatusCode.NotFound);
            }
        }
    
        if (isVerifyUser == true)
        {
            if (actionContext.Request.Headers.Authorization != null)
            {
                string userInfo = Encoding.Default.GetString(Convert.FromBase64String(actionContext.Request.Headers.Authorization.Parameter));
                if (string.Equals(userInfo, string.Format("{0}:{1}", "admin", "XXX")))
                {
                    IsAuthorized(actionContext);
                }
                else
                {
                    HandleUnauthorizedRequest(actionContext);
                }
            }
            else
            {
                HandleUnauthorizedRequest(actionContext);
            }
        }
    }

    2、直接在URL里获取登录账号拼个&UseName=XX,然后根据这个用户名看是否有权限

    呃这个嘛,完全没安全性(传个admin咋整。。。),只是判断内部管理的权限,配合内网IP限制,又是后端对后端的,基本不会暴露出去,凑合吧。

    3、参数拼上MD5同时传进来,比如有个public GetXX(string a, int b, string md5Key),那就是拿 a + b.ToString(),通过公司公共MD5加密(里面封装好了Salt),对方拿到后也同样把 a + b.ToString()进行同样公共方法的加密,看双方md5Key是否一致。

    这个安全性倒是有一定保证,但有好几个衍生问题:

      a.参数顺序不能乱,只能按a、b这样,如果反过来,md5也不一样了

      b.如果不是这种拼简单有限的几个参数,是用实体(实体可能定义了冗余的十几个字段)接收的话,要封装公共方法进行反射循环取值,排序不能乱。按理也是可以,但没试过。

      c.md5也可以反身暴力破解,网上都有免费直接用的,但拼上Salt后应该还好。此方案里不好加时间戳。或者加个分钟的时间戳,后台还要弄个时间段循环比对?比如取前3分钟后3分钟,以分钟字符串去md5,分别比对?效率太低了,不过有些小接口也凑合用就是了。

      d.最大问题,这个封装的md5加密公共方法,是公司内部定义的,和其它合作伙伴或公司等对接,双方无法达成一致的加密,主要是Salt不能共用,自家md5的盐总不能给别人吧。

    虽说有各种问题,但因这个简单有效,所以很多有一定安全要求的内部简单接口,还是会采用这个方案。

    4、直接传个约定好的字符串,该字符串可以用GUID生成,双方都存着,固定附在参数后面进来,固定验证

    和前面参数拼MD5类似,但双方代码更简单,也可以和其它公司对接,聊胜于无。实际也基本没安全保证,一旦被抓包或怎么泄露了,所有接口都能调用了。。。但因为是后端对后端,又多是内网项目,所以有时也会这样用,别人只知道接口几个业务参数,比如单号、名称什么的,这个随机串,没有专业点的抓包什么的,是不会知道。也是寄希望于内网没高手搞破坏吧(有中级IT水平就可能会泄露了。。。)。还是因其简单粗暴,项目试运行期间也偶尔会这样约定,后期再改进(实际一忙可能就一直这样用下去了。。。)

    5、公钥私钥,比较正规,但双方开发都较麻烦,一般用于和其它公司对接(很多是其它公司约定好这种方案,我们沿用)。

    二、前端对后端

    因前后端分离等,有很多专为前端服务的接口(如手机移动端),回忆下日常工作中都是怎么验证的

    因手机端基本都是互联网了,基本不存在内网IP判断了,所以安全性要比后端对后端考虑多些

    1、微信入口(企业号、公众号、订阅号、小程序等)

    先有一层后端,先验证好微信的授权,那一层再调API相当于后端对后端了,具体就用上面几种后端对后端方案了

    2、普通移动端H5页面,手机号注册/验证后,调API取数据

    也有用双方md5后传加密串验证的

    前端加密有个缺点,就是不管用什么加密方式,因为JS最终都是暴露出去的,虽然经过混淆后会较难破译些,也不保险。

    项目有采用这种把数据用JS的md5插件加密,又用了一些数组什么的组成盐,各种混淆,看着基本也没问题,数据又不是什么特别敏感或涉及金融的,所以这种方案也不时有用。

    后来同事还改进过,可传时间戳,后端循环验证,3分钟还是5分钟内有效

    3、先登录,不管是账号密码,还是扫码,还是短信验证码,总之登录完会返回一个token,请求时把这个token附上就可以。每次后台会判断,如果快过期了,会生成个新的token,返回给前端,下次前端就要用新的token访问了。只要若干天内有登录过就会重新刷新,不影响正常操作的流畅性。

    其实oAuth2、JWT这些基于Token(AccessToken、RefreshToken)的,基本也是这套路了,只不过封装得更好。

    4、JWT

    本想着重写下JWT的心得的,但其实是差不多的:

    JWT 标准的 Token 有三个部分:
       header(头部)
       payload(数据)
       signature(签名)
       中间用点分隔开,并且都会使用 Base64 编码,所以真正的 Token 看起来像这样:
    eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJuaW5naGFvLm5ldCIsImV4cCI6IjE0Mzg5NTU0NDUiLCJuYW1lIjoid2FuZ2hhbyIsImFkbWluIjp0cnVlfQ.SwyHTEx_RQppr97g4J5lKXtabJecpejuef8AqKYMAJc

    就是把单纯的验证,能多传一些其它参数,而且更正规

    header只是声明是什么样的加密方式

    payload是客户端自已想传的一些额外参数,通常是账号相关信息

    signature就是把上面两个的base64进行加密(用的算法就是header中声明的那个)

    最后生成的token就是把这三个依次拼起来,分隔符是 . (英文的点)

    客户端使用用户名密码进行认证
    服务端生成有效时间较短的 Access Token(例如 10 分钟),和有效时间较长的 Refresh Token(例如 7 天)
    客户端访问需要认证的接口时,携带 Access Token
    如果 Access Token 没有过期,服务端鉴权后返回给客户端需要的数据
    如果携带 Access Token 访问需要认证的接口时鉴权失败(例如返回 401 错误),则客户端使用 Refresh Token 向刷新接口申请新的 Access Token
    如果 Refresh Token 没有过期,服务端向客户端下发新的 Access Token
    客户端使用新的 Access Token 访问需要认证的接口
    
    作者:simpleapples
    链接:https://www.jianshu.com/p/25ab2f456904
    来源:简书
    简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。

    前后端要注意封装和判断下

    后端:1、验证AccessToken,如过期,则用RefreshToken去获取新的AccessToken

      2、每次都判断下RefreshToken,如果快过期了,则刷新下RefreshToken,返回新的RefreshToken、AccessToken。每次去数据库取有效期不值得,可用Redis缓存着  

      3、过期不过期干脆由前端判断。一种是前端自已判断快过期了,先调下刷新token的接口。另种是前端把过期时间通过payload传进来,因为每次返回实体数据、token时,也可以顺便返回token过期时间。

      不用担心伪造过期时间,毕竟前端提醒过期只是为了方便,即使伪造未过期,不刷新token,到时也是用不了的,自已骗自已

    前端:1、调接口的公共方法里封装好,如果401授权失败等,要重新去调token接口获取新的token,并重新请求数据,这个要做到用户无感知(最多因为多调了两次接口显得慢了一些,但整体流程未中断)

    2、返回的Token,要缓存在cookie或localstorage等,每个请求都要用的,如果要在payload里多传参数,也可放缓存里一并打包

    JWT有个小问题,就是后端要禁用客户时,会删除RefreshToken,以后就无法更新AccessToken了,但在过期间AccessToken仍然可用。可以搭配着其它验证(比如Redis缓存个黑名单,禁用时把账号加进去,每次都验证下黑名单,因为黑名单不多,所以效率损失可以忽略不计)

  • 相关阅读:
    信息学奥赛一本通(C++)在线评测系统——基础(一)C++语言—— 1063:最大跨度值
    信息学奥赛一本通(C++)在线评测系统——基础(一)C++语言—— 1063:最大跨度值
    信息学奥赛一本通(C++)在线评测系统——基础(一)C++语言—— 1062:最高的分数
    信息学奥赛一本通(C++)在线评测系统——基础(一)C++语言—— 1057:简单计算器
    信息学奥赛一本通(C++)在线评测系统——基础(一)C++语言—— 1057:简单计算器
    信息学奥赛一本通(C++)在线评测系统——基础(一)C++语言—— 1062:最高的分数
    信息学奥赛一本通(C++)在线评测系统——基础(一)C++语言—— 1062:最高的分数
    C#怎么给新建的winform程序添加资源文件夹Resources
    C#怎么给新建的winform程序添加资源文件夹Resources
    C#Win32API编程之PostMessage
  • 原文地址:https://www.cnblogs.com/liuyouying/p/10927558.html
Copyright © 2020-2023  润新知