TOKEN을 이용한 인증 

Cookie를 이용한 방식은 Client에서 데이터를 관리하고 전달하기 때문에 변조에 취약하다.

Session을 이용한 방식은 데이터가 Session에 저장되기 때문에 사용자가 많다면 부하가 발생한다.

 

Token의 방식은 Cookie의 방식과 유사하다. 다만 서버로 전달되는 데이터가 Token이 되는 것이다.

서버는 전달받은 Token의 유효성을 검사하여 로그인 여부를 확인한다.

 

 


 

JWT (Json Web Token)

JWT는 Json 형태의 데이터를 암호화한 토큰을 의미하며 3가지 파트로 나뉘어 있다.

JWT.io 사이트

JWT는 Header, PayLoad, Sigature로 이루어져 있으며 각 파트는 .(dot)으로 연결되어있다.

즉 Header.PayLoad.Signature 형식이다. (Header와 PayLoad는 Base64 인코딩을 하여 사용한다.)

 

Header: 알고리즘의 형식을 기술하는 부분

PayLoad: 사용자(개발자)가 필요한 데이터를 기술하는 부분

Signature: JWT가 변조되었는지 확인하는 부분

 

Token은 클라이언트에서 소유하고 있기에 변조의 가능성이 있다. 이를 해결하기위해 Signature가 존재하며 Signature는 Header, PayLoad, secret(개발자가 지정한 암호)를 사용하여 Hash 단반향 암호화를 진행한다.

이를 이용하여 서버가 요청을 받았을 때 Token의 Header, PayLoad, Secret을 사용하여 검증용 Signature를 만들고 전달 받은 Token의 Signature와 비교하여 변조 유무를 확인한다.

 

위와 같은 특성 때문에 JWT는 클라이언트에서 소유하고 있지만 변조의 탐지가 쉬우며 Session과 같이 서버에 저장되는 정보가 존재하지 않기 때문에 상대적으로 적은 부하를 발생한다.

 


JWT를 사용한 로그인 구현

jwt.php

<?php
  class JWT {
    protected $alg;
    protected $secret_key;
    // define('EXPIRE_ERROR', '401');
    // define('SIGNITURE_ERROR', 'ERROR');

    //    생성자
    function __construct()
    {
        //사용할 알고리즘
        $this->alg = 'sha256';
        // 비밀 키
        $this->secret_key = "normaltic";
    }

//    jwt 발급하기
    function hashing(array $data): string
    {
        // 헤더 - 사용할 알고리즘과 타입 명시
        $header = json_encode(array(
            'alg' => $this->alg,
            'typ' => 'JWT'

        ));

        // 페이로드 - 전달할 데이터
        $payload = json_encode($data);
        // 시그니처
        $signature = hash($this->alg, $header . $payload . $this->secret_key);

        return base64_encode($header . '.' . $payload . '.' . $signature);
    }

//    jwt 해석하기
    function dehashing($token)
    {
        // 구분자 . 로 토큰 나누기
        $parted = explode('.', base64_decode($token));

        $signature = $parted[2];

        // 토큰 만들 때처럼 시그니처 생성 후 비교
        if (hash($this->alg, $parted[0] . $parted[1] . $this->secret_key) != $signature) {
            return 0;
        }

        // 만료 검사
        $payload = json_decode($parted[1], true);
        if ($payload['exp'] < time()) { // 유효시간이 현재 시간보다 전이면
            return 0;
        }


        return json_decode($parted[1], true);
    }

    // 유효성 체크 
    function verify($token) {
        $parted = explode('.', base64_decode($token));
        $signature = $parted[2];

        // 토큰 만들 때처럼 시그니처 생성 후 비교
        if (hash($this->alg, $parted[0] . $parted[1] . $this->secret_key) != $signature) {
            return 0;
        }

        // 만료 검사
        $payload = json_decode($parted[1], true);
        if ($payload['exp'] < time()) { // 유효시간이 현재 시간보다 전이면
            return 0;
        }
        return 1;
    }
}

 $jwt = new JWT();
