-
3주차 과제모의해킹스터디_과제 2023. 11. 24. 00:46
과제 내용
[1] 오늘 수업 복습로그인 로직 이해(식별/인증)
[2] 지난 과제 (특별과제 제외)[3] 로그인 페이지 (로직 4개)
- 식별/인증 동시
- 식별/인증 분리
- 식별/인증 동시 (with Hash)
- 식별/인증 분리 (with Hash)
(+ 추가 과제)
- jwt 찾아보기
- jwt 로그인 만들어보기
[3] 로그인 페이지 (로직 4개)
기존에 작성했던 코드는 식별/인증을 분리하는 방식이었다.
이어서 나머지 3개를 작성했다.
login_check.php 공통 코드
<?php session_start(); define("DB_SERVER", 'localhost'); define("DB_USERNAME", 'admin'); define("DB_PASSWORD", 'student1234'); define("DB_NAME", 'db_test'); //login.php에서 입력받은 userName, password $userName = $_POST['userName']; $password = $_POST['password']; //userName에 아무것도 입력받지 않았을 때 if ($userName == "") { echo "<script>alert('please enter your ID.');</script>"; echo "<script>location.replace('login.php');</script>"; exit; } else if ($password == "") { echo "<script>alert('please enter the password.');</script>"; echo "<script>location.replace('login.php');</script>"; exit; } /*----------- 식별/인증 코드------------*/ /*----------- 식별/인증 코드------------*/ >
이 코드 뒤에 이어 원하는 방식으로 작성하면 된다.
1. 식별/인증 동시
/* ----------------- 식별/인증 동시 ----------------- */ //DB 접근 $db_conn = mysqli_connect(DB_SERVER, DB_USERNAME, DB_PASSWORD, DB_NAME); $sql = "SELECT * FROM user WHERE userName = '{$userName}' AND password = '{$password}'"; $result = mysqli_query($db_conn, $sql); mysqli_close($db_conn); //쿼리결과가 존재하지 않을 때(식별x or 인증x) = (ID가 존재하지 않음 or 비밀번호 틀림) if ($result->num_rows == 0) { echo "<script>alert('Login Failed!');</script>"; echo "<script>location.replace('login.php');</script>"; exit; } //쿼리결과가 존재한다면(식별o and 인증o) else { //레코드 추출 $row = mysqli_fetch_array($result); //세션변수 생성 $_SESSION['userName'] = $row['userName']; $_SESSION['name'] = $row['name']; echo "<script>location.replace('index.php');</script>"; exit; } /* ------------------------------------------------- */
2. 식별/인증 분리
/* ----------------- 식별/인증 분리 ----------------- */ //DB 접근 $db_conn = mysqli_connect(DB_SERVER, DB_USERNAME, DB_PASSWORD, DB_NAME); $sql = "SELECT * FROM user WHERE userName = '{$userName}'"; $result = mysqli_query($db_conn, $sql); $row = mysqli_fetch_array($result); mysqli_close($db_conn); //쿼리결과가 존재하지 않을 때(식별 X) if ($result->num_rows == 0) { echo "<script>alert('Login userName \"$userName\" is not found');</script>"; echo "<script>location.replace('login.php');</script>"; exit; } //userName는 존재하지만 비밀번호가 다를 때(인증 X) if ($password != $row['password']) { echo "<script>alert('Wrong password');</script>"; echo "<script>location.replace('login.php');</script>"; exit; } //userName와 비밀번호가 맞다면 세션 변수 생성(인증 확인) else { $_SESSION['userName'] = $row['userName']; $_SESSION['name'] = $row['name']; echo "<script>location.replace('index.php');</script>"; exit; } /* ------------------------------------------------- */
3. 식별/인증 동시 (with Hash)
/* -------------- 식별/인증 동시 +Hash -------------- */ //DB 접근 $db_conn = mysqli_connect(DB_SERVER, DB_USERNAME, DB_PASSWORD, DB_NAME); $sql = "SELECT * FROM user WHERE userName = '{$userName}' AND password = sha1('{$password}')"; $result = mysqli_query($db_conn, $sql); $row = mysqli_fetch_array($result); mysqli_close($db_conn); //쿼리결과가 존재하지 않을 때(식별X or 인증X) //(ID가 존재하지 않음 or 비밀번호 틀림) if ($result->num_rows == 0) { echo "<script>alert('Login Failed!');</script>"; echo "<script>location.replace('login.php');</script>"; exit; } //쿼리결과가 존재한다면(식별/인증 통과) else { //세션변수 생성 $_SESSION['userName'] = $row['userName']; $_SESSION['name'] = $row['name']; echo "<script>location.replace('index.php');</script>"; exit; } /* ------------------------------------------------- */
sql 구문에 sha1() 함수를 사용하면 해시를 적용한 비밀번호를 대조할 수 있다.
4. 식별/인증 분리 (with Hash)
/* -------------- 식별/인증 분리 +Hash -------------- */ //DB 접근 $db_conn = mysqli_connect(DB_SERVER, DB_USERNAME, DB_PASSWORD, DB_NAME); $sql = "SELECT * FROM user WHERE userName = '{$userName}'"; $result = mysqli_query($db_conn, $sql); $row = mysqli_fetch_array($result); mysqli_close($db_conn); //쿼리결과가 존재하지 않을 때(식별 X) if ($result->num_rows == 0) { echo "<script>alert('Login userName \"$userName\" is not found');</script>"; echo "<script>location.replace('login.php');</script>"; exit; } //userName는 존재하지만 비밀번호가 다를 때(인증 X) if ($row['password']!=sha1($password)) { echo "<script>alert('Wrong password');</script>"; echo "<script>location.replace('login.php');</script>"; exit; } //userName와 비밀번호가 맞다면 세션 변수 생성(인증 확인) else { $_SESSION['userName'] = $row['userName']; $_SESSION['name'] = $row['name']; echo "<script>location.replace('index.php');</script>"; exit; } /* ------------------------------------------------- */
보안상 hash를 사용하는 게 당연하고, 사용자에게 ID가 잘못되었는지, 비밀번호가 잘못되었는지 알려주는 게 필요하다고 생각해서 현재는 4.식별/인증 분리 (with Hash) 방식을 사용하고 있다.
특별히 3.식별/인증 동시 (with Hash) 방식을 구현하는데 있어서, password_hash() 함수를 사용하는 방식은 같은 평문을 암호화한다 해도 그 값이 달라져서 password_verify() 함수가 아니면 검증하기가 어렵기 때문에 어떻게 해야하나 고민이 많았는데, mysql에서 sha1() 함수를 제공해서 다행이었다. 덕분에 sql 구문에 name 과 password를 조건으로 한번에 입력할 수가 있어서 식별과 인증을 동시에 구현할 수 있었다.
$sql = "SELECT * FROM user WHERE userName = '{$userName}' AND password = sha1('{$password}')";
+ 추가 과제 jwt
jwt에 대한 설명은 3주차 수업 내용을 정리할 때 작성했고,
여기서는 구현에 관해서만 정리해보겠다.
jwt를 현재 로그인 페이지에 적용하기 보다는 test 폴더 안에 새로운 로그인 페이지를 만들어서 실습을 했다.
test 폴더 안의 파일들
jwt_index.php
jwt_login.php
jwt_login_check.php
jwt_logout.php
jwt_login.css
vendor
composer.json
composer.lockphp-jwt를 사용하면 쉽게 jwt를 만들 수 있는데, 위의 vendor, composer.json, composer.lock 은 php-jwt 라이브러리를
설치했을 때 받은 폴더와 파일들이다.
php-jwt는 command창에서 명령어로 설치할 수도 있고, github에서 다운받아 설치할 수도 있다.
(나는 명령어로 설치했다.)
명령어 : composer require firebase/php-jwt
github주소 : https://github.com/firebase/php-jwtphp-jwt 파일을 받으면 위의 폴더와 파일들이 다른 디렉토리에 저장되어 있을텐데, 계속해서 에러가 나서
그냥 하나의 폴더(test) 안에 넣어서 사용했다.
그럼 이제 코드를 작성할 준비가 됐다.
하나씩 살펴보겠다.
jwt_index.php
<?php require_once('vendor/autoload.php'); use Firebase\JWT\JWT; use Firebase\JWT\Key; //jwt 쿠키가 없으면 if (!isset($_COOKIE["jwt"])) { echo "<script>location.replace('jwt_login.php');</script>"; exit; } //jwt 쿠키가 있으면 else { $jwt = $_COOKIE["jwt"]; $key="normaltic"; //jwt 복호화 $decoded = JWT::decode($jwt, new Key($key, 'HS256')); //payload에서 userName, name 가져오기 $userName = $decoded->userName; $name = $decoded->name; //유효기간 체크 $now = time(); if ($decoded->exp <= $now) { echo "<script>alert('Token is expired.');</script>"; echo "<script>location.replace('jwt_logout.php');</script>"; exit; } } ?> <!doctype html> <html lang="ko"> <head> <meta charset="utf-8"> <link rel="stylesheet" href="jwt_login.css"> <title>로그인 페이지 만들기</title> </head> <body> <div class="container"> <a href="jwt_index.php" title="Button border blue/green" class="button btnBorder btnBlueGreen">Home</a> </div> <div class="index-wrapper"> <h2> <?php echo "Hi, $name($userName)"; ?> </h2> <button type="button" class="btn" onclick="location.href='jwt_logout.php'"> LOGOUT </button> </div> </body> </html>
jwt_login.php
<!DOCTYPE html> <html lang="ko"> <head> <meta charset="utf-8"> <link rel="stylesheet" href="jwt_login.css"> <title>로그인</title> </head> <body> <div class="container"> <a href="jwt_index.php" title="Button border blue/green" class="button btnBorder btnBlueGreen">Home</a> </div> <div class="login-wrapper"> <h2>Login</h2> <form method="POST" action="jwt_login_check.php" id="login-form"> <input type="text" name="userName" placeholder="ID"> <input type="password" name="password" placeholder="비밀번호"> <input type="submit" value="Login"> </form> </div> </body> </html>
jwt_login_check.php
<?php require_once('vendor/autoload.php'); use Firebase\JWT\JWT; define("DB_SERVER", 'localhost'); define("DB_USERNAME", 'admin'); define("DB_PASSWORD", 'student1234'); define("DB_NAME", 'db_test'); //login.php에서 입력받은 userName, password $userName = $_POST['userName']; $password = $_POST['password']; //userName에 아무것도 입력받지 않았을 때 if ($userName == "") { echo "<script>alert('please enter your ID.');</script>"; echo "<script>location.replace('jwt_login.php');</script>"; exit; } else if ($password == "") { echo "<script>alert('please enter the password.');</script>"; echo "<script>location.replace('jwt_login.php');</script>"; exit; } /* -------------- 식별/인증 분리 +hash -------------- */ //DB 접근 $db_conn = mysqli_connect(DB_SERVER, DB_USERNAME, DB_PASSWORD, DB_NAME); $sql = "SELECT * FROM user WHERE userName = '{$userName}'"; $result = mysqli_query($db_conn, $sql); $row = mysqli_fetch_array($result); mysqli_close($db_conn); //쿼리결과가 존재하지 않을 때(식별 X) if ($result->num_rows == 0) { echo "<script>alert('Login userName \"$userName\" is not found');</script>"; echo "<script>location.replace('jwt_login.php');</script>"; exit; } //userName는 존재하지만 비밀번호가 다를 때(인증 X) if ($row['password']!=sha1($password)) { echo "<script>alert('Wrong password');</script>"; echo "<script>location.replace('jwt_login.php');</script>"; exit; } //userName와 비밀번호가 맞다면 토큰 생성 else { $key = "normaltic"; $payload = array( "userName" => $row["userName"], "name" => $row["name"] ); $jwt = JWT::encode($payload, $key, 'HS256'); setcookie("jwt", $jwt, time() + 86600*30); echo "<script>location.replace('jwt_index.php');</script>"; exit; } /* ------------------------------------------------- */ ?>
jwt_logout.php
<?php setcookie('jwt', '', time() - 1); ?> <script> alert("You've been logged out"); location.replace('jwt_index.php'); </script>
이렇게 해서 만든 jwt 토큰을 https://jwt.io/ 에서 검증할 수 있는데
1.jwt.io 잘 생성된 것 같다.
물론 로그인 포함 다른 기능도 잘 된다.
참조
GitHub - firebase/php-jwt: PHP package for JWT
PHP package for JWT. Contribute to firebase/php-jwt development by creating an account on GitHub.
github.com
PHP JWT 라이브러리 사용법과 예제
JSON Web Token(JWT)은 인터넷에서 정보를 안전하게 전송하기 위해 사용되는 표준 방식입니다. JWT는 Base64 인코딩된 JSON 객체로 구성되며, 토큰의 정보는 디지털 서명을 통해 안전하게 전송됩니다. 이
summer-cat93.tistory.com