반응형

PHP를 사용하여 회원 가입 및 로그인이 되는 간단한 웹사이트를 만들어 보았습니다.

사용자가 입력한 패스워드 검사, 세션 유지나 AES 256으로 평문을 암호하는 코드도 포함되어 있습니다.




웹사이트에 회원 가입하고 로그인하면 해당 사용자를 위한 정보만 보여주는 방법이 궁금해서 진행해보았던 결과물을 정리해보았습니다.  정리하기에는 양이 많아서 우선 구현된 부분과 동작 과정만 해서 올립니다.

추후 코드 설명도 추가해보록 하겠습니다.



PHP도 익숙치 않은데 클래스까지 사용하기에는 무리인지라 제외시켰고 실제로 사용하는 방법과는 다른게 엉뚱하게 구현된 부분도 있을 듯합니다.


개인적인 공부를 위해서 만들어본 웹사이트인지라 보안 쪽은 전혀 신경안썼기 때문에  테스트나 공부 용도로만 사용하세요.



PHP 5.6 버전대를 사용하는 WAMP SERVER를 사용하여 테스트를 했습니다.

http://www.wampserver.com   




1. 전체적 구조


2. 데이터베이스 및 테이블 생성


3. 사용자 등록 및 로그인 과정 설명


4. 전체 소스 코드 및 사용방법

   4-1. admin.php

   4-2. check.php

   4-3. dbcon.php

   4-4. dbinit.php

   4-5.delete.php

   4-6. editform.php

   4-7. head.php

   4-8. index.php

   4-9. logout.php

   4-10. registration.php

   4-11. welcome.php


5. 참고한 사이트



최초 작성 - 2018. 6. 16




1. 전체적 구조



웹사이트에 로그인 기능을 추가하려면 다음 세 가지가 필요합니다.


  • 아이디와 패스워드를 입력할 수 있는 웹페이지

  • 로그인시 필수 정보인 아이디, 패스워드 및 주소 같은 부가 정보를 입력하기 위해 회원가입 웹페이지

  • 아이디와 패스워드를 저장할 테이블



구현된 주요 페이지는 다음과 같습니다. 저는 aeduser라는 폴더를 생성하여 관련 파일들을 모아두었습니다. 그래서  http://localhost/aeduser 주소를 입력해야 로그인 화면이 보입니다.

필요시  http://localhost/ 에서 접근 가능하도록 파일 위치를 변경해서 사용하세요.


데이터베이스 초기화 http://localhost/aeduser/dbinit.php


로그인 화면  http://localhost/aeduser/index.php


사용자 로그인 완료시 화면 http://localhost/aeduser/welcome.php


사용자 등록 화면 http://localhost/aeduser/registration.php


관리자 로그인 완료시 화면 http://localhost/aeduser/admin.php


사용자 정보 편집 화면  http://localhost/aeduser/editform.php




2. 데이터베이스 및 테이블 생성


웹사이트에 로그인을 할 때 입력하는 아이디와 패스워드를 데이터베이스 서버에 저장하기 위해서 필요한 작업입니다.


dbinit.php를 실행할 때 마다 userdb 데이터베이스와  users 테이블을 새로 만듭니다.




users 테이블 구조는 다음과 같습니다.


mysql> DESC userdb.users;
+-------------+--------------+------+-----+-------------------+----------------+
| Field       | Type | Null | Key | Default           | Extra |
+-------------+--------------+------+-----+-------------------+----------------+
| uid         | int(11) | NO   | PRI | NULL    | auto_increment |
| username    | varchar(255) | NO   | | NULL    | |
| password    | varchar(255) | NO   | | NULL    | |
| userprofile | varchar(255) | YES  | | NULL |       |
| salt        | varchar(255) | NO   | | NULL    | |
| regtime     | datetime | NO   | | CURRENT_TIMESTAMP |                |
| is_admin    | tinyint(4) | NO   | | 0    | |
| activate    | tinyint(4) | NO   | | 0    | |
+-------------+--------------+------+-----+-------------------+----------------+
8 rows in set (0.00 sec)

