Archive for Dev. (junyup2)

์ง€์‹์„ ์ฑ„์›Œ๊ฐ€๋Š” ใ€Ž๊ฐœ๋ฐœ์ž/ํ™”์ดํŠธํ•ด์ปคใ€๋ฅผ ๋ชฉํ‘œ๋กœ ์ •๋ฆฌํ•˜๋Š” ๋ธ”๋กœ๊ทธ

๋ชจ์˜ ํ•ดํ‚น ์Šคํ„ฐ๋””/๋ชจ์˜ ํ•ดํ‚น - ๊ณผ์ œ

[๊ณผ์ œ] 03์ฃผ์ฐจ(3-3) JWT ๊ตฌํ˜„ (๋กœ๊ทธ์ธ ์œ ์ง€)

Gearvirus(junyup2) 2023. 11. 15. 17:24

๋ชจ์˜ ํ•ดํ‚น ์Šคํ„ฐ๋”” - 3์ฃผ์ฐจ ๊ณผ์ œ (3-3) JWT ๊ตฌํ˜„ (๋กœ๊ทธ์ธ ์œ ์ง€)

 

์ฝ”๋“œ์— ๋Œ€ํ•˜์—ฌ ๋” ๋ณด๋‹ค๊ฐ€ ๋ฐœ๊ฒฌํ•œ ๋ฌธ์ œ์  ์ˆ˜์ •

TIL - JWT ํ† ํฐ (๋‚ด ์ฝ”๋“œ์˜ ๋ฌธ์ œ์  ๋ฐœ๊ฒฌ)

๋ชจ์˜ ํ•ดํ‚น ์Šคํ„ฐ๋”” - 3์ฃผ์ฐจ ๊ณผ์ œ (3-2) JWT ๊ตฌํ˜„ - jwt ๊ทœ๊ฒฉ์— ์–ด๊ธ‹๋‚จ

 

๋ชจ์˜ ํ•ดํ‚น ์Šคํ„ฐ๋”” - ๊ณผ์ œ 03์ฃผ์ฐจ(3-2) JWT ๊ตฌํ˜„ - jwt ๊ทœ๊ฒฉ์— ์–ด๊ธ‹๋‚จ

๋ชจ์˜ ํ•ดํ‚น ์Šคํ„ฐ๋”” - 3์ฃผ์ฐจ ๊ณผ์ œ (3-2) JWT ๊ตฌํ˜„(๋กœ๊ทธ์ธ ์œ ์ง€) ๋ฌธ์ œ์  ๋ฐœ๊ฒฌ ๊ธฐ์กด์˜ ๋กœ๊ทธ์ธ ํŽ˜์ด์ง€์˜ Develop ๊ณผ์ œ ๋ชจ์˜ ํ•ดํ‚น ์Šคํ„ฐ๋”” - 3์ฃผ์ฐจ ๊ณผ์ œ(1) ๋กœ๊ทธ์ธ ํŽ˜์ด์ง€ ๋ณด์™„ ๋ชจ์˜ ํ•ดํ‚น ์Šคํ„ฐ๋”” - ๊ณผ์ œ 03์ฃผ์ฐจ(1)

codegear-archive.tistory.com

JWT ๊ตฌํ˜„์— ์„ฑ๊ณตํ–ˆ๋‹ค๊ณ  ์ƒ๊ฐํ•˜๊ณ , ์ด๊ฒƒ์ €๊ฒƒ ์ถ”๊ฐ€์ ์œผ๋กœ ์ฐพ์•„๋ณด๋˜ ๋„์ค‘ jwt ๊ทœ๊ฒฉ์— ๋งž์ง€ ์•Š๋Š” ๋ฐฉ์‹์œผ๋กœ ์ฝ”๋“œ๋ฅผ ๊ตฌํ˜„ํ•จ์„ ๊นจ๋‹ซ๊ณ  ํ™•์ธํ•ด๋ณธ๊ฒฐ๊ณผ ๋ฌธ์ œ์ ์ด ์žˆ์–ด ๊ทธ๊ฒƒ์„ ์ˆ˜์ •


๊ธฐ์กด์˜ ๋ฌธ์ œ์  ์ธ์ง€

๊ณผ์ œ ์ง„ํ–‰์— ์žˆ์–ด jwt ๋ฅผ ๋ฐœ๊ธ‰ํ•  ๋‹น์‹œ

base64_encode($header . "." $payload . ". " . $signature) ๋ฅผ ์‚ฌ์šฉ

