• JSON Web Token(JWT)이란..?
    JSON Web Token(이하 JWT, 토큰)은 웹표준(RFC 7519)으로써 암호화와 검증(Signature) 기능을 가진 인증 토큰입니다.

  • JWT 구조는 아래와 같습니다.
    Base64(Header).Base64(Payload).Base64(Signature)
    • Header에는 사용 된 보안 메커니즘(암호 알고리즘 등)에 대한 정보가,
      Data에는 전달할 정보가,
      Signature에는 Base64(Header).Base64(Data)를
      Header에 명시된 알고리즘으로 암호화된 문자열이 담깁니다.
  • 예시
    • 토큰
      eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

    • 포함된 정보
      Header : {"alg": "HS256","typ": "JWT"}
      Payload : {"sub": "1234567890","name": "John Doe","iat": 1516239022}
      Signature : HMACSHA256(base64UrlEncode(header)+"."+base64UrlEncode(payload), your-256-bit-secret)
  • JWT의 무결성을 보장하기 위해 여러 서명 알고리즘(alg)을 사용할 수 있습니다.
    • RSA based
    • Elliptic curves
    • HMAC
    • None
  • 장단점
    • 장점
      • JWT는 인증에 필요한 모든 정보를 담고 있기 때문에 인증을 위한 별도의 저장소가 없어도 된다.
      • 세션(Stateful)과 다르게 서버는 무상태(StateLess)가 된다.
      • 확장성이 우수하다.
      • 토큰 기반으로 다른 로그인 시스템에 접근 및 권한 공유가 가능하다.
      • OAuth의 경우 소셜 계정을 통해서 다른 웹서비스에 로그인 할 수 있다.

    • 단점( 장점을 가지려면 그에 상응하는 단점도 발생한다. )
      • 쿠키/세션과 다르게 토큰의 길이가 길어, 인증 요청이 많아질수록 네트워크 부하가 심해진다.
      • Payload 자체는 암호화가 되지 않아 중요한 정보는 담을 수 없다.
      • 토큰을 탈취당한다면 대처가 매우 어렵다.

      • JWT는 매우 복잡한 표준이어서 사용자가(개발자) 잘못 이해할 가능성이 있다.
        설정이 잘못되는 경우 최악에 모든 사용자가 유효한 JWT를 생성해서 다른 사용자인척 할 수 있다.
        이는 초보개발자들 사이에서 나오는 실수가 아니고, 실제로 Auth0에서 이러한 버그를 만들어낸 적이 있다.
        이는 많은 보안 전문가들이 JWT를 싫어하게 되는 이유 중 하나가 되었다.
        JWT는 수많은 기능을 제공하고 있기 때문에,
        개발자들이 잠재적으로 실수 할 수 있는 범위가 넓다.

      • 로그아웃에 문제가 있다.
        전통적인 세션을 활용하는 방법의 경우, 단순히 세션 스토리지에서 해당 세션 값을 날리면 되었고 이것으로 충분했다.
        그러나 JWT와 다른 무상태 (stateless) 토큰으로는 이것이 불가능하다.
        우리는 이 토큰을 지울 수 없다.
        왜냐하면 이는 스스로 정보를 담고 있는 토큰이며,
        토큰의 상태를 관리하는 중앙 인증 관리 시스템이 없기 때문이다.
        이는 아래 세가지 방법으로 해결할 수 있다.
        • 토큰의 생명 주기를 굉장히 짧게 하는 방법 (5분 이내).
          해당 시간이 지나면, 새롭게 토큰을 만든다.

        • 시스템에서 최근에 만료된 토큰을 저장해 두는 것

        • 서버 로그아웃 기능 자체를 없애고,
          클라이언트에 토큰 삭제를 맡기는 방법 좋은 시스템의 경우엔 앞에 두가지 방법을 선택한다.

          그러나 두 문제의 해결책에서 볼 수 있는 것처럼,
          두 방법은 모두 중앙에서 인증을 관리해야 하는 시스템이 필요하며
          이는 더 이상 JWT의 장점을 유효하지 않게 만든다.
      • JWT의 크키는 상대적으로 크기 때문에, 쿠키에 JWT를 담을 경우 오버헤드가 발생한다는 것이다.

  • PHP JWT CLASS SAMPLE
<?php

class jwt{
	protected $alg;
	function __construct(){
		   //사용할 알고리즘
		   $this->alg = "sha256";
	}

	function hashing(array $data){
		   // 토큰의 헤더
		   $header = json_encode(array(
				  "alg"=>$this->alg,
				  "typ"=>"JWT"
		   ));
		   $payload = json_encode($data);
		   $signature = hash($this->alg, $header.$payload);
		   return base64_encode($header.".".$payload.".".$signature);
	}

	function dehashing($token){
		   // 토큰 만들때의 구분자 . 으로 나누기
		   $parted = explode(".", base64_decode($token));

		   $signature = $parted[2];

		   // 위에서 토큰 만들때와 같은 방식으로 시그니처 만들고 비교
		   if(hash($this->alg, $parted[0].$parted[1]) == $signature){
				//  echo "good";
		   }else{
				  //echo("잘못된 signature 입니다");
		   }
		   $payload = json_decode($parted[1],true);
		   return $payload;
	}
}


?>

 

  • 로그인 정보 저장 (JWT를 사용하여 해싱하고, 쿠키로 저장) 예제
<?php

// 쿠키변수 생성
function set_cookie($cookie_name, $value, $expire){
	setcookie(md5($cookie_name), base64_encode($value), time() + $expire, '/');
}
// 쿠키변수값 얻음
function get_cookie($cookie_name){
	$cookie = md5($cookie_name);
	if (array_key_exists($cookie, $_COOKIE))
		return base64_decode($_COOKIE[$cookie]);
	else
		return "";
}
$user_id='user1';
$user_nm='홍길동';
$city='서울특별시';

$jwt = new jwt();
$login_token = $jwt->hashing(array(
				 "user_id"=> $user_id,
				 "user_nm"=> $user_nm,
				 "city"=> $city
));

//Cookie 저장 : 1달
set_cookie("token", $login_token, 2592000);

?>

 

  • JWT 정보 가져오기 (저장된 JWT를 가져와 디코딩한 후, 사용자 정보를 출력하고 상수로 정의)
<?php

$get_login_token = get_cookie("token");

$jwt_data = $jwt->dehashing($get_login_token);

echo $jwt_data["user_id"]."<br>";
echo $jwt_data["user_nm"]."<br>";
echo $jwt_data["city"]."<br>";

define('SESSION_USER_ID',   $jwt_data["user_id"]);
define('SESSION_USER_NM',   $jwt_data["user_nm"]);
define('SESSION_CITY',    $jwt_data["city"]);


echo SESSION_USER_ID."<br>";
echo SESSION_USER_NM."<br>";
echo SESSION_CITY."<br>";

?>

 

 

 

 


참고출처 : https://velog.io/@thelm3716/JWTvul

참고출처 : https://jh-tr.tistory.com/100

참고출처 : https://yceffort.kr/2021/05/drawback-of-jwt