?>

 

login.php

<?php
        require_once './lib/login_function.php';
        require_once './lib/jwt.php';
        ini_set('display_errors', 1);
        
        $is_id = isset($_POST['id']) && $_POST['id'];
        $is_pw = isset($_POST['pw']) && $_POST['pw'];
        $is_submit = isset($_POST['submit']);
        
        setcookie("PHPSESSID", "delete", time() - 3600, "/");
      
        if($is_id && $is_pw && $is_submit) {
          // 로그인 로직 처리
          $login_result = login($_POST['id'], $_POST['pw']);
          echo $login_result;
          if ($login_result > 0) {

            $token = $jwt->hashing(array(
              'exp' => time() + (360 * 30), // 만료기간
              'iat' => time(), // 생성일
              'id' => $_POST['id']
            ));

            $refreshToken = $jwt->hashing(array(
              'exp' => time() + (360 * 60), // 만료기간
              'iat' => time(), // 생성일
              'id' => $_POST['id']
            ));
            
            $update = updateRefreshToken($refreshToken, $_POST['id']);
            if ($update){
              setcookie('TOKEN', $token, time() + (360 * 30), "/");
              setcookie('REFRESH_TOKEN', refreshToken, time() + (360 * 60), "/");
              header('Location: index.php');
            }
          }
        }
      ?>

 

login_function.php

<?php
    require_once 'db_connection.php';
    
    
    function idCount($id) {
        global $db_conn;

        $sql = "select count(USER_ID) as count from user where USER_ID ='" . $id . "'";
        $result = mysqli_query($db_conn, $sql);
        $row = mysqli_fetch_array($result);
        return $row['count'];
    }
    
    
    function register($id, $name, $pw) {
        global $db_conn;
        
        $pw = hash("sha256", $pw);
        // echo "pw : {$pw}";

        $sql = "INSERT INTO user (USER_ID, USER_NM, PW) VALUE(
            '{$id}'
            , '{$name}'
            , '{$pw}'
        )";

        echo "sql: {$sql}";

        return mysqli_query($db_conn, $sql);
    }

    function login($id, $pw) {
        global $db_conn;
        $pw = hash("sha256", $pw);
        $sql = "SELECT COUNT(USER_ID) as count 
        FROM user
        WHERE USER_ID = '{$id}' and PW = '{$pw}'";

        $result = mysqli_query($db_conn, $sql);
        $row = mysqli_fetch_array($result);

        return $row['count'];
    }

    function updateRefreshToken($refresh, $id) {
        global $db_conn;

        $sql = "update user set REFRESH_TOKEN = '{$refresh}' where USER_ID = '{$id}'";
        return mysqli_query($db_conn, $sql);
    } 
?>

 

 

index.php

<?php
  require_once './lib/jwt.php';
  ini_set('display_errors', 1);
  session_start();
  

  if(!isset($_COOKIE['TOKEN']) || !$jwt -> verify($_COOKIE['TOKEN']) ) {
      header('location:login.php');
      eixt;
  }
  $token = $_COOKIE['TOKEN'];
  echo '<br>';
  // var_dump($token);
  echo "decode: {$jwt->dehashing($token)['id']}";
?>

 

 

이후 로그인이 필요한 페이지로 이동 시 Token을 사용하여 로그인 여부를 확인하면 된다.

 

index.php

'웹 해킹 코스 > 과제' 카테고리의 다른 글

4-2 게시판 구현하기(게시판 등록)  (0) 2023.11.19
4-1 javascript를 사용한 키로거  (0) 2023.11.16
3-1(로그인 케이스)  (0) 2023.11.09
2-2 DB를 사용한 회원가입, 로그인  (0) 2023.11.02
2-1 php와 DB연결  (0) 2023.11.02

+ Recent posts