์ž˜ ๋Œ์•„๊ฐ€๋Š”๊ฒƒ์„ ํ™•์ธํ–ˆ์œผ๋ฏ€๋กœ ์‹ ๊ฒฝ์•ˆ์“ฐ๊ณ , ์ถ”๊ฐ€์ ์œผ๋กœ ์ฐพ์•„๋ณด๋˜์ค‘

https://jwt.io/introduction๋ฅผ ์ฝ์–ด๋ณด๋ฉด์„œ

 

JWT.IO

JSON Web Tokens are an open, industry standard RFC 7519 method for representing claims securely between two parties.

jwt.io

jwt ๊ทœ๊ฒฉ์— ๋งž์ง€ ์•Š๋Š” ์ฝ”๋”ฉ์„ ํ•˜๊ณ  ์žˆ์—ˆ์Œ์„ ํ™•์ธํ•จ

 

Signature
To create the signature part you have to take the 'encoded header', the 'encoded payload', a secret, the algorithm specified in the header, and sign that.

 

Putting all together
The output is three Base64-URL strings separated by dots that can be easily passed in HTML and HTTP environments, while being more compact when compared to XML-based standards such as SAML.

 

๊ฒฐ๋ก ์ ์œผ๋กœ ์˜ฌ๋ฐ”๋ฅธ ํ˜•ํƒœ๋Š”...

JWT์˜ ํ˜•ํƒœ๋Š” ํ—ค๋”(Header) . ํŽ˜์ด๋กœ๋“œ(PayLoad) . ์„œ๋ช…(Signature)๋กœ
์ „๋ถ€ base64UrlEncode ๋˜์–ด์žˆ๋Š” ์ƒํƒœ์—ฌ์•ผ ํ•จ
์„œ๋ช…์€
base64UrlEncode(header) + "." + base64UrlEncode(payload)๋ฅผ 
์ง€์ •ํ•œ ์•”ํ˜ธํ™” ์•Œ๊ณ ๋ฆฌ์ฆ˜(Header์— ์ง€์ •๋จ)์œผ๋กœ ์ธ์ฝ”๋”ฉํ•˜๊ณ  
Base64(Url Safe) Encodeํ•˜์—ฌ ์ƒ์„ฑ
๊ฒฐ๋ก ์ ์œผ๋กœ
The output is three Base64-URL strings separated by dots

Base64-URL string = baseUrlUncode = Base64(Url Safe) Encode

์ด๋ฅผ ํ† ๋Œ€๋กœ ์ˆ˜์ •ํ•œ ์ฝ”๋“œ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์Œ

 

jwt_.php

