IT 성장기 (교육이수)/모의해킹 스터디 (2024.04-09)

[webDev] 로그인 로직 구성 (식별과 인증)

eezy 2024. 5. 26. 18:17

식별과 인증을 구분하여, 로그인 로직을 구성하는 방법에 대해 자세한 코드와 함께 알아 보자. 

 

로그인 로직 구성

<목차>

1. 로그인 사이트 구성

2. 식별 / 인증 동시

3. 식별 / 인증 분리

4. 식별 / 인증 동시 (+HASH)

5. 식별 / 인증 분리 (+HASH)

6. PHP 객체 반환 문법

 

로그인 사이트 구성

로그인 사이트의 html 코드

더보기
<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <link rel="stylesheet" href="logintest.css">
    </head>

    <body>
        <div class="wrapper">
            <form class="form-signin" method="POST" action="logintest.php">       
            <h2 class="form-signin-heading">Login Test</h2>
            <input type="text" class="form-control" name="id" placeholder="User id" required="" autofocus="" />
            <input type="password" class="form-control" name="pass" placeholder="Password" required=""/>
            <label class="checkbox">
                <input type="checkbox" value="remember-me" id="rememberMe" name="rememberMe"> Remember me
            </label>
            <button name="Submit" value="Login" class="btn btn-lg btn-primary btn-block" type="submit">Sign in</button>
            </form>
        </div>
    </body>
</html>

로그인 사이트의 css 코드

더보기
body {
	background: #eee !important;	
}

.wrapper {
    margin-top: 80px;
    margin-bottom: 80px;
}

.form-signin {
  max-width: 300px;
  min-height: 250px;
  padding: 15px 35px 45px;
  margin: 0 auto;
  background-color: #fff;
  border: 1px solid rgba(0,0,0,0.1);  

  .form-signin-heading,
	.checkbox {
      margin-top: 10px;
	  margin-bottom: 30px;
      height: auto;
	}

	.checkbox {
      top: 10px;
      position: relative;
      display: block;
	  font-weight: normal;
	}
    .btn-block {
      display: block;
      width: 96%;
    }

    .btn-group-lg>.btn, .btn-lg {
      padding: 10px 10px;
      font-size: 15px
      line-height: 1;
      border-radius: 6px;
    }

    .btn-primary {
      color: #fff;
      background-color: #337ab7;
      border-color: #2e6da4;
    }

    .btn {
        display: inline-block;
        padding: 10px 12px;
        margin-bottom: 0;
        font-size: 14px;
        font-weight: 400;
        line-height: 1.42857143;
        text-align: center;
        white-space: nowrap;
        vertical-align: middle;
        -ms-touch-action: manipulation;
        touch-action: manipulation;
        cursor: pointer;
        -webkit-user-select: none;
        -moz-user-select: none;
        -ms-user-select: none;
        user-select: none;
        background-image: none;
        border: 1px solid transparent;
        border-radius: 4px;
    }

	.form-control {
	  position: relative;
	  font-size: 16px;
      width: 90%;
	  height: auto;
	  padding: 10px;
    }
}

html {
    -webkit-text-size-adjust: 100%;
    -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
}

로그인 사이트 화면

위 코드로 구성한 화면이다

식별 / 인증 동시

<?php
    //데이터베이스 연결 설정
    $db_connect = mysqli_connect("localhost", "mysql_id", "mysql_pass", "member");
    $db_connect-> set_charset("utf8");

    //데이터베이스 연결 오류 확인
    if ($db_connect->connect_error){
        die("No DB Connection". $db_connect->connect_error);}

    //POST 방식으로 사용자 입력 확인
    $id = $_POST["id"];
    $pass = $_POST["pass"];
    echo "POST ok<br>";
    
    //식별과 인증을 동시
    function authenticateUser($db_connect, $id, $pass) {
        //입력받은 데이터가 db에 존재하는지 확인
        $sql = "SELECT * FROM member WHERE UserId='$id' AND Pass='$pass'";
        $result = $db_connect -> query($sql);

        //Query 오류 확인
        if (!$result) {
            die("Query Failed: " . $db_connect->error);
        }
        
        //사용자 인증 결과 확인
        if($result->num_rows>0){
            echo "login ok<br>";
            echo "$id and $pass";
        }else{
            echo "login fail<br>";
            echo "$id and $pass";
        }
    }
    authenticateUser($db_connect, $id, $pass);
