๋ชจ์ ํดํน ์คํฐ๋ - 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์ (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
ํ๊ธฐ
- ์ฝ๋๊ฐ ๋์๊ฐ๋ค๊ณ ์ ๋๋ก ๊ตฌํํ๊ฑด ์๋๋ผ๋ ์ฌ์ค์ ๊ฐ๊ณผํ์ง ๋ง์!
- ๋ฌด์ธ๊ฐ ๊ตฌํํ๊ธฐ ์ ์๋ ๊ตฌํํ ๋ด์ฉ์ ์ ๋๋ก ์ฝ์ด๋ณด๊ณ ์ฐจ๋ก๋๋ก ์งํํ์!
- ์ง์ ๊ตฌํ๋ ์ข์ง๋ง ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฐพ์๋ณด๋ ค๋ ์๊ฐ๋ ํด๋ด์ผ ํจ!
์ง๋ฌธ ํ์, ์์ ๋ฐ ๋ณด์์ ๋ํ ์ง์ ํ์