세션과 쿠키를 이용한 로그인

Sangyeong Je·2023년 1월 19일
0

로그인

목록 보기
1/1

1. 메일로 인증번호 발송

클라이언트에게 $_POST['email'] 을 받으면
XXS 공격 방지를 위해 htmlspecialchars() 함수를 이용해 문자열로 변환합니다.

$email = htmlspecialchars($_POST['email']);
htmlspecialchars(): 문자열에서 특수문자를 HTML 코드형식으로 변환한다.
XXS 공격: 스크립트 코드를 삽입 해 개발자가 고려하지 않은 기능이 작동하게 하는 치명적일 수 있는 공격

변수 $email이 올바른 이메일 형식인지 체크합니다.

public function mailCheck($_str)
{
    if (preg_match("/^[_\.0-9a-zA-Z-]+@([0-9a-zA-Z][0-9a-zA-Z-]+\.)+[a-zA-Z]{2,6}$/i", $_str) == false)
    {
        return false;
    }
    else
    {
        return true;
    }
}
$emailChk = $this->mailCheck($email);
mailCheck(): 정규식을 사용하여 메일 주소의 패턴과 일치시킵니다. @ 기호 앞과 뒤에 문자가 있는지, @ 기호 뒤에 올바른 도메인 이름이 있는지 확인합니다.

이메일이 형식이 유효하면 데이터베이스에 이메일이 있는지 확인합니다.