?>

 

아래 방식부터는, 데이터베이스 연결 및 POST 방식으로 데이터를 가져오는 방법은 동일함으로, 함수 부분만 포스팅 하겠다. 

 

여기서 식별과 인증을 함수로 지정한 이유는, 같은 로그인 페이지에서 여러 가지 식별 / 인증 방식 실험을 용이하게 하기 위함이다. 

어떠한 방법을 택할지 정했다면, 함수로 지정하지 않아도 된다. 

 

식별 / 인증 분리

    //식별과 인증을 분리
    function authenticateUser2($db_connect, $id, $pass){
        $sql = "SELECT * FROM member WHERE UserId='$id'";
        $result = $db_connect -> query($sql);

        //사용자 ID 검증
        if($result->num_rows>0){
            $row_pass = $result->fetch_assoc(); // 한 행의 데이터를 가져옴
            //사용자 ID가 일치할 경우에만 비밀번호를 검증
            if($row_pass['Pass']==$pass){
                echo "login ok : 아이디와 비밀번호가 일치합니다. <br>";
            }else{
                echo "login fail : 비밀번호가 일치하지 않습니다. <br>";
            }
        }else{
            echo "login fail : 아이디 또는 비밀번호가 일치하지 않습니다. <br>";
            echo "$id and $pass";
        }
    }
    authenticateUser2($db_connect, $id, $pass);

 

식별 / 인증 동시 (+ HASH)

Client가 입력한 비밀번호를 해쉬처리한 후 식별과 인증을 동시에 한다. 

DB에 저장된 비밀번호는 평문으로 저장되어있다.

 

  • password_hash ("password", hash_algorithm) : 비밀번호를 안전하게 해시화하기 위해 사용된다.
    • 해쉬 알고리즘에는 2가지 매개변수를 작성할 수 있다. 
    • PASSWORD_DEFAULT : 현재 PHP 버전에서 권장하는 기본 알고리즘을 사용, 향후 PHP 버전에서 기본 알고리즘이 변경될 수 있으므로 보안 상 권장된다. 하지만, 현재로는 Bycrpt 알고리즘을 사용한다. 
    • PASSWORD_BCRYPT : Bcrypt 알고리즘을 명시적으로 사용한다. 
  • password_verify (password, hash) : 해시화된 비밀번호와 입력된 비밀번호가 일치하는지 확인하는 함수이다. 
    // 식별과 인증을 동시, 비밀번호 해쉬처리
    function passhash($db_connect, $id, $pass) {
        //입력받은 데이터가 db에 존재하는지 확인
        $sql = "SELECT * FROM member WHERE UserId='$id' AND Pass='$pass'";
        $hashed_pass = password_hash($pass, PASSWORD_DEFAULT); //입력받은 비밀번호를 해쉬로 변경
        $result = $db_connect -> query($sql);
        $row = $result->fetch_assoc();
        password_verify($hashed_pass, $row['Pass']); //해쉬처리된 비밀번호가, DB에 저장된 평문 비밀번호와 일치하는지 확인
        echo "verify ok<br>";

        //Query 오류 확인
        if (!$result) {
            die("Query Failed: " . $db_connect->error);
        }
        
        //사용자 인증 결과 확인
        if($result->num_rows>0){
            echo "login ok <br>id: $id<br>hashed pass: $hashed_pass<br>pass: $pass<br>";
        }else{
            echo "login fail <br>id: $id<br>hashed pass: $hashed_pass<br>pass: $pass<br>";
        }
    }   
    passhash($db_connect, $id, $pass);

 