mysql>



관리자 계정인 admin이 테이블에 추가됩니다.


mysql> select uid, username,is_admin, activate from userdb.users;
+-----+----------+----------+----------+
| uid | username | is_admin | activate |
+-----+----------+----------+----------+
|   1 | admin    | 1 |       1 |
+-----+----------+----------+----------+
2 rows in set (0.00 sec)




3. 사용자 등록 및 로그인 과정 설명

http://localhost/aeduser/ 로 접속하면   http://localhost/aeduser/index.php 가 실행되어 로그인 화면이 보이게 됩니다.




웹브라우저 크기를 일정 너비 이하로 줄이면 다음처럼 보이는데.. 항상 위 캡처화면처럼 보이게 하는 방법을 찾아봐야 할 듯합니다.




등록 버튼을 클릭하면 다음처럼 입력 양식이 보여집니다. sourcecodester에서 가져온 소스코드를 기반으로 수정한 거라 왼쪽 위에 있는 것을 그냥 두었습니다.




테스트를 위한 계정 정보를 입력하고 저장을 클릭합니다.  암호 만들 때 일정한 규칙(알파벳,숫자, 특수기호를 섞어 사용하도록)을 사용하도록 적용하지 않은 상태입니다.




다시 로그인 화면으로 돌아옵니다. 테스트용으로 만든 계정으로 로그인을 시도하면 다음과 같은 메시지가 보입니다.




admin 계정으로 로그인하여 계정이 활성화되도록 해주어야 합니다. (디폴트 패스워드는 admin 입니다. )

로그인 후, 다음처럼 등록된 아이디 목록이 보입니다. 계정 활성화 항목이 비활성으로 되어 있습니다. 수정 항목에 있는 Edit를 클릭합니다.




계정 활성화 항목에 있는 체크박스를 체크하고 업데이트를 클릭합니다.




이제 계정 활성화 항목이 활성으로 바뀌었습니다. 상단에 보이는 Log Out을 클릭합니다.





이제 테스트용으로 생성했던 계정으로 로그인이 가능해집니다.

로그인 후, 해당 계정을 생성했던 날짜,시간을 출력하고 있습니다.  


상단에 로그인 한 사용자인 webnautes를 표시해주고 있습니다.

수정을 좀 더 하면  해당 사용자를 위한 정보만 보여주는 웹사이트로 발전시킬 수 있을 듯합니다.





..


..

4. 전체 소스 코드 및 사용방법

php  파일을 저장한 곳에  다음 파일을 압축풀어 넣어두세요.


bootstrap.zip





4-1. admin.php

관리자 계정으로 로그인시 보여지는 화면입니다.  사용자를 삭제하거나 정보를 수정할 수 있습니다.


<?php

   include('dbcon.php');
   include('check.php');
  
   if (is_login()){

       if ($_SESSION['user_id'] == 'admin' && $_SESSION['is_admin']==1)
           ;
       else
           header("Location: welcome.php");
   }else
       header("Location: index.php");

   include('head.php');
?>

<div class="container">
<div class="page-header">
    <h1 class="h2">&nbsp; 사용자 목록</h1><hr>
   </div>
<div class="row">

   <table class="table table-bordered table-hover table-striped" style="table-layout: fixed">  
       <thead>  
       <tr>  
           <th>아이디</th>  
           <th>프로필</th>
           <th>계정 활성화</th>
           <th>수정</th>  
           <th>삭제</th>  
       </tr>  
       </thead>  
 
       <?php  
   $stmt = $con->prepare('SELECT * FROM users ORDER BY username DESC');
   $stmt->execute();

           if ($stmt->rowCount() > 0)
           {
               while($row=$stmt->fetch(PDO::FETCH_ASSOC))
       {
   extract($row);
      
if ($username != 'admin'){
?>  
<tr>  
<td><?php echo $username;  ?></td>
<td><?php echo $userprofile; ?></td>
<td>
<?php
if($activate)
{
echo "활성";
} else{
   echo "비활성";
}
?>
</td>
<td><a class="btn btn-primary" href="editform.php?edit_id=<?php echo $username ?>"><span class="glyphicon glyphicon-pencil"></span> Edit</a></td>
<td><a class="btn btn-warning" href="delete.php?del_id=<?php echo $username ?>" onclick="return confirm('<?php echo $username ?> 사용자를 삭제할까요?')">
<span class="glyphicon glyphicon-remove"></span>Del</a></td>
</tr>

       <?php
}
               }
            }
       ?>
       </table>  
