Almon Dev

로그인 로직 구현하기(과제) 본문

모의해킹/웹 개발

로그인 로직 구현하기(과제)

Almon 2024. 11. 4. 14:29

로그인 로직 구현하기

 

사전작업

비밀번호에 해쉬를 저장하기 위해서 varchar(512)로 최대 512바이트까지 저장하도록 변경했습니다.

alter table users modify column password varchar(512) not null;

 

 


식별 - 인증 동시

sql문에서 식별과 인증을 동시에 하는 로직입니다.

login1_proc.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);
        $token = Jwt_auth::createToken($id);

        header("location:$url");
        exit;
    }
    
    header('Location: login2.php?result=failed');
?>

 

    $sql = "select nickname from users where id='$id' and password='$pass';";

이 코드처럼 where과 and를 이용해 식별과 인증을 동시에 처리합니다.

 


식별 - 인증 분리

login2_proc.php

<?php
    include("mysql.php");
    require("jwt_auth.php");
    use Firebase\JWT\JWT;   
    
    $url = '/login_successful.php';
    $id = $_POST["id"];
    $pass = $_POST["passwd"];
    
    $sql = "select nickname,password from users where id='$id';";    
    $sql_result = runSQL($sql);
    
    if ($row = mysqli_fetch_array($sql_result)) {
        $nick = $row['nickname'];
        $pass_db = $row['password'];
        if($pass == $pass_db) {
            $update_sql = "update users set login=1 where nickname='$nick';";
            runSQL($update_sql);
            $token = Jwt_auth::createToken($id);
    
            header("location:$url");
            exit;
        }
    }
    
    header('Location: login2.php?result=failed');

?>
    $sql = "select nickname,password from users where id='$id';";    
    $sql_result = runSQL($sql);
    
    if ($row = mysqli_fetch_array($sql_result)) {
        $nick = $row['nickname'];
        $pass_db = $row['password'];
        if($pass == $pass_db) {

sql문에서 바로 비밀번호를 검사하는 게 아니라 비밀번호 값을 가져와서 사용자가 입력한 값과 비교합니다.


식별 - 인증 동시 + HASH

보안성이 뛰어나다는 argon2id 알고리즘을 사용해서 해쉬를 해보려고 했으나, argon2id나 bcrypt 알고리즘은 랜덤 한 값인 salt를 추가해서 해싱을 거치기 때문에 매번 다른 해시값이 나옵니다.
식별 - 인증을 동시에 하는 경우에는 사용하기가 힘든 것 같습니다.

 

signup_proc.php

<?php 
    include ("mysql.php");         
    header("Content-Type: apllication/json");

    $id = $_POST['id'];
    $nick = $_POST['nickname'];
    $pass = $_POST['pass'];
    $passCheck = $_POST['passCheck'];
    $email = $_POST['email'];
            
    $id_duple = false;
    $nick_duple = false;
    $pass_duple = false;
    $result = false;

    $sql = "select id,nickname from users where id='$id' or nickname='$nick';";
    $sql_result = runSQL($sql);

    if (mysqli_num_rows($sql_result) > 0) {
        while ($row = mysqli_fetch_array($sql_result)) {
            if ($id == $row["id"]) {
                $id_duple = true;
            }

            if ($nick == $row['nickname']) {
                $nick_duple = true;
            }

        }
    }
            
    if($pass != $passCheck) {
        $pass_duple = true;
    }
            
    if (!$id_duple && !$pass_duple && !$nick_duple) {
        $result = true;
        //$pass = password_hash($pass, PASSWORD_ARGON2ID);
        $pass = hash('sha256', $pass);
        runSQL("insert into users values ('$id', '$nick', '$email', '$pass')");
    }

    echo json_encode(['result' => $result, 'id' => $id_duple, 'nick' => $nick_duple, 'pass' => $pass_duple]);         
?>
        $pass = hash('sha256', $pass);
        runSQL("insert into users values ('$id', '$nick', '$email', '$pass')");

회원가입을 할 때 비밀번호를 sha256 알고리즘을 통해 해싱을 한 뒤 저장됩니다.

 

login3_proc.php

<?php
    include("mysql.php");
    require("jwt_auth.php");
    use Firebase\JWT\JWT;
    
    
    $url = '/login_successful.php';
    $id = $_POST["id"];
    $pass = $_POST["passwd"];
    //$pass_hash = password_hash($pass, PASSWORD_ARGON2ID);
    $pass_hash = hash('sha256', $pass);
    $sql = "select nickname from users where id='$id' and password='$pass_hash';";
    
    $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);

        $token = Jwt_auth::createToken($id);

        header("location:$url");
        exit;
    }
    
    header('Location: login2.php?result=failed');
