Java-Auth0 JWT验证-这正确吗? [英] Java - Auth0 JWT Verification - Is this correct?

查看:1163
本文介绍了Java-Auth0 JWT验证-这正确吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在将REST API设置为Auth0作为身份验证服务.一切都正常,但是在一次非常奇怪的事件后,我的信心被动摇了.

我的实现基于示例代码此处(RS256部分)和此处.唯一的修改是我将PublicKey强制转换为RSAPublicKey.

问题是我想肯定的是,如果签名错误,验证将失败.我更改了签名的最后一个字符(我们称"x"),并且令牌仍在验证中. -将其切换为除 "x"之外的任何其他字符或原始生成的字符会导致其按预期失败.

我怀疑这是由于某种填充/编码/解码/Base64问题引起的,而我只是碰巧选择了一个具有相同的前n个位数的字符或其他东西?当然,这意味着如果要进行成功的猜测",它将需要包括令牌的其余四十亿个字符-这是其存在的全部要点.因此,我不必担心令牌会是可猜测的-我只是确保已正确实施了验证的要旨.

import com.auth0.jwk.Jwk;
import com.auth0.jwk.JwkException;
import com.auth0.jwk.JwkProvider;
import com.auth0.jwk.UrlJwkProvider;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTVerificationException;
import com.auth0.jwt.interfaces.DecodedJWT;

import java.security.interfaces.RSAPublicKey;

public class Application {
    public static void main(String[] args) {

        try {
            JwkProvider provider = new UrlJwkProvider("<my-provider>");
            Jwk jwk = provider.get("<my-key-id>");

            String token = "<some-token-passed-from-client>";
            RSAPublicKey publicKey = (RSAPublicKey) jwk.getPublicKey();

            Algorithm algorithm = Algorithm.RSA256(publicKey, null);
            JWTVerifier verifier = JWT.require(algorithm)
                    .withIssuer("<my-issuer>")
                    .build();
            DecodedJWT jwt = verifier.verify(token);

        } catch (JWTVerificationException exception) {
            System.out.println("JWT Exception: " + exception.getMessage());
        } catch (JwkException e) {
            e.printStackTrace();
        }

    }
}

解决方案

除了 JWT示例 (下面的屏幕截图).

令牌包含3个单独的颜色部分:

  • 标头:有关令牌的元数据,包括所使用的算法.未签名且未验证.
  • 有效载荷:令牌的内容(用户声明).签名并验证.
  • 签名:令牌的签名.

尤其是标题没有签名,它们几乎可以包含任何内容并可以更改.尽管如果令牌丢失,令牌将无法解码(例如错误的算法值).

有将所有这些数据一起编码,填充和序列化的规则,请参考规范.可以在令牌中添加/删除/编辑几个字节,并且仍然具有有效的令牌,尽管略有不同(修改的填充或标头).但是,不可能更改令牌的有效负载,这很重要.

JWT库提供了单独的功能来从令牌中提取信息,例如get_unverified_headers()get_claims(),避免了任何潜在的混乱.假设如果不小心从标题而不是有效内容中读取用户标识符,那将是一个关键漏洞,因为可以自由更改.

I'm setting up a REST API with Auth0 as the authentication service. Everything is working but my confidence has been a bit shaken after a rather strange occurrence.

My implementation is based on the sample code here (The RS256 section) and here. The only modification being that I cast the PublicKey to an RSAPublicKey.

The issue is that I wanted to be positive that the verification would fail on a bad signature. I changed the signature's last character (we'll say "x") and the token still verified. BUT - switching it to any character other than "x" or the originally generated character caused it to fail as expected.

My suspicion is that this is due to some sort of padding/encoding/decoding/Base64 issue and that I just happened to pick a character with the same first n-number of bits or something? Of course, this means that if a successful "guess" were to be made, it would need to include the remaining forty-kabillion characters of the token - which is the whole point of its existence. So I'm not necessarrily concerned that the token will be guessable - I'm just making sure that I've implemented the gist of the verification correctly.

import com.auth0.jwk.Jwk;
import com.auth0.jwk.JwkException;
import com.auth0.jwk.JwkProvider;
import com.auth0.jwk.UrlJwkProvider;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTVerificationException;
import com.auth0.jwt.interfaces.DecodedJWT;

import java.security.interfaces.RSAPublicKey;

public class Application {
    public static void main(String[] args) {

        try {
            JwkProvider provider = new UrlJwkProvider("<my-provider>");
            Jwk jwk = provider.get("<my-key-id>");

            String token = "<some-token-passed-from-client>";
            RSAPublicKey publicKey = (RSAPublicKey) jwk.getPublicKey();

            Algorithm algorithm = Algorithm.RSA256(publicKey, null);
            JWTVerifier verifier = JWT.require(algorithm)
                    .withIssuer("<my-issuer>")
                    .build();
            DecodedJWT jwt = verifier.verify(token);

        } catch (JWTVerificationException exception) {
            System.out.println("JWT Exception: " + exception.getMessage());
        } catch (JwkException e) {
            e.printStackTrace();
        }

    }
}

解决方案

Have a look no further than the JWT example (screenshot below).

The token has 3 parts in separate colors:

  • Header: Metadata about the token, including the algorithm used. Not signed and not verified.
  • Payload: Contents of the token (user claims). Signed and verified.
  • Signature: The signature of the token.

Headers in particular are not signed, they can contain almost anything and be altered. Although the token won't be able to be decoded if it's borked (bad algorithm value for example).

There are rules to encode, pad and serialize all this data together, refer to the specifications. It is possible to add/remove/edit few bytes in a token and still have a valid token, albeit slightly different (modified padding or headers). However it is not possible to alter the payload of a token, which is what's important.

JWT libraries offer separate functions to extract information from the token, like get_unverified_headers() and get_claims(), avoiding any potential confusion. Hypothetically if one were to accidentally read the user identifier from the header instead of the payload, it would be a critical vulnerability because that can be altered freely.

这篇关于Java-Auth0 JWT验证-这正确吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