资讯专栏INFORMATION COLUMN

使用JWT确保API的安全

jeffrey_up / 2772人阅读

摘要:下面举几个未经保护的可能将会造成的安全事故通过抓包,找到发送短信验证码的。这可能将会直接对用户造成财产损失。因此,未经安全保护的非常的危险,对进行安全保护异常重要。可以将请求参数一并放入中,以此来确保数据的安全性。

未经安全保护的API非常的危险

未经安全保护的API非常的危险,其和裸奔无异。即使API文档没有被人为泄露,通过简单的抓包也可以非常容易的获取到API的URL以及对应的请求参数。下面举几个未经保护的API可能将会造成的安全事故:

通过抓包,找到发送短信验证码的API。然后利用该API恶意的发送短信验证码。而发送短信验证码是需要收费的,这样的恶意攻击,将会导致无故的损失发送短信的费用。还会让很多不明真相的吃瓜群众收到奇怪的短信验证码,进而对产品产生不好的印象。

通过抓包,找到获取用户信息的API。该API的暴露,将会让该平台上所有用户的信息被毫无保留的暴露在互联网上。如果用户信息中还涉及到一些重要的个人信息。比如身份证号,手机号等。将会让用户受到人生和财产的损失。

通过抓包,找到和财产相关的API。比如说修改订单状态的API。一旦该API暴露,黑客可以恶意的修改用户的订单状态。比如说将订单的状态修改为取消。这可能将会直接对用户造成财产损失。

上面这些例子仅仅只是笔者临时想到的一些利用裸露API进行恶意攻击的方式,实际中还存在着更多由于未对API进行加密,而造成损失的情况。因此,未经安全保护的API非常的危险,对API进行安全保护异常重要。

什么是JWT

JWT是json web token的缩写。关于其如何确保数据传输的安全性的文章你可以在搜索引擎上找到很多,在这里我将仅仅简单介绍我对此的理解。

JWT可以理解为一串通过特定算法生成的字符串,在API的请求中,将这段字符串放入请求参数中。API Server通过判断这段字符串是合法的还是伪造的,来确定这次API请求是否有效。通过该安全措施,将确保即使API被暴露,没有生成JWT字符串的算法,也没有办法成功调用API。

JWT字符串分为两个部分(官方的说法是分为3个部分),分别是‘未加密部分’和‘加密部分’。而‘加密部分’的内容实际上是‘未加密部分’加密得到的。API Server检查JWT字符串是否有效的第一步是将‘加密部分’解密然后与‘未加密部分’进行比较,查看是否内容一致。如果内容不一致,则说明该JWT字符串是伪造的。

JWT字符串中包括一个‘过期时间’的字段,当API Server获取到JWT字符串后,可以通过检查该字段与当前时间相比,是否已经处于过期的状态。如果‘过期时间’字段早于当前时间,则说明这次API请求是无效的。

你也可以在JWT字段种加入自定义的字段。然后在API Server获取到JWT字段后,通过这些自定义的字段判断是不是符合具体的业务逻辑,进而判断这次请求是不是有效。

利用jjwt实现JWT对API的保护

jjwtjava对JWT的封装,下面的方法将会演示。在java中如何利用jjwt实现API的保护

gradle依赖
compile "io.jsonwebtoken:jjwt:0.7.0"
生成JWT字符串
public String buildJwt(Date exp) {
    String jwt = Jwts.builder()
            .signWith(SignatureAlgorithm.HS256,SECRET_KEY)//SECRET_KEY是加密算法对应的密钥,这里使用额是HS256加密算法
            .setExpiration(exp)//expTime是过期时间
            .claim("key","vaule")//该方法是在JWT中加入值为vaule的key字段
            .compact();
    return jwt;
}
判断JWT是否有效
public boolean isJwtValid(String jwt) {
    try {
        //解析JWT字符串中的数据,并进行最基础的验证
        Claims claims = Jwts.parser()
                .setSigningKey(SECRET_KEY)//SECRET_KEY是加密算法对应的密钥,jjwt可以自动判断机密算法
                .parseClaimsJws(jwt)//jwt是JWT字符串
                .getBody();
        String vaule = claims.get("key", String.class);//获取自定义字段key
        //判断自定义字段是否正确
        if ("vaule".equals(vaule)) {
            return true;
        } else {
            return false;
        }
    }
    //在解析JWT字符串时,如果密钥不正确,将会解析失败,抛出SignatureException异常,说明该JWT字符串是伪造的
    //在解析JWT字符串时,如果‘过期时间字段’已经早于当前时间,将会抛出ExpiredJwtException异常,说明本次请求已经失效
    catch (SignatureException|ExpiredJwtException e) {
        return false;
    }
}
Client端

Client端需要做的就是,根据API的需求将JWT字符串放入http请求中。我的做法是对于所有的API,在Client端生成JWT字段,然后将其添加到http请求的header中,确保所有的API都获得保护。对于一些比较敏感的信息,再用加一层JWT验证。比如说用户信息,在调用登录API后,API Server将会返回一个特定的JWT字符串,该JWT字段总将会包含该用户的userId。如果要获取用户信息,除了要将Client端生成的JWT字段放入请求,还需要将该JWT字符串放入请求。接下来展示一下利用OKHttp在http请求的header中加入JWT字段的代码:

//该方法将会在所有请求的header中加入jwt
public Response call(Request request) throws IOException {
    OkHttpClient client = new OkHttpClient();
    Request.Builder requestBuilder = request.newBuilder()
            .addHeader("commonJwt", jwtService.makeJwt());//加入Client本地生成的JWT字符串
    //加入登录成功后获取到的JWT字符串
    String userJwt = jwtService.getUserJwt();
    if (!StringUtils.isSpace(userJwt))
        requestBuilder.addHeader("userJwt", userJwt);
    request = requestBuilder.build();
    return client.newCall(request).execute();
}
API Server