</div>

</body>
</html>




4-2. check.php

사용자가 로그인 중인지 체크하는 is_login 함수와 평문 암호에 AES 256을 적용하기 위해 필요한 함수로 구성되어 있습니다.


<?php
   error_reporting(E_ALL);
   ini_set('display_errors',1);

function is_login(){

   global $con;

   if (isset($_SESSION['user_id']) && !empty($_SESSION['user_id']) ){

       $stmt = $con->prepare("select username from users where username=:username");
       $stmt->bindParam(':username', $_SESSION['user_id']);
       $stmt->execute();
       $count = $stmt->rowcount();

       if ($count == 1){
      
           return true; //로그인 상태
       }else{
           //사용자 테이블에 없는 사람
           return false;
       }
   }else{

       return false; //로그인 안된 상태
   }
}

//https://stackoverflow.com/a/46872528

function encrypt($plaintext, $salt) {
   $method = "AES-256-CBC";
   $key = hash('sha256', $salt, true);
   $iv = openssl_random_pseudo_bytes(16);

   $ciphertext = openssl_encrypt($plaintext, $method, $key, OPENSSL_RAW_DATA, $iv);
   $hash = hash_hmac('sha256', $ciphertext, $key, true);

   return $iv . $hash . $ciphertext;
}

function decrypt($ivHashCiphertext, $salt) {
   $method = "AES-256-CBC";
   $iv = substr($ivHashCiphertext, 0, 16);
   $hash = substr($ivHashCiphertext, 16, 32);
   $ciphertext = substr($ivHashCiphertext, 48);
   $key = hash('sha256', $salt, true);

   if (hash_hmac('sha256', $ciphertext, $key, true) !== $hash) return null;

   return openssl_decrypt($ciphertext, $method, $key, OPENSSL_RAW_DATA, $iv);
}


?>




4-3. dbcon.php

MySQL 서버에 접속하기 위해 필요한 코드입니다.  주요 웹페이지마다 이 파일을 사용하도록 되어 있습니다.


<?php

   $host = 'localhost';
   $username = 'MySQL 계정 아이디';
   $password = 'MySQL 계정 패스워드';
   $dbname = 'userdb';

   $options = array(PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8');

   try {

       $con = new PDO("mysql:host={$host};dbname={$dbname};charset=utf8",$username, $password);
   } catch(PDOException $e) {

       die("Failed to connect to the database: " . $e->getMessage());
   }


   $con->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
   $con->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);

   if(function_exists('get_magic_quotes_gpc') && get_magic_quotes_gpc()) {
       function undo_magic_quotes_gpc(&$array) {
           foreach($array as &$value) {
               if(is_array($value)) {
                   undo_magic_quotes_gpc($value);
               }
               else {
                   $value = stripslashes($value);
               }
           }
       }

       undo_magic_quotes_gpc($_POST);
       undo_magic_quotes_gpc($_GET);
       undo_magic_quotes_gpc($_COOKIE);
   }

   header('Content-Type: text/html; charset=utf-8');
   session_start();
?>




4-4. dbinit.php

데이터베이스 및 테이블 초기화를 위해 사용됩니다.


<?php
error_reporting(E_ALL);
ini_set('display_errors',1);

include('check.php');


$databaseName = 'userdb';
$databaseUser = 'MySQL 계정이름';
$databasePassword = '패스워드';


/*
* 데이터베이스 생성
*/
$pdoDatabase = new PDO('mysql:host=localhost', $databaseUser, $databasePassword);
$pdoDatabase->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$pdoDatabase->exec('DROP DATABASE IF EXISTS userdb;');
$pdoDatabase->exec('CREATE DATABASE IF NOT EXISTS userdb DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci');


