๋ชจ์ ํดํน ์คํฐ๋ - 3์ฃผ์ฐจ ๊ณผ์ (3-3) JWT ๊ตฌํ (๋ก๊ทธ์ธ ์ ์ง)
์ฝ๋์ ๋ํ์ฌ ๋ ๋ณด๋ค๊ฐ ๋ฐ๊ฒฌํ ๋ฌธ์ ์ ์์
TIL - JWT ํ ํฐ (๋ด ์ฝ๋์ ๋ฌธ์ ์ ๋ฐ๊ฒฌ)
๋ชจ์ ํดํน ์คํฐ๋ - 3์ฃผ์ฐจ ๊ณผ์ (3-2) JWT ๊ตฌํ - jwt ๊ท๊ฒฉ์ ์ด๊ธ๋จ
JWT ๊ตฌํ์ ์ฑ๊ณตํ๋ค๊ณ ์๊ฐํ๊ณ , ์ด๊ฒ์ ๊ฒ ์ถ๊ฐ์ ์ผ๋ก ์ฐพ์๋ณด๋ ๋์ค jwt ๊ท๊ฒฉ์ ๋ง์ง ์๋ ๋ฐฉ์์ผ๋ก ์ฝ๋๋ฅผ ๊ตฌํํจ์ ๊นจ๋ซ๊ณ ํ์ธํด๋ณธ๊ฒฐ๊ณผ ๋ฌธ์ ์ ์ด ์์ด ๊ทธ๊ฒ์ ์์
๊ธฐ์กด์ ๋ฌธ์ ์ ์ธ์ง
๊ณผ์ ์งํ์ ์์ด jwt ๋ฅผ ๋ฐ๊ธํ ๋น์
base64_encode($header . "." $payload . ". " . $signature) ๋ฅผ ์ฌ์ฉ
์ ๋์๊ฐ๋๊ฒ์ ํ์ธํ์ผ๋ฏ๋ก ์ ๊ฒฝ์์ฐ๊ณ , ์ถ๊ฐ์ ์ผ๋ก ์ฐพ์๋ณด๋์ค
https://jwt.io/introduction๋ฅผ ์ฝ์ด๋ณด๋ฉด์
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์ (Value - Encoding) ํ
์ด๋ธ
- ํ ์ด๋ธ์ 62, 63, ๊ทธ๋ฆฌ๊ณ padding์ ๋ณด๋ฉด ๊ฐ๊ฐ + , / , =
- URL์์ +,/,= ๋ ํน์ํ๊ฒ ์์ฝ๋ ์ ์ด ๋ฌธ์
- + ๋ ๋์ด์ฐ๊ธฐ(๊ณต๋ฐฑ๋ฌธ์, ์คํ์ด์ค)๋ฅผ ์๋ฏธ
- / ๋ URL ๋๋ ํ ๋ฆฌ ๊ฐ์ ๊ฒฝ๋ก ๊ตฌ๋ถ์
- = ๋ ํ๋ผ๋ฏธํฐ์์ name๊ณผ value ์ฌ์ด์ ์ฐ๋ ๊ธฐํธ
- ์์ ๊ฐ์ ์ด์ ์์ base64 ๋ฅผ base64(Url Safe)๋ก ๋ฐ๊ฟ์ค์ผํจ
- strtr($base64, '+/', '-_');
- ( “+” ๋ “-” )๋ก, (“/” ๋ “_”)๋ก ๊ต์ฒด
- =์ ๊ฒฝ์ฐ ์ญ์ ํด๋ ๋๊ณ ๋๋ฌ๋ ๋จ(decoding์๋ ์ง์ฅ์ด ์์)
- ๋๋ฌ๋ ๋๋ ์ด์
- = ์ ๊ฒฝ์ฐ URL์์ padding ์ด์ฐจํผ ๊ฐ์ฅ ๋ง์ง๋ง ๋ถ๋ถ์ ์์นํ๊ธฐ ๋๋ฌธ์ ๋ธ๋ผ์ฐ์ ์์ ์๋์ผ๋ก ํน์ํ ์ ์ด ๋ฌธ์๊ฐ ์๋ URL ์ธ์ฝ๋ฉ์ ํตํด %3D ๊ฐ ๋ ๊ฒ์
- rtrim($base64Url, '=');
- ํ์ง๋ง ์ง์๋ ๋๊ธฐ ๋๋ฌธ์ ์ง์ ์ค
- strtr($base64, '+/', '-_');
- 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
- https://base64.guru/developers/php/examples/base64url
- https://www.php.net/manual/en/function.hash-hmac.php
ํ๊ธฐ
- ์ฝ๋๊ฐ ๋์๊ฐ๋ค๊ณ ์ ๋๋ก ๊ตฌํํ๊ฑด ์๋๋ผ๋ ์ฌ์ค์ ๊ฐ๊ณผํ์ง ๋ง์!
- ๋ฌด์ธ๊ฐ ๊ตฌํํ๊ธฐ ์ ์๋ ๊ตฌํํ ๋ด์ฉ์ ์ ๋๋ก ์ฝ์ด๋ณด๊ณ ์ฐจ๋ก๋๋ก ์งํํ์!
- ์ง์ ๊ตฌํ๋ ์ข์ง๋ง ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฐพ์๋ณด๋ ค๋ ์๊ฐ๋ ํด๋ด์ผ ํจ!
์ง๋ฌธ ํ์, ์์ ๋ฐ ๋ณด์์ ๋ํ ์ง์ ํ์