Almon Dev

게시판 만들기 #7 (아이디 비밀번호 찾기) 본문

모의해킹/웹 개발

게시판 만들기 #7 (아이디 비밀번호 찾기)

Almon 2025. 1. 22. 19:36

 

 추가한 기능

 

아이디 찾기

이름과 이메일을 이용해 아이디를 찾는 기능입니다.

 

 forget_id.php

<?php
if(isset($_POST['name']) && isset($_POST['email'])) {
    require_once('mysql.php');

    $name = addslashes($_POST['name']);
    $email = addslashes($_POST['email']);

    $sql = "select user_id from users where name='$name' and email='$email'";
    $result = runSQL($sql);

    if($result->num_rows == 1) {
        $id = $result->fetch_array()['user_id'];
        $find_message = "<h1>아이디를 찾았습니다.<br>ID : $id</h1>";
    }else {
        $find_message = "<h1>아이디를 찾지 못했습니다.</h1>";
    }
}
?>
<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>아이디 찾기</title>
    <link rel="stylesheet" href="/css/forget_id.css">
</head>
<body>
    <container class="find-id-container">
        <form action="" method="post" class="find-id-form">
            <div class="logo-container">
                <a href="/login2.php"><img src="/imgs/almond.png" alt="logo" class="logo"></a>
            </div>
            <h2>Almond</h2>
            <h2>아이디 찾기</h2>
            <?= isset($find_message) ? $find_message : '' ?>
            <input type="text" name="name" placeholder="이름" class="find-id-input name-input" required>
            <input type="email" name="email" placeholder="이메일" class="find-id-input email-input" required>
            <button type="submit" class="find-id-submit">아이디 찾기</button>
        </form>
    </container>
</body>
</html>

 