<?php
    // Base64URL ์ธ์ฝ”๋”ฉ & ๋””์ฝ”๋”ฉ ํ•จ์ˆ˜
    // base64UrlEncode
    function base64UrlEncode($data){
        // First of all you should encode $data to Base64 string
        $base64 = base64_encode($data);
        // Make sure you get a valid result, otherwise, return FALSE, as the base64_encode() function do
        if($base64 === false){
            return false;
        }
        // Convert Base64 to Base64URL by replacing “+” with “-” and “/” with “_”
        $base64Url = strtr($base64, '+/', '-_');
        // Remove padding character from the end of line and return the Base64URL result
        return rtrim($base64Url, '=');
    }

    // base64UrlDecode
    function base64UrlDecode($base64Url, $strict = false){
        // Convert Base64URL to Base64 by replacing “-” with “+” and “_” with “/”
        $base64 = strtr($base64Url, '-_', '+/');
        // Decode Base64 string and return the original data
        return base64_decode($base64, $strict);
    }

    class JWT{
        protected $alg;
        protected $secret_key;

        // ์ƒ์„ฑ์ž
        function __construct(){
            // ์‚ฌ์šฉํ•  ์•Œ๊ณ ๋ฆฌ์ฆ˜ SHA-2(SHA-256)
            $this->alg = 'sha256';
            // ๋น„๋ฐ€ ํ‚ค
            $this->secret_key = "your secret key";
        }
                
        // JWT ๋ฐœ๊ธ‰(hash) - Header . PayLoad . Signature
        function hashing(array $data): string{
            // Header(ํ—ค๋”) - alg (์‚ฌ์šฉํ•  ์•Œ๊ณ ๋ฆฌ์ฆ˜), typ (ํƒ€์ž…) ๋ช…์‹œ
            $header = json_encode(array(
                'alg' => $this->alg,
                'typ' => 'JWT'
            ));

            // PayLoad(ํŽ˜์ด๋กœ๋“œ) - ์ „๋‹ฌํ•  ๋ฐ์ดํ„ฐ
            $payload = json_encode($data);

            // Signature(์„œ๋ช…) = (base64UrlEncode(ํ—ค๋”) . base64UrlEncode(ํŽ˜์ด๋กœ๋“œ), secret)
            $signature = hash_hmac($this->alg, base64UrlEncode($header) . base64UrlEncode($payload), $this->secret_key);
            // base64UrlEncode(ํ—ค๋”) . base64UrlEncode(ํŽ˜์ด๋กœ๋“œ) . base64UrlEncode(์„œ๋ช…)
            return (base64UrlEncode($header) . '.' . base64UrlEncode($payload) . '.' . base64UrlEncode($signature));
        }

        // JWT ํ•ด์„(dehash) - ์‚ฌ์šฉ์ž ๊ฒ€์ฆ
        function dehashing($token){
            // ํ† ํฐ ๋ถ„ํ•  (๊ตฌ๋ถ„์ž . ๊ธฐ์ค€) - ๋ฐฐ์—ด๋กœ ์ €์žฅ
            $parted = explode('.', $token);
            
            $signature = $parted[2];
            // ํ† ํฐ ๋ฐœ๊ธ‰์‹œ์™€ ๊ฐ™์€ ๋ฐฉ๋ฒ•์œผ๋กœ Signature ์ƒ์„ฑ ํ›„ ๋น„๊ต
            if(base64UrlEncode(hash_hmac($this->alg, $parted[0] . $parted[1], $this->secret_key)) != $signature){
                return "Signature Error";
            }

            // parted[1]์€ (base64 ์ธ์ฝ”๋”ฉ ๋˜์–ด์žˆ๋Š”) $payload -> base64 ๋””์ฝ”๋”ฉ
            $payload = base64UrlDecode($parted[1]);

            // json ๋””์ฝ”๋”ฉ์„ ํ†ตํ•ด $payload -> $data
            $data = json_decode($payload, true);
            
            // ๋งŒ๋ฃŒ ๊ฒ€์‚ฌ exp
            // 'ํ† ํฐ ๋งŒ๋ฃŒ ์‹œ๊ฐ„'์ด 'ํ˜„์žฌ ์‹œ๊ฐ„'๋ณด๋‹ค ์ด์ „์ธ ๊ฒฝ์šฐ
            if($data['exp'] < time()) {
                return 'Expired';
            }
           
            /*
            * ๊ธฐํƒ€ ํ† ํฐ ํ™•์ธ ์ž‘์—…
            */

            return $data;
        }
    }
    $jwt = new JWT();
?>
  • Base64URL Encode & Decode ํ•จ์ˆ˜๊ฐ€ ํ•„์š”
    • base64_encode, base64_decode ๋ฅผ ํ™œ์šฉ
base64 ์ธ์ฝ”๋”ฉ/๋””์ฝ”๋”ฉ ํ•จ์ˆ˜์˜ ํŠน์ง•

1. base64_encode ํ•จ์ˆ˜๋กœ ์ธ์ฝ”๋”ฉ๋œ ๋ฌธ์ž์—ด์€ ์ธ์ˆ˜๋กœ ์ „๋‹ฌ๋œ ๋ฌธ์ž์—ด์˜ ๊ธธ์ด, ์ˆ˜์— ๋น„๋ก€
2. `์ธ์ฝ”๋”ฉ ๋ฌธ์ž์—ด์˜ byte = ( ์ธ์ˆ˜ ๋ฌธ์ž์—ด์˜ byte / 3 )  * 4` ๊ณต์‹์ด ์„ฑ๋ฆฝ
       โ€ป ๋‹จ, ๊ด„ํ˜ธ ์•ˆ์˜ ๊ฐ’์ด ์†Œ์ˆ˜์ ์ด ๋‚˜์˜ค๋ฉด ์˜ฌ๋ฆผํ•˜์—ฌ ๊ณ„์‚ฐ
3. base64_encode ํ•จ์ˆ˜์˜ ๊ฒฐ๊ณผ๋กœ ๋ฐ˜ํ™˜๋œ ๋ฌธ์ž์—ด์˜ `=` ๋ฌธ์ž๋Š” ์ž๋ฆฌ ์ˆ˜ ์ฑ„์šฐ๊ธฐ ์œ„ํ•œ ์šฉ๋„
4. base64_encode ํ•จ์ˆ˜์˜ ๊ฒฐ๊ณผ ๋ฌธ์ž์—ด์—์„œ `=` ๋ฌธ์ž๋ฅผ ์ œ๊ฑฐํ•ด๋„, ๋””์ฝ”๋”ฉ์—๋Š” ์˜ํ–ฅ์„ ์ฃผ์ง€ ์•Š์Œ
5. base64 ํ•จ์ˆ˜๋Š” ํ•œ, ์˜, ํŠน์ˆ˜๋ฌธ์ž ๊ตฌ๋ถ„ ์—†์ด byte ๋กœ ๊ณ„์‚ฐ

