博客專欄

        EEPW首頁 > 博客 > JSON Web Token(JWT、JWS、JSE)

        JSON Web Token(JWT、JWS、JSE)

        發(fā)布人:電子禪石 時(shí)間:2024-02-23 來源:工程師 發(fā)布文章
        前言

        基于session/cookie的web網(wǎng)站認(rèn)證方式轉(zhuǎn)變?yōu)榱嘶贠Auth2等開放授權(quán)協(xié)議的單點(diǎn)登錄模式(SSO),相應(yīng)的基于服務(wù)器session+瀏覽器cookie的Auth手段也發(fā)生了轉(zhuǎn)變,Json Web Token出現(xiàn)成為了當(dāng)前的熱門的Token Auth機(jī)制。

        Json Web Token(JWT)

        JSON Web Token(JWT)是一個(gè)非常輕巧的規(guī)范。這個(gè)規(guī)范允許我們使用JWT在兩個(gè)組織之間傳遞安全可靠的信息。

        官方定義:JSON Web Token (JWT) is a compact URL-safe means of representing claims to be transferred between two parties

        JWT 的官方文檔: https://jwt.io/introduction/

        JWS只是JWT的一種實(shí)現(xiàn),除了JWS外,JWE(JSON Web Encryption)也是JWT的一種實(shí)現(xiàn):

         什么是 JWT

        一個(gè)JWT(這里應(yīng)該是JWS),應(yīng)該是如下形式的:

        eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.
        eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.
        TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ

        這些東西看上很凌亂,但是非常緊湊,并且是可打印的主要用于驗(yàn)證簽名的真實(shí)性。

        JWT 解決什么問題?

        JWT的主要目的是在服務(wù)端和客戶端之間以安全的方式來轉(zhuǎn)移聲明。主要的應(yīng)用場景如下所示:

        1.認(rèn)證 Authentication;

        2.授權(quán) Authorization // 注意這兩個(gè)單詞的區(qū)別;

        3.聯(lián)合識別;

        4.客戶端會話(無狀態(tài)的會話);

        5.客戶端機(jī)密。

        JWT 的一些名詞解釋

        1.JWS:Signed JWT簽名過的jwt

        2.JWE:Encrypted JWT部分payload經(jīng)過加密的jwt;

        目前加密payload的操作不是很普及;

        3.JWK:JWT的密鑰,也就是我們常說的scret;

        4.JWKset:JWT key set在非對稱加密中,需要的是密鑰對而非單獨(dú)的密鑰,在后文中會闡釋;

        5.JWA:當(dāng)前JWT所用到的密碼學(xué)算法;

        6.nonsecure JWT:當(dāng)頭部的簽名算法被設(shè)定為none的時(shí)候,該JWT是不安全的;因?yàn)楹灻牟糠挚杖保腥硕伎梢孕薷摹?/p>0×01 JWT的組成

        一個(gè)通常你看到的jwt,由以下三部分組成,它們分別是:

        1.header:主要聲明了JWT的簽名算法;

        2.payload:主要承載了各種聲明并傳遞明文數(shù)據(jù);

        3.signture:擁有該部分的JWT被稱為JWS,也就是簽了名的JWS;沒有該部分的JWT被稱為nonsecure JWT 也就是不安全的JWT,此時(shí)header中聲明的簽名算法為none。

        三個(gè)部分用·分割。形如 xxxxx.yyyyy.zzzzz的樣式。

        JWT header
        {  "typ": "JWT",  "alg": "none",  "jti": "4f1g23a12aa"}

        頭通常由兩部分組成:令牌的類型,即JWT,alg代表正在使用的散列算法,例如HMAC SHA256或RSA。

        當(dāng)然,還有兩個(gè)可選的部分,一個(gè)是jti,也就是JWT ID,代表了正在使用JWT的編號,這個(gè)編號在對應(yīng)服務(wù)端應(yīng)當(dāng)唯一。當(dāng)然,jti也可以放在payload中。

        另一個(gè)是cty,也就是content type。這個(gè)比較少見,當(dāng)payload為任意數(shù)據(jù)的時(shí)候,這個(gè)頭無需設(shè)置,但是當(dāng)內(nèi)容也帶有jwt的時(shí)候。也就是嵌套JWT的時(shí)候,這個(gè)值必須設(shè)定為jwt。這種情況比較少見。

        jwt header 的加密算法

        加密的方式如下:

        base64UrlEncode(header)>> eyJ0eXAiOiJKV1QiLCJhbGciOiJub25lIiwianRpIjoiNGYxZzIzYTEyYWEifQ
        JWT payload
        復(fù)制代碼
        {  "iss": "http://shaobaobaoer.cn",  "aud": "http://shaobaobaoer.cn/webtest/jwt_auth/",  "jti": "4f1g23a12aa",  "iat": 1534070547,  "nbf": 1534070607,  "exp": 1534074147,  "uid": 1,  "data": {    "uname": "shaobao",    "uEmail": "shaobaobaoer@126.com",    "uID": "0xA0",    "uGroup": "guest"
          }
        }
        復(fù)制代碼

        payload通常由三個(gè)部分組成,分別是 Registered Claims ; Public Claims ; Private Claims ;每個(gè)聲明,都有各自的字段。

        Registered Claims

        iss  【issuer】發(fā)布者的url地址

        sub 【subject】該JWT所面向的用戶,用于處理特定應(yīng)用,不是常用的字段

        aud 【audience】接受者的url地址

        exp 【expiration】 該jwt銷毀的時(shí)間;unix時(shí)間戳

        nbf  【not before】 該jwt的使用時(shí)間不能早于該時(shí)間;unix時(shí)間戳

        iat   【issued at】 該jwt的發(fā)布時(shí)間;unix 時(shí)間戳

        jti    【JWT ID】 該jwt的唯一ID編號

        Public Claims這些可以由使用JWT的那些標(biāo)準(zhǔn)化組織根據(jù)需要定義,應(yīng)當(dāng)參考文檔IANA JSON Web Token Registry。

        Private Claims這些是為在同意使用它們的各方之間共享信息而創(chuàng)建的自定義聲明,既不是注冊聲明也不是公開聲明。上面的payload中,沒有public claims只有private claims。

        jwt payload 的加密算法

        加密的方式如下:

        base64UrlEncode(payload)>> eyJpc3MiOiJodHRwOi8vc2hhb2Jhb2Jhb2VyLmNuIiwiYXVkIjoiaHR0cDovL3NoYW9iYW9iYW9lci5jbi93ZWJ0ZXN0L2p3dF9hdXRoLyIsImp0aSI6IjRmMWcyM2ExMmFhIiwiaWF0IjoxNTM0MDcwNTQ3LCJuYmYiOjE1MzQwNzA2MDcsImV4cCI6MTUzNDA3NDE0NywidWlkIjoxLCJkYXRhIjp7InVuYW1lIjoic2hhb2JhbyIsInVFbWFpbCI6InNoYW9iYW9iYW9lckAxMjYuY29tIiwidUlEIjoiMHhBMCIsInVHcm91cCI6Imd1ZXN0In19

        所以,在JWT中,不應(yīng)該在載荷里面加入任何敏感的數(shù)據(jù)。在上面的例子中,我們傳輸?shù)氖怯脩舻腢ser ID,郵箱等。這個(gè)值實(shí)際上不是什么敏感內(nèi)容,一般情況下被知道也是安全的。但是像密碼這樣的內(nèi)容就不能被放在JWT中了。如果將用戶的密碼放在了JWT中,那么懷有惡意的第三方通過Base64解碼就能很快地知道你的密碼了。

        當(dāng)然,這也是有解決方案的,那就是加密payload。在之后會說到。

        0×02 JWS 的概念JWS 的結(jié)構(gòu)

        JWS ,也就是JWT Signature,其結(jié)構(gòu)就是在之前nonsecure JWT的基礎(chǔ)上,在頭部聲明簽名算法,并在最后添加上簽名。創(chuàng)建簽名,是保證jwt不能被他人隨意篡改。

        為了完成簽名,除了用到header信息和payload信息外,還需要算法的密鑰,也就是secret。當(dāng)利用非對稱加密方法的時(shí)候,這里的secret為服務(wù)器保管的私鑰。

        為了方便后文的展開,我們把JWT的密鑰或者密鑰對,統(tǒng)一稱為JSON Web Key,也就是JWK。

        jwt signature 的簽名算法

        復(fù)制代碼
        RSASSA || ECDSA || HMACSHA256(
          base64UrlEncode(header) + "." +
          base64UrlEncode(payload),
          secret)>> GQPGEpixjPZSZ7CmqXB-KIGNzNl4Y86d3XOaRsfiXmQ>> # 上面這個(gè)是用 HMAC SHA256生成的
        復(fù)制代碼

        到目前為止,jwt的簽名算法有三種。

        對稱加密HMAC【哈希消息驗(yàn)證碼】:HS256/HS384/HS512

        非對稱加密RSASSA【RSA簽名算法】(RS256/RS384/RS512)和ECDSA【橢圓曲線數(shù)據(jù)簽名算法】(ES256/ES384/ES512)

        最后將簽名與之前的兩段內(nèi)容用.連接,就可以得到經(jīng)過簽名的JWT,也就是JWS。

        eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiIsImp0aSI6IjRmMWcyM2ExMmFhIn0.eyJpc3MiOiJodHRwOi8vc2hhb2Jhb2Jhb2VyLmNuIiwiYXVkIjoiaHR0cDovL3NoYW9iYW9iYW9lci5jbi93ZWJ0ZXN0L2p3dF9hdXRoLyIsImp0aSI6IjRmMWcyM2ExMmFhIiwiaWF0IjoxNTM0MDcwNTQ3LCJuYmYiOjE1MzQwNzA2MDcsImV4cCI6MTUzNDA3NDE0NywidWlkIjoxLCJkYXRhIjp7InVuYW1lIjoic2hhb2JhbyIsInVFbWFpbCI6InNoYW9iYW9iYW9lckAxMjYuY29tIiwidUlEIjoiMHhBMCIsInVHcm91cCI6Imd1ZXN0In19.GQPGEpixjPZSZ7CmqXB-KIGNzNl4Y86d3XOaRsfiXmQ

        當(dāng)驗(yàn)證簽名的時(shí)候,利用公鑰或者密鑰來解密Sign,和 base64UrlEncode(header) + “.” + base64UrlEncode(payload) 的內(nèi)容完全一樣的時(shí)候,表示驗(yàn)證通過。

        JWS 的額外頭部聲明

        如果對于CA有些概念的話,這些內(nèi)容會比較好理解一些。為了確保服務(wù)器的密鑰對可靠有效,同時(shí)也方便第三方CA機(jī)構(gòu)來簽署JWT而非本機(jī)服務(wù)器簽署JWT,對于JWS的頭部,可以有額外的聲明,以下聲明是可選的,具體取決于JWS的使用方式。如下所示:

        jku: 發(fā)送JWK的地址;最好用HTTPS來傳輸

        jwk: 就是之前說的JWK

        kid: jwk的ID編號

        x5u: 指向一組X509公共證書的URL

        x5c: X509證書鏈

        x5t:X509證書的SHA-1指紋

        x5t#S256: X509證書的SHA-256指紋

        typ: 在原本未加密的JWT的基礎(chǔ)上增加了 JOSE 和 JOSE+ JSON。JOSE序列化后文會說及。適用于JOSE標(biāo)頭的對象與此JWT混合的情況。

        crit: 字符串?dāng)?shù)組,包含聲明的名稱,用作實(shí)現(xiàn)定義的擴(kuò)展,必須由 this->JWT的解析器處理。不常見。

        多重驗(yàn)證與JWS序列化

        當(dāng)需要多重簽名或者JOSE表頭的對象與JWS混合的時(shí)候,往往需要用到JWS的序列化。JWS的序列化結(jié)構(gòu)如下所示:

        復(fù)制代碼
        {    "payload": "eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ","signatures": 
            [
                {            "protected": "eyJhbGciOiJSUzI1NiJ9",            "header": { "kid": "2010-12-29" },            "signature":"signature1"
                },
                {            "protected": "eyJhbGciOiJSUzI1NiJ9",            "header": { "kid": "e9bc097a-ce51-4036-9562-d2ade882db0d" },            "signature":"signature2"
                },
                ...
            ]
        }
        復(fù)制代碼

        結(jié)構(gòu)很容易理解。首先是payload字段,這個(gè)不用多講,之后是signatures字段,這是一個(gè)數(shù)組,代表著多個(gè)簽名。每個(gè)簽名的結(jié)構(gòu)如下:

        protected:之前的頭部聲明,利用b64uri加密;

        header:JWS的額外聲明,這段內(nèi)容不會放在簽名之中,無需驗(yàn)證;

        signature:也就是對當(dāng)前header+payload的簽名。

        0×03 JWE 相關(guān)概念

        JWE是一個(gè)很新的概念,總之,除了jwt的官方手冊外,很少有網(wǎng)站或者博客會介紹這個(gè)東西。也并非所有的庫都支持JWE。這里記錄一下自己看官方手冊后理解下來的東西。

        JWS是去驗(yàn)證數(shù)據(jù)的,而JWE(JSON Web Encryption)是保護(hù)數(shù)據(jù)不被第三方的人看到的。通過JWE,JWT變得更加安全。

        JWE和JWS的公鑰私鑰方案不相同,JWS中,私鑰持有者加密令牌,公鑰持有者驗(yàn)證令牌。而JWE中,私鑰一方應(yīng)該是唯一可以解密令牌的一方。

        在JWE中,公鑰持有可以將新的數(shù)據(jù)放入JWT中,但是JWS中,公鑰持有者只能驗(yàn)證數(shù)據(jù),不能引入新的數(shù)據(jù)。因此,對于公鑰/私鑰的方案而言,JWS和JWE是互補(bǔ)的。

         JWSJWE
        producerpri_keypub_key
        consumerpub_keypri_key
        JWE 的構(gòu)成

        一個(gè)JWE,應(yīng)該是如下形式的:

        復(fù)制代碼
        eyJhbGciOiJSU0ExXzUiLCJlbmMiOiJBMTI4Q0JDLUhTMjU2In0.
        UGhIOguC7IuEvf_NPVaXsGMoLOmwvc1GyqlIKOK1nN94nHPoltGRhWhw7Zx0-kFm1NJn8LE9XShH59_
        i8J0PH5ZZyNfGy2xGdULU7sHNF6Gp2vPLgNZ__deLKxGHZ7PcHALUzoOegEI-8E66jX2E4zyJKxYxzZIItRzC5hlRirb6Y5Cl_p-ko3YvkkysZIFNPccxRU7qve1WYPxqbb2Yw8kZqa2rMWI5ng8Otv
        zlV7elprCbuPhcCdZ6XDP0_F8rkXds2vE4X-ncOIM8hAYHHi29NX0mcKiRaD0-D-ljQTPcFPgwCp6X-nZZd9OHBv-B3oWh2TbqmScqXMR4gp_A.
        AxY8DCtDaGlsbGljb3RoZQ.
        KDlTtXchhZTGufMYmOYGS4HffxPSUrfmqCHXaI9wOGY.
        9hH0vgRfYgPnAHOd8stkvw
        復(fù)制代碼

        如你所見JWE一共有五個(gè)部分,分別是:

        The protected header,類似于JWS的頭部;

        The encrypted key,用于加密密文和其他加密數(shù)據(jù)的對稱密鑰;

        The initialization vector,初始IV值,有些加密方式需要額外的或者隨機(jī)的數(shù)據(jù);

        The encrypted data (cipher text),密文數(shù)據(jù);

        The authentication tag,由算法產(chǎn)生的附加數(shù)據(jù),來防止密文被篡改。

        JWE 密鑰加密算法

        一般來說,JWE需要對密鑰進(jìn)行加密,這就意味著同一個(gè)JWT中至少有兩種加密算法在起作用。但是并非將密鑰拿來就能用,我們需要對密鑰進(jìn)行加密后,利用JWK密鑰管理模式來導(dǎo)出這些密鑰。JWK的管理模式有以下五種,分別是:

        Key Encryption

        Key Wrapping

        Direct Key Agreement

        Key Agreement with Key Wrapping

        Direct Encryption

        并不是所有的JWA都能夠支持這五種密鑰管理管理模式,也并非每種密鑰管理模式之間都可以相互轉(zhuǎn)換。可以參考Spomky-Labs/jose中給出的表格至于各個(gè)密鑰管理模式的細(xì)節(jié),還請看JWT的官方手冊,解釋起來較為復(fù)雜。

        JWE Header

        就好像是JWS的頭部一樣。JWE的頭部也有著自己規(guī)定的額外聲明字段,如下所示:

        type:一般是 jwt

        alg:算法名稱,和JWS相同,該算法用于加密稍后用于加密內(nèi)容的實(shí)際密鑰

        enc:算法名稱,用上一步生成的密鑰加密內(nèi)容的算法。

        zip:加密前壓縮數(shù)據(jù)的算法。該參數(shù)可選,如果不存在則不執(zhí)行壓縮,通常的值為 DEF,也就是deflate算法

        jku/jkw/kid/x5u/x5c/x5t/x5t#S256/typ/cty/crit:和JWS額額外聲明一樣。

        JWE 的加密過程

        步驟2和步驟3,更具不同的密鑰管理模式,應(yīng)該有不同的處理方式。在此只羅列一些通常情況。

        之前談及,JWE一共有五個(gè)部分。現(xiàn)在來詳細(xì)說一下加密的過程:

        1.根據(jù)頭部alg的聲明,生成一定大小的隨機(jī)數(shù);

        2.根據(jù)密鑰管理模式確定加密密鑰;

        3.根據(jù)密鑰管理模式確定JWE加密密鑰,得到CEK;

        4.計(jì)算初始IV,如果不需要,跳過此步驟;

        5.如果ZIP頭申明了,則壓縮明文;

        6.使用CEK,IV和附加認(rèn)證數(shù)據(jù),通過enc頭聲明的算法來加密內(nèi)容,結(jié)果為加密數(shù)據(jù)和認(rèn)證標(biāo)記;

        7.壓縮內(nèi)容,返回token。

        復(fù)制代碼
        base64(header) + '.' +base64(encryptedKey) + '.' + // Steps 2 and 3base64(initializationVector) + '.' + // Step 4base64(ciphertext) + '.' + // Step 6base64(authenticationTag) // Step 6
        復(fù)制代碼
        多重驗(yàn)證與JWE序列化

        和JWS類似,JWE也定義了緊湊的序列化格式,用來完成多種形式的加密。大致格式如下所示:

        復(fù)制代碼
        {    "protected": "eyJlbmMiOiJBMTI4Q0JDLUhTMjU2In0",    "unprotected": { "jku":"https://server.example.com/keys.jwks" },    "recipients":[
                {        "header": { "alg":"RSA1_5","kid":"2011-04-29" },        "encrypted_key":        "UGhIOguC7Iu...cqXMR4gp_A"
                },
                {        "header": { "alg":"A128KW","kid":"7" },        "encrypted_key": "6KB707dM9YTIgH...9locizkDTHzBC2IlrT1oOQ"
                }
            ],    "iv": "AxY8DCtDaGlsbGljb3RoZQ",    "ciphertext": "KDlTtXchhZTGufMYmOYGS4HffxPSUrfmqCHXaI9wOGY",    "tag": "Mz-VPPyU4RlcuYv1IwIvzw"}
        復(fù)制代碼

        結(jié)構(gòu)很容易理解,如下所示:

        protected:之前的頭部聲明,利用b64uri加密;

        unprotected:一般放JWS的額外聲明,這段內(nèi)容不會被b64加密;

        iv:64加密后的iv參數(shù);

        add:額外認(rèn)證數(shù)據(jù);

        ciphertext:b64加密后的加密數(shù)據(jù);

        recipients:b64加密后的認(rèn)證標(biāo)志-加密鏈,這是一個(gè)數(shù)組,每個(gè)數(shù)組中包含了兩個(gè)信息;

        header:主要是聲明當(dāng)前密鑰的算法;

        encrypted_key:JWE加密密鑰。

        總結(jié):
        JWT:只有Header部分(alg為none)和Payload兩個(gè)部分組成,兩個(gè)部分通過base64編碼,以一個(gè)點(diǎn)號拼接起來的token,缺點(diǎn):無法保證token有沒有被修改。
        JWS:由Header部分(alg為服務(wù)器加密算法名)、Payload部分、和Signature三個(gè)部分組成,以兩個(gè)點(diǎn)拼接起來的token
        而JWE的計(jì)算過程相對繁瑣,不夠輕量級,因此適合與數(shù)據(jù)傳輸而非token認(rèn)證,但該協(xié)議也足夠安全可靠,用簡短字符串描述了傳輸內(nèi)容,兼顧數(shù)據(jù)的安全性與完整性。
        refer:
        https://www.freebuf.com/articles/web/180874.html


        *博客內(nèi)容為網(wǎng)友個(gè)人發(fā)布,僅代表博主個(gè)人觀點(diǎn),如有侵權(quán)請聯(lián)系工作人員刪除。



        關(guān)鍵詞: jwt

        技術(shù)專區(qū)

        關(guān)閉
        主站蜘蛛池模板: 白玉县| 林甸县| 通山县| 洪泽县| 怀来县| 贵州省| 沾化县| 苏尼特左旗| 邵东县| 大埔县| 礼泉县| 临潭县| 富锦市| 阿尔山市| 成都市| 城市| 江山市| 天门市| 陇川县| 大理市| 田林县| 洪泽县| 松原市| 温泉县| 镇巴县| 南宁市| 巴彦县| 定边县| 涟水县| 日土县| 衡阳县| 陕西省| 闵行区| 苗栗市| 浦东新区| 施甸县| 台湾省| 兰考县| 茂名市| 邵东县| 临武县|