?>

입력받은 비밀번호를 해싱해서 sql문의 where에 삽입합니다.

 


식별 - 인증 분리 + HASH

signup_proc.php

<?php 
    include ("mysql.php");            
    header("Content-Type: apllication/json");

    $id = $_POST['id'];
    $nick = $_POST['nickname'];
    $pass = $_POST['pass'];
    $passCheck = $_POST['passCheck'];
    $email = $_POST['email'];
            
    $id_duple = false;
    $nick_duple = false;
    $pass_duple = false;
    $result = false;

    $sql = "select id,nickname from users where id='$id' or nickname='$nick';";
    $sql_result = runSQL($sql);

    if (mysqli_num_rows($sql_result) > 0) {
        while ($row = mysqli_fetch_array($sql_result)) {
            if ($id == $row["id"]) {
                $id_duple = true;
            }

            if ($nick == $row['nickname']) {
                $nick_duple = true;
            }

        }
    }           
    if($pass != $passCheck) {
        $pass_duple = true;
    }
            
    if (!$id_duple && !$pass_duple && !$nick_duple) {
        $result = true;
        $pass = password_hash($pass, PASSWORD_ARGON2ID);
        runSQL("insert into users values ('$id', '$nick', '$email', '$pass', '0')");
    }
    echo json_encode(['result' => $result, 'id' => $id_duple, 'nick' => $nick_duple, 'pass' => $pass_duple]);         
?>
        $pass = password_hash($pass, PASSWORD_ARGON2ID);
        runSQL("insert into users values ('$id', '$nick', '$email', '$pass', '0')");

argon2id 알고리즘을 이용해서 비밀번호를 저장합니다.

 

login4_proc.php

<?php
    include("mysql.php");
    require("jwt_auth.php");
    use Firebase\JWT\JWT;    
    
    $url = '/login_successful.php';
    $id = $_POST["id"];
    $pass = $_POST["passwd"];
    $sql = "select nickname,password from users where id='$id';";
    
    $sql_result = runSQL($sql);
    
    if ($row = mysqli_fetch_array($sql_result)) {
        $nick = $row['nickname'];
        $pass_db = $row['password'];
        if(password_verify( $pass, $pass_db)) {
            $update_sql = "update users set login=1 where nickname='$nick';";
            runSQL($update_sql);
            $token = Jwt_auth::createToken($id);
    
            header("location:$url");
            exit;
        }
    }
    
    header('Location: login2.php?result=failed');
?>
    $pass = $_POST["passwd"];
    $sql = "select nickname,password from users where id='$id';";
    
    $sql_result = runSQL($sql);
    
    if ($row = mysqli_fetch_array($sql_result)) {
        $nick = $row['nickname'];
        $pass_db = $row['password'];
        if(password_verify( $pass, $pass_db)) {

password_verify 함수는 db에 저장된 비밀번호의 salt를 추출한 뒤, 사용자에게 입력받은 비밀번호인 $pass에 추출한 salt를 추가하여 argon2id 알고리즘으로 해싱해서 검사합니다.


마무리

insert into users values ('test_hash', 'test_hash', 'test_hash@google.com', '$argon2id$v=19$m=65536,t=4,p=1$ZktUSEY5cFVnWjJXRS5RMQ$v7HpyL7siKDXwis35fU39E8pdpIiCCa7iDWyHd4nTUs', '0')
select nickname from users where id='test_hash' and password='$argon2id$v=19$m=65536,t=4,p=1$UHIualFMUGo5WU5LMjVVRQ$88i1WqH9z6gd2E891XWkihpz70ZOrVxwoNIuuI37rZY'

정말로 다른 해시가 생성되는지 확인하기 위해 식별 - 인증 동시방법을 사용했을 때 로그에 남은 sql문입니다.

 

salt를 추가하는 방식은 같은 비밀번호를 입력하더라도 다른 해시가 나오기 때문에 최근에 사용되는 비밀번호 알고리즘들은 기본적으로 salt를 사용하고 있습니다. 그렇기에 식별 - 인증 분리방법이 더 자주 사용됩니다.

salt 구별
$argon2id$v=19$m=65536,t=4,p=1$UHIualFMUGo5WU5LMjVVRQ$ argon2id에서 salt는 $salt$처럼 $안에 있습니다.