일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | ||||||
2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 |
- sql injection point
- csrf
- sql injection
- file upload
- XSS
- JWT
- 세션
- cookie 탈취
- 과제
- Error based sql injection
- 쿠키
- JS
- MySQL
- Python
- cors
- 로그인
- lord of sql injection
- php
- Cross Site Request Forgery
- 로그인페이지
- 게시판 만들기
- blind sql injection
- css
- CTF
- Los
- 모의해킹
- lord of sqli
- 웹개발
- Reflected Xss
- union sql injection
- Today
- Total
Almon Dev
JWT로 로그인 구현하기 본문
JWT로 로그인 구현하기
php에서 jwt를 사용하기 위해서는 firebase/php-jwt라는 패키지를 사용해야 합니다.
php-jwt를 설치하기 위해서는 php의 패키지 관리 툴인 composer를 사용해야 합니다.
composer 설치하기
php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');"
php -r "if (hash_file('sha384', 'composer-setup.php') === 'dac665fdc30fdd8ec78b38b9800061b4150413ff2e3b6f88543c636f7cd84f6db9189d43a81e5503cda447da73c7e5b6') { echo 'Installer verified'; } else { echo 'Installer corrupt'; unlink('composer-setup.php'); } echo PHP_EOL;"
php composer-setup.php
php -r "unlink('composer-setup.php');"
sudo mv composer.phar /usr/local/bin/composer
php-jwt 설치하기
// 웹루트 폴더로 이동해서 해야합니다
composer require firebase/php-jwt
JWT 클래스
jwt_auth.php
<?php
require_once 'vendor/autoload.php';
use Firebase\JWT\JWT;
use Firebase\JWT\Key;
use Firebase\JWT\ExpiredException;
class Jwt_auth {
private static $secret_key = "DA5A431AD2AA76963B2DF723264CECCCDEF";
private static $algorithm = "HS256";
private static $cookie_name = "token";
private static $login_page = "login2.php";
private static $login_scess_page = "login_successful.php";
public static function auth(): bool|stdClass {
if(isset($_COOKIE[self::$cookie_name])){
$token = $_COOKIE[self::$cookie_name];
try {
// 디코딩과 서명 검증
$token_d = JWT::decode($token,new Key(
self::$secret_key, self::$algorithm
));
return $token_d;
} catch (Exception $e) {
// 검증실패
}
}
return false;
}
public static function createToken($id):bool|string {
$payload = [
"sub" => $id,
"iat" => time(),
"exp" => time() + (60 * 60),
];
try {
$token = JWT::encode($payload, self::$secret_key, self::$algorithm);
setcookie("token", $token, time() + (60 * 60),"/");
} catch (Exception $e) {
return false;
}
return $token;
}
public static function delToken() {
setcookie(self::$cookie_name,"", time() - (60 * 60),"/");
}
}
?>
require_once 'vendor/autoload.php';
vendor/autoload.php는 composer로 설치한 패키지를 자동으로 포함해 줍니다.
use Firebase\JWT\JWT;
use Firebase\JWT\Key;
use Firebase\JWT\ExpiredException;
use를 이용해서 본래는 \Firebase\JWT\JWT 이렇게 써야 할 것을 JWT로 쓸 수 있도록 네임스페이스를 불러옵니다.
class Jwt_auth {
jwt의 기능을 관리하기 위해서 Jwt_auth라는 이름의 클래스를 정의합니다.
private static $secret_key = "DA5A431AD2AA76963B2DF723264CECCCDEF";
private static $algorithm = "HS256";
private static $cookie_name = "token";
private static $login_page = "login2.php";
private static $login_scess_page = "login_successful.php";
클래스가 가지는 속성들을 정의합니다.
변수의 앞에 붙은 private static은 접근 제어자와 그 외 수정자를 의미합니다.
접근 제어자
객체의 속성, 메서드에 대한 접근 범위를 수정합니다.
public : 클래스의 외부에서 이 속성에 접근할 수 있습니다.
ex) $jiho = new Student(); echo jigo->grades;
private : 클래스의 내부에서만 접근할 수 있습니다.
=> 외부에서 속성에 직접 접근할 수 없어서 값을 받아오는 메서드를 따로 만들어줘야 합니다.
ex) $jiho = new Student(); echo jigo->grades (X) echo jigo->getGrades() (O)
protected : 클래스의 내부와 상속받은 자식 클래스에서 접근할 수 있습니다.
=> 외부에서는 접근이 불가능하고, 자식 클래스의 메서드와 클래스 자신의 메서드에서만 접근이 가능합니다.
수정자
클래스, 메서드, 속성의 동작방식과 특성을 지정합니다.
=> 접근 제어자도 수정자에 포함됩니다.
static : 클래스를 생성하지 않고도 사용할 수 있습니다.
=> new 명령어로 객체를 생성하지 않고도 내부의 속성이나 메서드에 접근할 수 있습니다.
=> 클래스가 static이 아니어도 static인 메서드를 사용할 수 있습니다.
=> static은 객체를 생성하지 않아도 메모리에 존재하므로 남발하면 성능에 문제가 생깁니다.
ex) class Stduent { static function grades(); } $score = Student::grades();
final : final 클래스는 클래스가 상속할 수 없다는 뜻이고 final 메서드는 오버라이딩(덮어쓰기)이 불가능합니다.
ex) final class Student {} $yoin = new Student() (X)
abstract : abstract 클래스는 추상 클래스로 상속 시 하나 이상의 메서드를 직접 구현해야 한다는 뜻입니다.
ex) abstract class Student { abstract function grades();}
auth()
public static function auth(): bool|stdClass {
if(isset($_COOKIE[self::$cookie_name])){
$token = $_COOKIE[self::$cookie_name];
try {
// 디코딩과 서명 검증
$token_d = JWT::decode($token,new Key(
self::$secret_key, self::$algorithm
));
return $token_d;
} catch (Exception $e) {
// 검증실패
}
}
return false;
}
토큰이 존재하는지, 존재한다면 유효한 토큰인지 검사한 뒤 디코딩된 토큰을 반환하는 메서드입니다.
public static function auth(): bool|stdClass {
외부에서 접근 가능한 static 메서드인 auth를 정의합니다. :bool|stdClass는 반환 타입을 나타냅니다.
bool인 true, false와 stdClass인 JWT토큰을 디코딩한 값을 반환합니다.
if(isset($_COOKIE[self::$cookie_name])){
$token = $_COOKIE[self::$cookie_name];
self::$cookie_name은 클래스의 static 속성인 $cookie_name의 값을 불러오는 것입니다.
token이라는 이름을 가진 쿠키가 있는지 확인하고 $token에 값을 대입합니다.
try {
// 디코딩과 서명 검증
$token_d = JWT::decode($token,new Key(
self::$secret_key, self::$algorithm
));
return $token_d;
JWT의 static 메서드인 decode를 이용해서 서명을 검증하고 검증이 완료된 토큰을 디코딩해서 $token_d에 대입합니다.
decode함수는 인자로 토큰과 시크릿키, 암호화에 사용된 알고리즘을 받습니다.
그리고 return으로 디코딩된 stdClass타입의 토큰값을 반환합니다.
} catch (Exception $e) {
// 검증실패
}
}
return false;
try문에서 에러가 날 경우 catch에서 에러를 잡아와서 {} 안에 코드를 실행합니다.
검증이 실패하거나 토큰이 없는 경우 false를 리턴합니다.
createToken()
public static function createToken($id):bool|string {
$payload = [
"sub" => $id,
"iat" => time(),
"exp" => time() + (60 * 60),
];
try {
$token = JWT::encode($payload, self::$secret_key, self::$algorithm);
setcookie("token", $token, time() + (60 * 60),"/");
} catch (Exception $e) {
return false;
}
return $token;
}
유저의 ID값을 받아와서 jwt토큰을 만들어서 반환하는 메서드입니다.
public static function createToken($id):bool|string {
public static 함수인 createToken을 정의합니다. 반환값은 bool과 string입니다.
$payload = [
"sub" => $id,
"iat" => time(),
"exp" => time() + (60 * 60),
];
jwt토큰에서 사용될 페이로드로, sub는 ID를 iat는 생성시간을 exp는 만료시간을 의미합니다.
try {
$token = JWT::encode($payload, self::$secret_key, self::$algorithm);
setcookie("token", $token, time() + (60 * 60),"/");
JWT클래스의 static 메서드인 encode를 이용해서 토큰을 생성해 $token에 저장합니다.
encode는 페이로드와 비밀키, 암호화 알고리즘은 인수로 받습니다.
setcookie를 이용해서 브라우저에 토큰을 저장합니다.
} catch (Exception $e) {
return false;
}
return $token;
}
생성에 실패할 경우 false를 반환하고, 성공할 경우 만들어진 jwt토큰을 반환합니다.
delToken()
public static function delToken() {
setcookie(self::$cookie_name,"", time() - (60 * 60),"/");
}
delToken은 브라우저의 쿠키에 저장된 토큰을 제거하는 메서드입니다.
setcookie를 이용해 쿠키의 만료시간을 1시간 전으로 설정해 브라우저에서 삭제합니다.
로그인 페이지
login2.php
<?php
//JWT
require_once("jwt_auth.php");
if(Jwt_auth::auth()) {
header("Location: ".self::$login_scess_page);
exit;
};
?>
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Almond Login</title>
<link rel="stylesheet" href="/css/login2.css">
</head>
<body>
<container class="login-container">
<form class="login-form" action="login_proc2.php" method="post">
<div class="logo-container">
<a href="/login2.php"><img src="/imgs/almond.png" alt="logo" class="logo"></a>
</div>
<h2>Almond</h2>
<input type="text" class="login-input" name="id" placeholder="아이디 또는 전화번호" required>
<input type="password" class="login-input" name="passwd" placeholder="비밀번호" required>
<?php
if (isset($_GET['result'])) {
$result = $_GET['result'];
if ($result == "failed") {
echo '<p class="login-failed">로그인에 실패하셨습니다.</p>';
}
}
?>
<button type="submit" class="login-submit">로그인</button>
</form>
</container>
<div class="login-more">
<a href="#">아이디 찾기</a>
<a href="#">비밀번호 찾기</a>
<a href="sign_up.php">회원가입</a>
</div>
<footer>
<a href="https://www.flaticon.com/kr/free-icons/" title=" 아이콘" class="logo-source"> 아이콘 제작자: Freepik - Flaticon</a>
</footer>
</body>
</html>
<?php
//JWT
require_once("jwt_auth.php");
if(Jwt_auth::auth()) {
header("Location: ".self::$login_scess_page);
exit;
};
?>
jwt_auth.php를 require 해서 auth() 메서드로 토큰이 인증에 성공하면 로그인 프로필 페이지로 이동합니다.
login_proc2.php
<?php
include("mysql.php");
require("jwt_auth.php");
use Firebase\JWT\JWT;
$id = $_POST["id"];
$pass = $_POST["passwd"];
$url = '/login_successful.php';
$sql = "select nickname from users where id='$id' and password='$pass';";
$sql_result = runSQL($sql);
if ($row = mysqli_fetch_array($sql_result)) {
$nick = $row['nickname'];
$update_sql = "update users set login=1 where nickname='$nick';";
runSQL($update_sql);
// JWT 생성
// $secret_key = secretKey();
// $payload = [
// "iat" => time(),
// "exp" => time() + (60 * 60),
// "user_id" => $id,
// ];
$token = Jwt_auth::createToken($id);
header("location:$url");
exit;
}
header('Location: login2.php?result=failed');
?>
include("mysql.php");
require("jwt_auth.php");
use Firebase\JWT\JWT;
mysql.php와 jwt_auth.php를 불러옵니다.
use를 이용해서 JWT 클래스의 네임 스페이스를 불러옵니다.
$token = Jwt_auth::createToken($id);
header("location:$url");
exit;
Jwt_auth클래스의 createToken메서드를 이용해 토큰을 생성하고, 프로필 페이지로 이동합니다.
프로필 페이지
login_successful.php
<?php
require_once("jwt_auth.php");
require_once("mysql.php");
$nickname = "";
if($token = Jwt_auth::auth()){
try {
//인증 성공
$user_id = $token->sub;
$sql = "select nickname from users where id='$user_id'";
$nickname = runSQL($sql)->fetch_array()['nickname'];
} catch(Exception $e) {
// 인증 실패
}
}else {
header("Location: login2.php");
exit;
}
?>
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Almond</title>
<link rel="stylesheet" href="/css/login_successful.css">
<script src="./script/logout.js" defer></script>
<script src="./script/mypage.js" defer></script>
</head>
<body>
<container class="profile-container">
<div class="profile">
<img src="/imgs/almond-profile.jpg" class="profile_img">
<div class="profile_info">
<h2 class="profile_name"><?= $nickname; ?></h2>
<p class="profile_subs">구독자 <strong>0명</strong></p>
</div>
</div>
<div class="profile_link">
<a href="#" class="link_tab"><p>글쓰기</p></a>
<!-- <a href="mypage.php" class="link_tab"><p>마이페이지</p></a> -->
<?php
printf('<button class="link_tab mypage-btn" onclick=\'mypage("%s")\'>마이페이지</button>', $nickname);
?>
<a href="logout.php" class="link_tab"><p>로그아웃</p></a>
</div>
<div class="content"></div>
<button class="mypageset-btn">수정하기</button>
</container>
</body>
</html>
if($token = Jwt_auth::auth()){
try {
//인증 성공
$user_id = $token->sub;
$sql = "select nickname from users where id='$user_id'";
$nickname = runSQL($sql)->fetch_array()['nickname'];
} catch(Exception $e) {
// 인증 실패
}
}else {
header("Location: login2.php");
exit;
}
Jwt_auth를 이용해서 토큰을 인증하고 인증에 성공하면 닉네임을 가져온 뒤 프로필 페이지를 출력하고,
인증에 실패하면 로그인 페이지로 이동시킵니다.
로그아웃
logout.php
<?php
require_once("jwt_auth.php");
if(Jwt_auth::auth()){
Jwt_auth::delToken();
}
header("location: login2.php");
?>
Jwt_auth의 delToken 메서드로 쿠키를 삭제합니다.
마무리
'모의해킹 > 웹 개발' 카테고리의 다른 글
게시판 만들기 #1 (0) | 2024.11.11 |
---|---|
로그인 로직 구현하기(과제) (1) | 2024.11.04 |
세션으로 로그인 구현하기(모의해킹 공부) (0) | 2024.11.01 |
쿠키로 로그인 구현하기 (모의해킹 공부) (0) | 2024.10.31 |
모의해킹 공부 정리 12일차 (중복확인) (0) | 2024.10.28 |