if(isset($_POST['name']) && isset($_POST['email'])) {
    $name = addslashes($_POST['name']);
    $email = addslashes($_POST['email']);

이름과 이메일이 포함된 POST 요청이 왔을 때 $name과 $email변수에 저장합니다.

addslashes() 함수는 sql문에 포함되었을 때 문제가 될만한 특수문자에 \를 붙여서 문자로 취급하도록 만듭니다.

 

    $sql = "select user_id from users where name='$name' and email='$email'";
    $result = runSQL($sql);

    if($result->num_rows == 1) {

이메일과 이름이 동일한 sql문의 결과가 한 개인 경우만 if문을 실행합니다.

결과가 하나인 경우만 실행하는 이유는 혹시라도 sql injection을 통해 결과가 여러 개일 경우 출력하지 않기 위함입니다.
=> prepared statement를 사용하는 것이 sql injection에 가장 안전합니다.

Prepared Statement
SQL 질의문을 유저의 입력값 부분만 비워둔 채 미리 컴파일해서 쿼리문의 구조 변환이 불가능하게 만듭니다.

 

 

    if($result->num_rows == 1) {
        $id = $result->fetch_array()['user_id'];
        $find_message = "<h1>아이디를 찾았습니다.<br>ID : $id</h1>";
    }else {
        $find_message = "<h1>아이디를 찾지 못했습니다.</h1>";
    }

$find_message에 결과를 출력한 문자열을 삽입합니다.

 

<?= isset($find_message) ? $find_message : '' ?>

$find_message가 있을 경우 html에 $find_message를 삽입합니다.

이름과 이메일 입력
찾은 아이디 출력
아이디 찾기 실패


비밀번호 찾기

이름과 아이디 이메일을 이용해 아이디를 찾는 기능입니다.

 

 forget_pw.php

<?php
session_start();
if(isset($_POST['name']) && isset($_POST['email']) && isset($_POST['id'])) {
    require_once('mysql.php');

    $name = addslashes($_POST['name']);
    $id = addslashes($_POST['id']);
    $email = addslashes($_POST['email']);

    $sql = "select user_id from users where name='$name' and email='$email' and user_id='$id'";
    $result = runSQL($sql);

    if($result->num_rows == 1) {
        $id = $result->fetch_array()['user_id'];
        $_SESSION['id'] = $id;
        header('location: forget_pw_proc.php');
        exit;
    }else {
        $find_message = "<h1>계정정보를 찾지 못했습니다.</h1>";
    }
}
?>
<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>비밀번호 찾기</title>
    <link rel="stylesheet" href="/css/forget_pw.css">
</head>
<body>
    <container class="find-pw-container">
        <form action="" method="post" class="find-pw-form">
            <div class="logo-container">
                <a href="/login2.php"><img src="/imgs/almond.png" alt="logo" class="logo"></a>
            </div>
            <h2>Almond</h2>
            <h2>비밀번호 찾기</h2>
            <?= isset($find_message) ? $find_message : '' ?>
            <input type="text" name="name" placeholder="이름" class="find-pw-input name-input" required>
            <input type="text" name="id" placeholder="아이디" class="find-pw-input id-input" required>
            <input type="email" name="email" placeholder="이메일" class="find-pw-input email-input" required>
            <button type="submit" class="find-pw-submit">비밀번호 찾기</button>
        </form>
    </container>
</body>
</html>

이름과 이메일 아이디를 입력하면 sql 질의를 통해 해당하는 아이디를 찾고 세션에 저장하는 코드입니다.

세션을 사용하는 이유는 해당하는 서버에서 저장하고 사용하기 위함입니다.
=> GET이나 POST 등을 이용해서 요청받으면 클라이언트에서 변조 위험이 있습니다.
=> 비밀번호를 변경하는 기능을 세션을 가진 이용자만 사용하게 하기 위함입니다.

 

session_start();
if(isset($_POST['name']) && isset($_POST['email']) && isset($_POST['id'])) {
    require_once('mysql.php');

session_start()로 세션을 생성합니다.

name, email, id가 POST 요청으로 온 경우만 if문을 실행합니다.

 

    $name = addslashes($_POST['name']);
    $id = addslashes($_POST['id']);
    $email = addslashes($_POST['email']);

addslashes 함수를 이용해서 sql 문에 방해되는 특수문자의 앞에 \를 붙여줍니다.

=> sql injection 혹은 sql문 에러를 방지

 

    $sql = "select user_id from users where name='$name' and email='$email' and user_id='$id'";
    $result = runSQL($sql);

    if($result->num_rows == 1) {
        $id = $result->fetch_array()['user_id'];
        $_SESSION['id'] = $id;

sql 질의문을 통해 이름, 이메일, 아이디가 동일한 계정의 user_id가 하나인 경우 user_id를 세션에 저장합니다.

 

        header('location: forget_pw_proc.php');
        exit;

Location 헤더를 이용해 forget_pw_proc.php로 리다이렉트 시킵니다.

 

}else {
        $find_message = "<h1>계정정보를 찾지 못했습니다.</h1>";
    }

user_id가 하나가 아닐 경우 $find_message에 실패 메시지를 삽입합니다.

 

 forget_pw_proc.php

<?php
require_once('mysql.php');
session_start();
if (isset($_SESSION['id'])) {
    $id = $_SESSION['id'];
    if (isset($_POST['pass']) && isset(($_POST['passCheck']))) {
        $pass = $_POST['pass'];
        $pass_check = $_POST['passCheck'];
        $pass_filtered = preg_replace("/\s+/", "", $pass);
        // var_dump($pass);
        // var_dump($pass_filtered === $pass);
        if ($pass === $pass_check && $pass_filtered === $pass) {
            $pass_hash = password_hash($pass, PASSWORD_ARGON2ID);
            $sql = "update users set password='$pass_hash' where user_id='$id'";
            // echo "$sql";
            $result = runSQL($sql);
            if($result) {
                unset($_SESSION['id']);
                session_destroy();
                header('location: login2.php');
            }else {
                $fail_message = '<h1>비밀번호 변경에 실패했습니다.</h1>';
            }
        }else {
            $fail_message = '<h1>비밀번호에 공백은 불가능합니다.</h1>';
        }
    }
}else {
    die('잘못된 접근입니다.');
}
?>
<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>비밀번호 변경</title>
    <link rel="stylesheet" href="/css/forget_pw.css">
    <script src="/script/pw_check.js"></script>
</head>
<body>
    <container class="find-pw-container">
        <form action="" method="post" class="find-pw-form">
            <div class="logo-container">
                <a href="/login2.php"><img src="/imgs/almond.png" alt="logo" class="logo"></a>
            </div>
            <h2>Almond</h2>
            <h2>비밀번호 변경</h2>
            <?= isset($fail_message) ? $fail_message : '' ?>
            <input type="password" name="pass" placeholder="비밀번호" class="find-pw-input pass-input" required>
            <input type="password" name="passCheck" placeholder="비밀번호 확인" class="find-pw-input pass-input-check" required>
            <button type="submit" class="find-pw-submit">비밀번호 변경</button>
        </form>
    </container>
</body>
</html>

세션에 id가 있을경우 비밀번호를 변경하고, 없을 경우 에러 메시지를 출력하는 코드입니다.

 

session_start();
if (isset($_SESSION['id'])) {
    $id = $_SESSION['id'];

세션에 id가 있을 경우 $id 변수에 세션의 id를 저장합니다.

 

    if (isset($_POST['pass']) && isset(($_POST['passCheck']))) {
        $pass = $_POST['pass'];
        $pass_check = $_POST['passCheck'];
        $pass_filtered = preg_replace("/\s+/", "", $pass);

pass와 passCheck가 post로 요청이 오면 $pass, $pass_check 변수에 저장합니다.

preg_replace함수로 공백을 빈칸으로 치환한 뒤 $pass_filtered 변수에 저장합니다.

preg_replace 함수는 정규식을 통해 문자열을 검색하고 일치하는 패턴을 다른 문자로 변경해 줍니다.
preg_replace(정규식(검색), 변경할 문자, 변경할 문자열)

/\s+/ 정규식
/  / : / 사이의 문자열을 정규식으로 해석합니다.
\s : 공백을 뜻합니다. ex) \t \n \r space 등

 

        if ($pass === $pass_check && $pass_filtered === $pass) {
            $pass_hash = password_hash($pass, PASSWORD_ARGON2ID);

 

비밀번호와 비밀번호 확인, 비밀번호와 공백을 제거한 비밀번호가 일치한다면 if문을 실행합니다.

db에서 사용 중인 ARGON2ID 해시 알고리즘을 적용한 비밀번호를 $pass_hash에 저장합니다.

 

            $sql = "update users set password='$pass_hash' where user_id='$id'";
            // echo "$sql";
            $result = runSQL($sql);

update sql문을 이용해서 비밀번호를 수정합니다.

 

            if($result) {
                unset($_SESSION['id']);
                session_destroy();
                header('location: login2.php');
            }else {
                $fail_message = '<h1>비밀번호 변경에 실패했습니다.</h1>';
            }

비밀번호 변경에 성공하면 세션을 삭제하고 로그인페이지로 리다이렉트 시킵니다.

실패한 경우에는 실패메시지를 $fail_message에 저장합니다.

세션을 삭제하는 이유는 더이상 사용자를 식별할 필요가 없기 때문입니다.
사용자가 다시 비밀번호를 찾을 경우 다시 인증을 해야합니다.

비밀번호 찾기 페이지 접근
테스트용 계정정보 입력
비밀번호 변경 페이지
공백을 포함한 비밀번호