base64 index table

  • ์œ„์˜ ํ…Œ์ด๋ธ”์€ base64์˜ (Value - Encoding) ํ…Œ์ด๋ธ”
    • ํ…Œ์ด๋ธ”์˜ 62, 63, ๊ทธ๋ฆฌ๊ณ  padding์„ ๋ณด๋ฉด ๊ฐ๊ฐ + ,  / , = 
  •  URL์—์„œ +,/,= ๋Š” ํŠน์ˆ˜ํ•˜๊ฒŒ ์˜ˆ์•ฝ๋œ ์ œ์–ด ๋ฌธ์ž
    • + ๋Š” ๋„์–ด์“ฐ๊ธฐ(๊ณต๋ฐฑ๋ฌธ์ž, ์ŠคํŽ˜์ด์Šค)๋ฅผ ์˜๋ฏธ
    • / ๋Š” URL ๋””๋ ‰ํ† ๋ฆฌ ๊ฐ„์˜ ๊ฒฝ๋กœ ๊ตฌ๋ถ„์ž
    • = ๋Š” ํŒŒ๋ผ๋ฏธํ„ฐ์—์„œ name๊ณผ value ์‚ฌ์ด์— ์“ฐ๋Š” ๊ธฐํ˜ธ
  • ์œ„์™€ ๊ฐ™์€ ์ด์œ ์—์„œ base64 ๋ฅผ base64(Url Safe)๋กœ ๋ฐ”๊ฟ”์ค˜์•ผํ•จ
    • strtr($base64, '+/', '-_');
      • ( “+” ๋Š” “-” )๋กœ,  (“/” ๋Š” “_”)๋กœ ๊ต์ฒด
    • =์˜ ๊ฒฝ์šฐ ์‚ญ์ œํ•ด๋„ ๋˜๊ณ  ๋†”๋‘ฌ๋„ ๋จ(decoding์—๋Š” ์ง€์žฅ์ด ์—†์Œ)
    • ๋†”๋‘ฌ๋„ ๋˜๋Š” ์ด์œ 
      • = ์˜ ๊ฒฝ์šฐ URL์—์„œ padding ์–ด์ฐจํ”ผ ๊ฐ€์žฅ ๋งˆ์ง€๋ง‰ ๋ถ€๋ถ„์— ์œ„์น˜ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๋ธŒ๋ผ์šฐ์ €์—์„œ ์ž๋™์œผ๋กœ ํŠน์ˆ˜ํ•œ ์ œ์–ด ๋ฌธ์ž๊ฐ€ ์•„๋‹Œ URL ์ธ์ฝ”๋”ฉ์„ ํ†ตํ•ด %3D ๊ฐ€ ๋ ๊ฒƒ์ž„
    • rtrim($base64Url, '=');
      • ํ•˜์ง€๋งŒ ์ง€์›Œ๋„ ๋˜๊ธฐ ๋•Œ๋ฌธ์— ์ง€์›Œ ์คŒ
  • Decoding์€ Encoding์˜ ์—ญ์ˆœ์œผ๋กœ ์ง„ํ–‰
    • strtr($base64Url, '-_', '+/');
    • ("-" ๋Š” "+")๋กœ, ("_" ๋Š” "/")๋กœ ๊ต์ฒด

JWT ๋ฐœ๊ธ‰
  • ์„œ๋ช…์— ๋“ค์–ด๊ฐ€๋Š” ํ—ค๋”์™€ ํŽ˜์ด๋กœ๋“œ๋Š” base64UrlEncode ์ƒํƒœ๋กœ ๋“ค์–ด๊ฐ
    • Signature(์„œ๋ช…) = (base64UrlEncode(ํ—ค๋”) . base64UrlEncode(ํŽ˜์ด๋กœ๋“œ), secret)
  • ๊ธฐ์กด์— ์‚ฌ์šฉํ–ˆ๋˜ hash() ๋Œ€์‹ 
  • hash_hmac() ์„ ์ด์šฉ
    • hash_hmac($this->alg, base64UrlEncode($header) . base64UrlEncode($payload), $this->secret_key);
  • ํ—ค๋”, ํŽ˜์ด๋กœ๋“œ, ์„œ๋ช…์„ ๊ฐ๊ฐ base64UrlEncode ํ•˜๊ณ  ์ (.)์œผ๋กœ ์ด์–ด ๋ถ™์ธ ๊ฐ’์„ ๋ฐ˜ํ™˜
    • return base64UrlEncode(ํ—ค๋”) . base64UrlEncode(ํŽ˜์ด๋กœ๋“œ) . base64UrlEncode(์„œ๋ช…)