/*
* 테이블 생성
*/
$pdo = new PDO('mysql:host=localhost;dbname='.$databaseName, $databaseUser, $databasePassword);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
 
$pdo->exec('DROP TABLE IF EXISTS users;');

$pdo->exec('CREATE TABLE `users` (
`uid` int(11) NOT NULL AUTO_INCREMENT,
`username` VARCHAR(255) NOT NULL,
`password` VARCHAR(255) NOT NULL,
`userprofile` VARCHAR(255),
`salt` VARCHAR(255) NOT NULL,
`regtime` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
`is_admin` tinyint(4) NOT NULL DEFAULT 0,
`activate` tinyint(4) NOT NULL DEFAULT 0,
PRIMARY KEY (`uid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci');


/*
* 관리자 계정 admin 생성
*/
$default_password = 'admin';
$salt = bin2hex(openssl_random_pseudo_bytes(32));
$encrypted_password = base64_encode(encrypt($default_password, $salt));

$createAdmin = $pdo->prepare('INSERT INTO users
(username, password, is_admin, activate, salt) VALUES
   ("admin", :password, 1, 1, :salt)');

$createAdmin->bindparam(":password", $encrypted_password);
$createAdmin->bindparam(":salt", $salt);
$createAdmin->execute();


echo "데이터베이스 초기화에 성공했습니다.\n";




4-5.delete.php

관리자가 선택한 사용자 계정을 삭제하기 위해 사용됩니다.


<?php

   include('dbcon.php');
   include('check.php');
  
   if (is_login()){

       if ($_SESSION['user_id'] == 'admin' && $_SESSION['is_admin']==1)
           ;
       else
           header("Location: welcome.php");
   }else
       header("Location: index.php");


   if(isset($_GET['del_id']))
   {
$stmt = $con->prepare('DELETE FROM users WHERE username =:del_id');
$stmt->bindParam(':del_id',$_GET['del_id']);
$stmt->execute();
   }

   header("Location: admin.php");
?>




4-6. editform.php

관리자가특정 사용자 계정을 활성화시키거나 정보 수정을 위해 사용됩니다.


<?php
  
   include('dbcon.php');
   include('check.php');
  
   if (is_login()){


       if ($_SESSION['user_id'] == 'admin' && $_SESSION['is_admin']==1)
           ;
       else
           header("Location: welcome.php");
   }else
       header("Location: index.php");


if(isset($_GET['edit_id']) && !empty($_GET['edit_id']))
{
$edit_id = $_GET['edit_id'];
$stmt_edit = $con->prepare('SELECT * FROM users WHERE username = :user_id');
$stmt_edit->execute(array(':user_id'=>$edit_id));
$edit_row = $stmt_edit->fetch(PDO::FETCH_ASSOC);
extract($edit_row);
}
else
{
header("Location: index.php");
}


if( ($_SERVER['REQUEST_METHOD'] == 'POST') && isset($_POST['btn_save_updates']))
{
       foreach ($_POST as $key => $val)
       {
           if(preg_match('#^__autocomplete_fix_#', $key) === 1){
               $n = substr($key, 19);
               if(isset($_POST[$n])) {
                   $_POST[$val] = $_POST[$n];
           }
       }
       }



$user_id = $_POST['editusername'];
$user_password = $_POST['editpassword'];
$userprofile = $_POST['edituserprofile'];
if ( isset($_POST['activate'])) $activate=1;
else $activate=0;

try {
$stmt = $con->prepare('select * from users where username=:username');
$stmt->bindParam(':username', $user_id);
$stmt->execute();

  } catch(PDOException $e) {
die("Database error: " . $e->getMessage());
  }

  $row = $stmt->fetch();
  $password = $row['password'];
  $salt = $row['salt'];


if(!isset($errMSG))
{
$stmt = $con->prepare('UPDATE users SET password=:user_password, userprofile=:userprofile, activate=:activate WHERE username=:user_id');
$stmt->bindParam(':user_id',$user_id);
$stmt->bindParam(':userprofile',$userprofile);
$stmt->bindParam(':activate',$activate);
                       $encrypted_password = base64_encode(encrypt($user_password, $salt));
                       $stmt->bindParam(':user_password', $encrypted_password);
$stmt->bindParam(':userprofile',$userprofile);

if($stmt->execute()){
?>
                               <script>
alert('업데이트 성공');
window.location.href='admin.php';
</script>
               <?php
}
else{
$errMSG = "업데이트 실패";
}
}
}
   include('head.php');
?>

<div class="container">
<div>
    <h1 class="h2" align="center">&nbsp; 사용자 정보 수정<a class="btn btn-success" href="admin.php" style="margin-left: 850px"><span class="glyphicon glyphicon-home"></span>&nbsp; Back</a></h1><hr>
   </div>
<form id="myform" method="post" enctype="multipart/form-data" class="form-horizontal" style="margin: 0 300px 0 300px;border: solid 1px;border-radius:4px">
   <?php
if(isset($errMSG)){
?>
       <div class="alert alert-danger">
         <span class="glyphicon glyphicon-info-sign"></span> &nbsp; <?php echo $errMSG; ?>
       </div>
       <?php
}
?>
<table class="table table-responsive">
   <tr>
       <? $r1 = rmd5(rand().mocrotime(TRUE)); ?>
    <td><label class="control-label">아이디</label></td>
       <td>
       <input id="id" class="form-control" type="text" name="<? echo $r1; ?>" value="<?php echo $username; ?>" placeholder="아이디를 입력하세요." autocomplete="off" readonly   />
       <input type="hidden" name="__autocomplete_fix_<? echo $r1; ?>" value="editusername" />
       </td>
   </tr>
   <tr>
       <? $r2 = rmd5(rand().mocrotime(TRUE)); ?>
    <td><label class="control-label">패스워드</label></td>
       <td>
       <?php
       $decrypted_password = decrypt(base64_decode($password), $salt);
       ?>
       <input id="pw" class="form-control" type="password" name="<? echo $r2; ?>" value="<?php echo $decrypted_password; ?>" placeholder="패스워드를 입력하세요."
              autocomplete="off" readonly onfocus="this.removeAttribute('readonly');" required />
       <input type="hidden" name="__autocomplete_fix_<? echo $r2; ?>" value="editpassword" />
       </td>
   </tr>
   <tr>
       <? $r3 = rmd5(rand().mocrotime(TRUE)); ?>
    <td><label class="control-label">프로필</label></td>
       <td>
       <input class="form-control" type="text" name="<? echo $r3; ?>" value="<?php echo $userprofile; ?>" placeholder="프로필을 입력하세요."
              autocomplete="off" readonly onfocus="this.removeAttribute('readonly');" required />
       <input type="hidden" name="__autocomplete_fix_<? echo $r3; ?>" value="edituserprofile" />
       </td>
   </tr>


<tr>
<td><label class="control-label">계정 활성화</label></td>
<td>
<?php if($activate)
{
?>
<input type="checkbox" name="activate" checked >     
<?php } else{
?>

<input type="checkbox" name="activate" >     
<?php
}
?>

</td>
</tr>

   <tr>


       <td colspan="2" align="center">
<button type="submit" name="btn_save_updates" class="btn btn-primary"><span class="glyphicon glyphicon-floppy-save"></span>&nbsp; 업데이트</button>
       <a class="btn btn-warning" href="admin.php"> <span class="glyphicon glyphicon-remove"></span>&nbsp; 취소</a>
       </td>
   </tr>
   </table>
</form>
</div>
</body>
</html>




4-7. head.php

주요 웹페이지의 상단에 보이는 메뉴를 위해 사용되어 집니다.


<html>
<head>
 <meta http-equiv="Content-Type" content="text/html;
   charset=UTF-8" />
<title>로그인 예제</title>
<link rel="stylesheet" href="bootstrap/css/bootstrap.min.css">
<script src="bootstrap/js/bootstrap.min.js"></script>
</head>

<body>
<nav class="navbar navbar-default navbar-static-top">
   <div class="container-fluid">
       <div class="navbar-header">
           <a class="navbar-brand" href="http://www.sourcecodester.com">Sourcecodester</a>
<ul class="nav navbar-nav">
           <li class="active"><a href="index.php">메인</a></li>
           <?php if (isset($_SESSION['user_id'])) { ?>
               <li><a href="">Signed in as <?php echo $_SESSION['user_id']; ?></a></li>
               <li><a href="logout.php">Log Out</a></li>
           <?php } else { ?>
               <li><a href="index.php">Login</a></li>
            <?php } ?>
</ul>
       </div>
   </div>
</nav>




4-8. index.php

사이트에 처음 접속시 실행되는 php 파일입니다.  디폴트로 로그인 화면을 보여주게 되어있으며 이미 로그인한 사용자가 접근시에는 관리자는 admin.php, 사용자는 welcome.php 로 다시 돌아가게 합니다.


사용자가 입력한 평문 패스워드를 데이터베이스에 있는 암호화된 패스워드와 비교하기 위한 작업도 포함되어 있습니다.


사용자가 활성화 안된 경우 관리자에게 문의하라는 메시지도 보여줍니다.


<?php
   include('dbcon.php');
   include('check.php');

   if(is_login()){

       if ($_SESSION['user_id'] == 'admin' && $_SESSION['is_admin']==1)
           header("Location: admin.php");
       else
           header("Location: welcome.php");
   }
?>

<!DOCTYPE html>
<html>
<head>
<title>로그인 예제</title>
<link rel="stylesheet" href="bootstrap/css/bootstrap1.min.css">
</head>


<body>

<div class="container">

<h2 align="center">로그인</h2><hr>
<form class="form-horizontal" method="POST">
<div class="form-group" style="padding: 10px 10px 10px 10px;">
<label for="user_name">아이디:</label>
<input type="text" name="user_name"  class="form-control" id="inputID" placeholder="아이디를 입력하세요."
required autocomplete="off" readonly onfocus="this.removeAttribute('readonly');" />
</div>
<div class="form-group" style="padding: 10px 10px 10px 10px;">
<label for="user_password">패스워드:</label>
<input type="password" name="user_password" class="form-control" id="inputPassword" placeholder="패스워드를 입력하세요."
required  autocomplete="off" readonly onfocus="this.removeAttribute('readonly');" />
</div>
<div class="checkbox">
<label><input type="checkbox"> 아이디 기억</label>
</div>
</br>
<div class="from-group" style="padding: 10px 10px 10px 10px;" >
<button type="submit" name="login" class="btn btn-success">로그인</button>
<a class="btn btn-success" href="registration.php" style="margin-left: 50px">
<span class="glyphicon glyphicon-user"></span>&nbsp;등록
</a>
</div>
</br>
</form>
</div>
</body>
</html>


<?php

   $login_ok = false;

   if ( ($_SERVER['REQUEST_METHOD'] == 'POST') and isset($_POST['login']) )
   {
$username=$_POST['user_name'];  
$userpassowrd=$_POST['user_password'];  

if(empty($username)){
$errMSG = "아이디를 입력하세요.";
}else if(empty($userpassowrd)){
$errMSG = "패스워드를 입력하세요.";
}else{


try {

$stmt = $con->prepare('select * from users where username=:username');

$stmt->bindParam(':username', $username);
$stmt->execute();
  
} catch(PDOException $e) {
die("Database error. " . $e->getMessage());
}

$row = $stmt->fetch();  
$salt = $row['salt'];
$password = $row['password'];

$decrypted_password = decrypt(base64_decode($password), $salt);

if ( $userpassowrd == $decrypted_password) {
$login_ok = true;
}
}


if(isset($errMSG))
echo "<script>alert('$errMSG')</script>";


       if ($login_ok){

           if ($row['activate']==0)
echo "<script>alert('$username 계정 활성이 안되었습니다. 관리자에게 문의하세요.')</script>";
           else{
session_regenerate_id();
$_SESSION['user_id'] = $username;
$_SESSION['is_admin'] = $row['is_admin'];

if ($username=='admin' && $row['is_admin']==1 )
header('location:admin.php');
else
header('location:welcome.php');
session_write_close();
}
}
else{
echo "<script>alert('$username 인증 오류')</script>";
}
}
?>




4-9. logout.php

사용자가 로그아웃을 시도한 경우를 처리하기 위한 코드입니다.


<?php

   include('dbcon.php');    
   include('check.php');

   if (is_login()){

       unset($_SESSION['user_id']);
       session_destroy();
   }

   header("Location: index.php");
?>




4-10. registration.php

사용자가 자신의 계정을 등록하기 위해 사용되는 웹페이지입니다.

평문 암호를 AES 256으로 암호화하여 데이터베이스에 저장하는 코드가 포함되어 있습니다.


테스트 편의를 위해 일정한 기준의 패스워드를 사용자가 입력한지 체크하는 validatePassword 함수를 사용하지 않도록 되어 있습니다.


<?php

   include('dbcon.php');
   include('check.php');
  
   if (is_login()){

       if ($_SESSION['user_id'] == 'admin' && $_SESSION['is_admin']==1)
           header("Location: admin.php");
       else
           header("Location: welcome.php");
   }


function validatePassword($password){
//Begin basic testing
if(strlen($password) < 8 || empty($password)) {
return 0;//Returns 0 if: password is too short (<8 characters) OR doesn't exist.
}
if((strlen($password) > 48)) {
return 0;//Returns 0 if: password is too long (>48 characters)
}
//End basic length tests

//Begin more advanced testing

if(preg_match('/[A-Z]/',$password) == (0 || false)){
return 1;//Returns 1 if: password does NOT contain upper case letters
}
if(!preg_match('/[\d]/',$password) != (0 || false)){
return 2;//Returns 2 if: password does NOT contain digits
}
if(preg_match('/[\W]/',$password) == (0 || false)){
return 3;//Returns 3 if: password does NOT contain any special characters
}
return true;
}


       if( ($_SERVER['REQUEST_METHOD'] == 'POST') && isset($_POST['submit']))
{

       foreach ($_POST as $key => $val)
       {
           if(preg_match('#^__autocomplete_fix_#', $key) === 1){
               $n = substr($key, 19);
               if(isset($_POST[$n])) {
                   $_POST[$val] = $_POST[$n];
           }
       }
       }

$username=$_POST['newusername'];
$password=$_POST['newpassword'];
$confirmpassword=$_POST['newconfirmpassword'];
$userprofile=$_POST['newuserprofile'];

            //   if (!validatePassword($password)){
// $errMSG = "잘못된 패스워드";
         //      }

               if ($_POST['newpassword'] != $_POST['newconfirmpassword']) {
                       $errMSG = "패스워드가 일치하지 않습니다.";
               }

if(empty($username)){
$errMSG = "아이디를 입력하세요.";
}
else if(empty($password)){
$errMSG = "패스워드를 입력하세요.";
}
else if(empty($userprofile)){
$errMSG = "프로필을 입력하세요.";
}

               try {
                   $stmt = $con->prepare('select * from users where username=:username');
                   $stmt->bindParam(':username', $username);
                   $stmt->execute();

              } catch(PDOException $e) {
                   die("Database error: " . $e->getMessage());
              }

              $row = $stmt->fetch();
              if ($row){
                   $errMSG = "이미 존재하는 아이디입니다.";
              }



if(!isset($errMSG))
{
                  try{
$stmt = $con->prepare('INSERT INTO users(username, password, userprofile, salt) VALUES(:username, :password, :userprofile, :salt)');
$stmt->bindParam(':username',$username);
                       $salt = bin2hex(openssl_random_pseudo_bytes(32));
                       $encrypted_password = base64_encode(encrypt($password, $salt));
                       $stmt->bindParam(':password', $encrypted_password);
$stmt->bindParam(':userprofile',$userprofile);
$stmt->bindParam(':salt',$salt);