public static function emailExistChk($data=null)
{
    $email = $data;

    $db = static::getDB();
    $emailChk = $db->query("SELECT 
        idx
        FROM DB.Table 
        WHERE email='$email'
        ");
    $count = $emailChk->rowCount();
    $row = $emailChk->fetch(PDO::FETCH_ASSOC);

    $dataPack = array('count' => $count , 'row' => $row);
    return $dataPack;
}
$emailExist= AdminMo::emailExistChk($email);
emailExistChk(): 인자값을 WHERE절로 구분해 데이터가 있는지 확인

DB에 매칭되는 데이터가 있을시 데이터의 row값과 idx값, 랜덤토큰값, 인증코드, ip, 시간을 저장합니다.

public function Common_RandomString($LENGTH = 32, $TYPE = NULL)
{
  switch ($TYPE) 
  {
    case 'RandEnDecryptKEY':
    $characters  = '0123456789';
    $characters .= 'abcdef';
    $characters .= 'ABCDEF';
    $string_generated = '';
    $nmr_loops = $LENGTH;
    while ($nmr_loops--)
    {
        $string_generated .= $characters[mt_rand(0, strlen($characters) - 1)];
    }
    break;
    
    case 'SignUpHistoryRandoms':
    $characters  = '123456789';
    $string_generated = '';
    $nmr_loops = $LENGTH;
    while ($nmr_loops--)
    {
        $string_generated .= $characters[mt_rand(0, strlen($characters) - 1)];
    }
    break;
  }

}

$adminIDX=$emailExist['row']['idx'];
$Token_Random=$this->Common_RandomString(32,'RandEnDecryptKEY');
$SignUpHistoryRandoms=$this->Common_RandomString(5,'SignUpHistoryRandoms');
$ipBring = $this->GetIPaddress();
$nowDate = date('Y-m-d H:i:s');
Common_RandomString(32,'RandEnDecryptKEY'): 0-9, 'abcdef', 'ABCDEF'를 지정, while문을 이용해 32번 반복합니다. 각 반복에서 mt_rand()함수를 사용하여 가능한 문자 범위 내에서 임의의 숫자를 생성해 문자를 선택해 추가합니다. 마지막으로 32자 랜덤 문자열을 반환합니다.
Common_RandomString(5,'SignUpHistoryRandoms'): 0-9를 지정 5자리 랜덤 문자열을 반환합니다.

저장된 정보들로 DB의 데이터를 저장하고 업데이트합니다.

$db = static::getDB();
$stat=$db->query("UPDATE DB.Table SET
    loginToken='$Token_Random'
    WHERE email='$email'
    ");
$stat2=$db->query("INSERT INTO DB.Table SET
    adminIDX = '$adminIDX', 
    code = '$SignUpHistoryRandoms', 
    createTime = '$nowDate',
    ipAddress = '$ipBring'
    ");

정보들을 이메일로 발송합니다.

public function emailSendForm($email=null,$body=null,$title=null)
{
    Modules::PHPMailer(); 
    // PHPMailer 선언
    $mail = new PHPMailer(true);
    // SMTP 서버 세팅
    $mail->isSMTP();
    try {
    // 구글 smtp 설정
        $mail->Host = "smtp.gmail.com";
    // SMTP 암호화 여부
        $mail->SMTPAuth = true;
    // SMTP 포트
        $mail->Port = 123;
    // SMTP 보안 프초트콜
        $mail->SMTPSecure = "ssl";
    // gmail 유저 아이디
        $mail->Username = "123@gmail.com";
    // gmail 패스워드
        $mail->Password ="123";
    // 인코딩 셋
        $mail->CharSet = 'utf-8';
        $mail->Encoding = "base64";
    // 보내는 사람
        $mail->setFrom('123@gmail.com', 'customercenter');
    // 받는 사람
        $mail->AddAddress($email);
    // 본문 html 타입 설정
        $mail->isHTML(true);
    // 제목
        $mail->Subject = $title;
    // 본문 (HTML 전용)
        $mail->Body = $body;
    // $mail->Body = 'Dear asurroixx,</br> We need to be sure it is you trying to log in to your Coin account. Here is your authentication code:'.$SignUpHistoryRandoms;
        $mail->Send();
    // echo "Message has been sent";
    } catch (phpmailerException $e) {
    // echo $e->errorMessage();
    } catch (Exception $e) {
    // echo $e->getMessage();
    }
}
$body = $SignUpHistoryRandoms;
$title = '이메일 왔습니다';
$emailSend = $this->emailSendForm($email,$body,$title);

2. 로그인

클라이언트에게 $_POST['email'],$_POST['code']를 받으면 htmlspecialchars() 함수를 이용해 문자열로 변환 후 아이피와 함께 배열에 담습니다.

public function GetIPaddress()
{
    if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {
        $_SERVER['REMOTE_ADDR'] = $_SERVER['HTTP_X_FORWARDED_FOR'];
    }
    return $_SERVER['REMOTE_ADDR'];
}
$ipAddress=$this->GetIPaddress();
$code = htmlspecialchars($_POST['code']);
$email = htmlspecialchars($_POST['email']);
$paramArr = ['code' => $code,'email' => $email,'ipAddress' => $ipAddress];
GetIPaddress(): 프록시 서버에서 오고 있음을 나타내는 "HTTP_X_FORWARD_FORD" 헤더가 설정되어 있는지 확인합니다. 이 값이 설정된 경우, 이 헤더의 값을 "REMOTE_ADDR" 서버 변수에 할당한 다음 반환합니다. "HTTP_X_FORWARD_FORD" 헤더가 설정되지 않은 경우 "REMOTE_ADDR" 서버 변수를 반환합니다.
HTTP_X_FORWARD_FOR: HTTP 프록시 또는 로드 밸런서를 통해 웹 서버에 연결하는 클라이언트의 원래 IP 주소를 식별하는 데 사용되는 HTTP 헤더 필드입니다.
REMOTE_ADDR: 서버에 연결하는 클라이언트의 IP 주소를 보유하는 PHP의 서버 변수입니다. 클라이언트가 프록시 또는 로드 밸런서를 통해 연결하는 경우 REMOTE_ADDR 변수에는 클라이언트의 실제 IP 주소 대신 프록시 또는 로드 밸런서의 IP 주소가 포함됩니다.

배열에 담은값이 DB에 있는지 조회합니다.

public static function GetLogin($data=null)
{
    $code=$data['code'];
    $email=$data['email'];
    $ipAddress=$data['ipAddress'];

    $db = static::getDB();
    $GetDump = $db->prepare("SELECT 
        A.adminIDX,
        A.code
        FROM DB.Table AS A
        LEFT JOIN sendipay.Admin AS B
        ON(A.adminIDX = B.idx)
        WHERE B.email='$email' AND A.code='$code' AND A.ipAddress='$ipAddress'
        ORDER BY A.idx DESC LIMIT 1;
        ");
    $GetDump->execute();
    $count = $GetDump->rowCount();
    $row = $GetDump->fetch(PDO::FETCH_ASSOC);
    return $row;
}
$getLogin=AdminMo::GetLogin($paramArr);

유효성을 통과하면 아이디의 adminIDX값을 가져와 쿠키를 만듭니다.

$adminIDX=$getLogin['adminIDX'];
$adCookieMake=$this->AdCookieMakeDo($adminIDX);
$globalCookie = $this->AdGlobalCookieMakeDo($adminIDX);

2-1. 쿠키만들기

adminIDX와 suburl, 토큰과 아이피를 받아 adminIDX와 일치한 데이터의 토큰값을 업데이트하고 배열에 담고 encode화 합니다.

$adminIDX=$data;
$suburl = $this->SubDomain();
$randomToken=$this->Common_RandomString(32,'RandEnDecryptKEY');
$ipAddress=$this->GetIPaddress();
$db = static::getDB();
$stat=$db->prepare("UPDATE sendipay.Admin SET
    loginToken='$randomToken'
    WHERE idx='$adminIDX'
    ");
$stat->execute();

$tokenDataArr = ['idx' => $adminIDX,'token' => $randomToken,'ip' => $ipAddress];
$tokenDataArrEn = json_encode($tokenDataArr,JSON_UNESCAPED_UNICODE);

encode화한 배열을 암호화하고 세션이 시작되지않았으면 세션을 시작합니다. 'AdSession'이라는 세션 변수를 설정하고 $sessionArr 배열을 해당 변수에 직렬화된 문자열로 저장합니다.

$admin_LOGIN_KEY=DefinAll::admin_LOGIN_KEY;
$tokenEncrypt=$this->encrypt($tokenDataArrEn,$admin_LOGIN_KEY);
if (session_status() == PHP_SESSION_NONE) {
  session_start();
}
$sessionArr = ['idx'=>$adminIDX,'token'=>$randomToken];
$_SESSION['AdSession'] = serialize($sessionArr);
serialize(): 배열을 세션 변수에 저장할 수 있도록 문자열로 변환하는 데 사용됩니다.

AdCookie라는 이름과 암호화된 토큰값을 가지고있는 쿠키를 굽습니다.

$setCookie = setcookie("AdCookie", $tokenEncrypt, time() + (86400 * 30), "/", "".$suburl.'.1234.com');	

2-2. 글로벌 쿠키만들기

로그인 화면을 출력해줍니다.

$redirectUri = "https://admin.1234.com";
$result=['result'=>'t','redirectUri'=>$redirectUri];
echo json_encode($result,JSON_UNESCAPED_UNICODE);

0개의 댓글