JWT ํ•ด์„

 

๋ฐœ๊ธ‰๊ณผ ์—ญ์ˆœ์œผ๋กœ ์ง„ํ–‰

  • ๋ฐ›์•„์˜จ ํ† ํฐ ๋ถ„ํ•  (๊ตฌ๋ถ„์ž . ๊ธฐ์ค€) - ๋ฐฐ์—ด๋กœ ์ €์žฅ
    • $parted = explode('.', $token);
  • ํ† ํฐ ๋ฐœ๊ธ‰์‹œ์™€ ๊ฐ™์€ ๋ฐฉ๋ฒ•์œผ๋กœ Signature ์ƒ์„ฑ ํ›„ ๋น„๊ต
    • if(base64UrlEncode(hash_hmac($this->alg, $parted[0] . $parted[1], $this->secret_key)) != $signature)
  • parted[1]์€ base64 ์ธ์ฝ”๋”ฉ ๋˜์–ด์žˆ๋Š” $payload ์ด๋ฏ€๋กœ base64 ๋””์ฝ”๋”ฉ
    • $payload = base64UrlDecode($parted[1]);
  • json ๋””์ฝ”๋”ฉ์„ ํ†ตํ•ด $payload -> $data
    • $data = json_decode($payload, true);
    • json์œผ๋กœ ์ผ๋ ฌ์ด ๋˜์—ˆ๋˜ ๊ฐ’์„ ๋ฐฐ์—ด๋กœ ๋Œ๋ ค์ฃผ๋Š” ๊ฒƒ
  • ์ตœ์ข…์ ์œผ๋กœ $data๊ฐ’์„ ๋ฐ˜ํ™˜

eyJhbGciOiJzaGEyNTYiLCJ0eXAiOiJKV1QifQ.eyJleHAiOjE3MDAwMzkxNjksImlhdCI6MTcwMDAzNTU2OSwiaWQiOiJMSlkiLCJuYW1lIjoiXHVjNzc0XHVjOTAwXHVjNWZkIn0.YTQwOGQ0YjYwMjRhOTIyMTg4NWZlNjkyY2MxYmEyNDU5MDEzMjNmYzkwYzQ2OWFkNWY4ZmUzNzIzOTNmYmM5ZQ

  • ๋กœ๊ทธ์ธ ํ›„์— ํ† ํฐ์ด ์ž˜ ๋ฐœํ–‰๋œ ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์—ˆ์Œ
  • jwt.io์— ๋„ฃ์–ด๋ณธ ๊ฒฐ๊ณผ ์ž˜ ๋ณ€ํ™˜๋˜๋Š” ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์—ˆ์Œ

Reference

ํ›„๊ธฐ

 

  • ์ฝ”๋“œ๊ฐ€ ๋Œ์•„๊ฐ„๋‹ค๊ณ  ์ œ๋Œ€๋กœ ๊ตฌํ˜„ํ•œ๊ฑด ์•„๋‹ˆ๋ผ๋Š” ์‚ฌ์‹ค์„ ๊ฐ„๊ณผํ•˜์ง€ ๋ง์ž!
  • ๋ฌด์–ธ๊ฐ€ ๊ตฌํ˜„ํ•˜๊ธฐ ์ „์—๋Š” ๊ตฌํ˜„ํ•  ๋‚ด์šฉ์„ ์ œ๋Œ€๋กœ ์ฝ์–ด๋ณด๊ณ  ์ฐจ๋ก€๋Œ€๋กœ ์ง„ํ–‰ํ•˜์ž!
  • ์ง์ ‘ ๊ตฌํ˜„๋„ ์ข‹์ง€๋งŒ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์ฐพ์•„๋ณด๋ ค๋Š” ์ƒ๊ฐ๋„ ํ•ด๋ด์•ผ ํ•จ!

 

 

 

์งˆ๋ฌธ ํ™˜์˜, ์ˆ˜์ • ๋ฐ ๋ณด์™„์— ๋Œ€ํ•œ ์ง€์  ํ™˜์˜