식별 / 인증 분리 (+ HASH)

	// 식별과 인증 분리, 비밀번호 해쉬처리
     function passhash2($db_connect, $id, $pass) {
        //입력받은 데이터가 db에 존재하는지 확인
        $hashed_pass = password_hash($pass, PASSWORD_DEFAULT);
        $sql = "SELECT * FROM member WHERE UserId='$id'";
        $result = $db_connect -> query($sql);
        echo "id 가져오기 ok<br>";

        if (!$result) {
            die("Query Failed: " . $db_connect->error);
        }

        if($result->num_rows>0){
            $row = $result->fetch_assoc(); // id가 일치하는 행의 데이터를 가져옴
            //사용자 ID가 일치할 경우에만 비밀번호를 검증. DB의 비밀번호와, 해쉬처리된 비밀번호를 검증
            if(password_verify($row['Pass'], $hashed_pass)){
                echo "login ok <br>id: $id<br>hashed pass: $hashed_pass<br>pass: $pass<br>";
            }else{
                echo "login fail : 비밀번호가 일치하지 않습니다.<br>id: $id<br>hashed pass: $hashed_pass<br>pass: $pass<br>";
            }
        }else{
            echo "login fail : 아이디 또는 비밀번호가 일치하지 않습니다. <br>id: $id<br>hashed pass: $hashed_pass<br>pass: $pass<br>";
        }
     }
     passhash2($db_connect, $id, $pass);

 

PHP 객체 반환 문법

$sql을 선언하고, 객체를 가져오는 2가지 방법과 차이점을 알아보자. 

$sql = "SELECT * FROM member WHERE UserId='$id'"; // 동일

$result = $db_connect -> query($sql); // 1번

$result =mysqli_fetch_array($db_connect, $sql); // 2번

 

1번 

  • 데이터베이스에 쿼리를 실행하고, 결과를 나타내는 mysqli_result 객체를 반환한다.
  • 이 객체에는 쿼리에서 선택된 행들에 대한 정보가 포함되어 있습니다. 즉, $result 에는 여러 행이 포함될 수 있다.

2번

  • 이 코드는 쿼리를 실행한 결과에서 한 행을 가져온다.
  • mysqli_fetch_array() 함수는 쿼리 결과에서 한 행을 가져와 배열로 반환한다.
  • 따라서 $db_id에는 쿼리 결과의 한 행만 포함된다.

첫 번째 코드에서는 $id 변수를 사용하여 WHERE 절에 조건을 지정했기 때문에, 해당 조건에 맞는 행들이 모두 선택된다.

따라서 $id가 아닌 다른 행의 정보도 불러올 수 있다.

 

예를 들어, $id가 특정한 사용자의 ID라고 가정하면, 해당 사용자의 정보만 가져오는 것이 아니라, 모든 member 테이블에서 $id와 일치하는 사용자들의 정보가 선택된다. 따라서 $id와 일치하는 여러 행이 있다면, 모든 행들의 정보가 $result 에 포함된다.

 

결론 : 1개의 데이터만 불러오고 싶다면 2번 문법을 / 여러 행을 모두 불러오고 싶다면 1번 문법을 선택하여 작성할 수 있다.

 

데이터 반환 함수의 차이 : fetch_arrayfetch_assoc

둘 다 MySQL 결과 집합에서 데이터를 가져오는 데 사용되지만, 반환되는 데이터의 형식에 차이가 존재한다. 

  1. mysqli_fetch_array():
    • 이 함수는 결과 집합의 다음 행을 숫자 인덱스와 연관 배열 형태로 모두 포함하는 배열을 반환
    • 반환된 배열은 숫자 인덱스와 열 이름 모두로 접근할 수 있다.
    • 따라서 반환된 배열은 많은 메모리를 사용하며, 데이터를 처리하는 데에도 많은 시간이 소요될 있다. 
  2. mysqli_fetch_assoc():
    • 이 함수는 결과 집합의 다음 행을 연관 배열 형태로 반환
    • 반환된 배열은 열 이름을 키(key)로 사용하며, 해당하는 값을 가져올 때 훨씬 직관적이다.
    • 연관 배열 형태로 반환되기 때문에 일반적으로 메모리 사용량이 적고 데이터 처리 속도가 빠르다.

따라서 대부분의 경우, 연관 배열 형태로 데이터를 가져오는 mysqli_fetch_assoc() 함수가 더 효율적이고 사용하기 편리하다. 특히 열 이름을 직접 지정하여 데이터에 접근하는 경우에 유용하다.