API Server端需要做的就是,在收到API请求时,首先检查client端生成的JWT字段是否有效,然后如果该API涉及敏感信息,则检查检测特定的JWT字段是否有效。接下来展示一下在spring中利用aop进行JWT字段的验证:

@Pointcut("@annotation(org.springframework.web.bind.annotation.ResponseBody)")
public void onCommonAuth(){}

//所有的API都需要验证client生成的JWT字段是否有效
@Order(1)
@Around("onCommonAuth()")
public Object onCommonAuth(ProceedingJoinPoint joinPoint) throws Throwable {
    HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
    String commonJwt = request.getHeader("commonJwt");
    if (jwtService.isCommonJwtValid(commonJwt)) {
        return joinPoint.proceed();
    } else {
        return "没有访问该API的权限";
    }
}

@Pointcut("execution(* com.demo.controller.UserController.getUserInfo(..))")
public void onGetByUserInfo() {}

//对获取用户信息API,坚持userJwt是否有效
@Order(2)
@Around("onGetByUserInfo()&&args(userId,..)")
public Object onGetByUserInfo(ProceedingJoinPoint joinPoint, Long userId) throws Throwable {
    HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
    String userJwt = request.getHeader("userJwt");
    if (jwtService.isUserJwtValid(userJwt, userId)) {
        return joinPoint.proceed();
    } else {
        return "没有访问该API的权限";
    }
}
补充

JWT在一定程度上,保护了API的安全。但是其本身还是存在一定的缺陷的。比如说,一定JWT的加密密钥一旦被泄露,那么黑客就可以生成JWT字符串了,因此保护好JWT加密密钥非常重要。

在上面的例子当中,介绍了获取用户信息API需要加入userJwt的例子。userJwt其实就是在JWT字符串中加入了userId字段,继而保证一个userJwt只能访问一个用户的信息。对于其他的API,比如说PUT和POST操作,需要新增和修改数据的API。可以将请求参数一并放入jwt中,以此来确保数据的安全性。否则黑客还可以在JWT字符串还没有过期的时间段内,修改请求中的参数,达到攻击的目的。

另外还要防止重复式攻击,黑客还可以在JWT字符串还没有过期的时间段内,重复提交请求,达到攻击的目的。比如说新增订单的API,如果被黑客采用重复式攻击的方式,就会生成多个订单。

参考

RESTful Api 身份认证中的安全性设计探讨
JSON Web Token - 在Web应用间安全地传递信息
八幅漫画理解使用JSON Web Token设计单点登录系统
How to Create and verify JWTs in Java
jwt官方介绍
jwt官网
jjwt在github上的地址

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/66104.html

相关文章

  • 微服务实战:从架构到发布(二)

    摘要:微服务架构着重培养通用可重用的服务。服务注册和发现微服务架构下,有大量的微服务需要处理。网关也是获得微服务状态监控信息的中心。实际情况是,微服务和其它企业架构并存。 引言:上篇文章介绍了微服务和单体架构的区别、微服务的设计、消息、服务间通信、数据去中心化,本篇会继续深入微服务,介绍其它特性。 治理去中心化 通常治理的意思是构建方案,并且迫使人们通过努力达到组织的目标。SOA治理指导开发...

    JinB 评论0 收藏0
  • 微服务实战:从架构到发布(二)

    摘要:微服务架构着重培养通用可重用的服务。服务注册和发现微服务架构下,有大量的微服务需要处理。网关也是获得微服务状态监控信息的中心。实际情况是,微服务和其它企业架构并存。 引言:上篇文章介绍了微服务和单体架构的区别、微服务的设计、消息、服务间通信、数据去中心化,本篇会继续深入微服务,介绍其它特性。 治理去中心化 通常治理的意思是构建方案,并且迫使人们通过努力达到组织的目标。SOA治理指导开发...

    zhaot 评论0 收藏0
  • 微服务实战:从架构到发布(二)

    摘要:微服务架构着重培养通用可重用的服务。服务注册和发现微服务架构下,有大量的微服务需要处理。网关也是获得微服务状态监控信息的中心。实际情况是,微服务和其它企业架构并存。 引言:上篇文章介绍了微服务和单体架构的区别、微服务的设计、消息、服务间通信、数据去中心化,本篇会继续深入微服务,介绍其它特性。 治理去中心化 通常治理的意思是构建方案,并且迫使人们通过努力达到组织的目标。SOA治理指导开发...

    sf190404 评论0 收藏0
  • 论微服务安全

    摘要:微服务能够为应用程序设计提供一种更具针对性范围性与模块性的实现方案。安全微服务部署模式可谓多种多样但其中使用最为广泛的当数每主机服务模式。在微服务环境下,安全性往往成为最大的挑战。不同微服务之间可通过多种方式建立受信关系。 每个人都在讨论微服务,每个人也都希望能够实现微服务架构,而微服务安全也日渐成为大家关注的重要问题。今天小数与大家分享的文章,就从应用层面深入探讨了应对微服务安全挑战...

    plokmju88 评论0 收藏0
  • Spring Security

    摘要:框架具有轻便,开源的优点,所以本译见构建用户管理微服务五使用令牌和来实现身份验证往期译见系列文章在账号分享中持续连载,敬请查看在往期译见系列的文章中,我们已经建立了业务逻辑数据访问层和前端控制器但是忽略了对身份进行验证。 重拾后端之Spring Boot(四):使用JWT和Spring Security保护REST API 重拾后端之Spring Boot(一):REST API的搭建...

    keelii 评论0 收藏0

发表评论

0条评论

jeffrey_up

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<