PHP

관리자 로그인에 구글 OTP 적용하기

옥수수빵 2022. 6. 23. 10:28
728x90

우선 자신의 폰에 맞게 OTP 앱을 다운로드 받아서 설치합니다.

그리고 여기로 가셔서 파일을 다운로드 받습니다.

실제로 사용할 파일은 PHPGangsta 안에 있는 GoogleAuthenticator.php 이 파일입니다. 그러니 이 파일만 다운로드 받으셔도 됩니다.

PHP 5.5, 5.6에서 실행됩니다.

 

OTP를 이용한 로그인 순서는 대략 아래와 같습니다.

  1. 로그인 페이지에서 아이디, 패스워드를 입력합니다(ajax).
  2. 아이디, 패스워드가 정확할 경우 레이어 창을 띄웁니다(일반적인(?) 로그인처럼 로그인 버튼을 눌렀을 때 다음 페이지로 이동을 시키는 게 제일 편하지만 간혹 그 자리에서 레이어 창 같은 걸 뜨게 하는 걸 좋아하는 클라이언트가 있습니다).
  3. OTP가 뜨고 여기에서 앱을 사용해서 QR 코드를 비추면 나오는 6자리 숫자를 입력하고 맞으면 로그인을 시킵니다.

테스트 삼아서 만든 페이지입니다.

아이디/패스워드 모두 test입니다.

아이디와 패스워드를 입력하고 로그인 버튼을 누르면 위처럼 QR 코드가 뜹니다.

디비 연결은 되어 있지 않습니다. 테스트를 위해서...

.otp-popup { position: absolute; top: 0; left: 0; width: 100%; height: 100%; display: none; }
.otp-popup-content { position: absolute; top: 350px; left: 50%; width: 300px; height: 320px; padding: 20px; background-color: rgb(255, 255, 255); border: 1px solid #ccc; box-shadow: 0 2px 3px 0 rgba(34, 36, 38, 0.15); transform: translateX(-50%) translateY(-50%); font-size: 12px; overflow-y: scroll; }
#btnCheckQr { background: #000; color: #fff; width: 150px; height: 24px; border: 0; }
$(function() {
	$("#btnSubmit").on("click", function() {
		var id = $("#userId").val(), password = $("#userPassword").val();
		$.ajax({
			url: "check-login.php",
			type: "POST",
			data: {
				id: id,
				password: password
			},
			dataType: "json",
			success: function(res) {
				if(res.fl == "success") {
					$("#otpImage").attr("src", res.qr);
					$("#userId").val(res.userid);
					$("#secretCode").val(res.secret);
					$("#qrImage").val(res.qr);
					$(".otp-popup").show();
				}
				console.log(res);
			}
		});
	});

	$("#btnCheckQr").on("click", function() {
		var code = $("#userCode").val();
		if(code) {
			$.ajax({
				url: "check-code.php",
				type: "POST",
				data: {
					userid: $("#userId").val(),
					secret: $("#secretCode").val(),
					qr: $("#qrImage").val(),
					code: code
				},
				success: function(res) {
					if(res == true) {
						$(location).attr("href", "admin/main");
					} else {
						alert("QR 코드 오류");
					}
				}
			});
		}
	});
});
<form>
	<input type="text" name="userid" id="userId" required>
	<input type="password" name="userpassword" id="userPassword" required>
	<button type="button" id="btnSubmit">로그인</button>
</form>
<div class="otp-popup">
	<div class="otp-popup-content">
		<img src="" id="otpImage">
		<form id="qForm" method="post">
			<input type="hidden" name="userid" id="userId" value="">
			<input type="hidden" name="secret" id="secretCode" value="">
			<input type="hidden" name="qr" id="qrImage" value="">
			<input type="text" name="user_code" id="userCode" value="" maxlength="6">
			<button type="button" id="btnCheckQr">확인</button>
		</form>
	</div>
</div>

우선 첫 번째 form에서 아이디, 패스워드를 입력하고 ajax로 check-login.php 파일로 값을 던집니다.

$message = [];
if($_POST['id'] == "test" && $_POST['password'] == "test") {
	require_once "GoogleAuthenticator.php";
	$ga = new PHPGangsta_GoogleAuthenticator();
	$secret = $ga->createSecret();
	$qrCodeUrl = $ga->getQRCodeGoogleUrl("test", $secret);
	$message = [
		'fl' => 'success',
		'userid' => $_POST['id'],
		'secret' => $secret,
		'qr' => $qrCodeUrl
	];
} else {
	$message['fl'] = 'fail';
}

echo json_encode($message);

check-login.php 파일에서 지금 입력한 아이디, 패스워드가 디비에 있는지 체크를 합니다. 위 파일에서는 두 번째 줄에 해당합니다. 테스트니 디비 연결은 하지 않았지만 실제로는 그 위에서 디비 접속해서 이런 회원이 있는지 체크를 해야 하겠지요.

이 단계는 단순히 유저가 입력한 아이디, 패스워드 정보가 디비에 있나 없나 체크를 하는 단계인지라 디비에 값이 있더라도 로그인을 시키지는 않습니다. 대신 값이 있으면 여기에서 QR 관련 정보를 생성해서 다시 보냅니다. 입력한 값과 디비에 있는 값이 일치하면 fl부터 qr까지 값을 생성하고 값이 일치하지 않는다면 fl에 fail만 넣어서 JSON으로 꼬아서 보냅니다.

여기에서 아이디, 패스워드가 일치했다면 아래 이미지처럼 나와야 합니다(개발자 도구 - 콘솔).

check-login.php 파일에서 보낸 값 중에 fl키에 값이 success라면 위 html 파일에서 opt-popup이라는 클래스를 가진 div를 띄웁니다. 이때 hidden으로 숨겨둔 input에 각각 값을 다 넣어줍니다. 여기에서 name이 qr인 값은 필요가 없습니다. 그냥 넣은 거니 신경 안 쓰셔도 됩니다.

이제 QR 이미지가 뜨면 폰으로 받아둔 Authenticator라는 앱을 실행해 줍니다. 그리고 QR 코드 스캔을 선택해서 위에 뜬 이미지를 스캔합니다. 그러면 숫자 6자리가 나오는데 이걸 위 빈 칸에 입력합니다. 참고로 이 앱은 QR 코드를 스캔할 때마다 계속 생성이 됩니다. 이건 직접 앱을 실행해 보시면 무슨 말인지 알 수 있습니다. 그래서 헷갈릴 수 있으니 QR 코드를 스캔할 때마다 삭제를 하는 게 좋습니다.

아이폰의 경우는 해당 앱의 숫자가 나오는 우측 상단에 ... 세 개가 나오는데 이걸 터치하고 수정, 연필, 휴지통, 계정 삭제 순서로 터치하면 됩니다.

안드로이드는 그냥 해당 메뉴를 길게 터치하고 있으면 메뉴가 나오니 거기서 삭제!

QR 코드가 뜨면 이제 여기서 값을 입력하고 확인 버튼을 클릭하면 됩니다.

require_once "GoogleAuthenticator.php";
$ga = new PHPGangsta_GoogleAuthenticator();
$code_verify = $ga->verifyCode($_POST['secret'], $_POST['code'], 2);
if($code_verify == true) {
// 로그인 관련 처리
}
echo $code_verify;

위는 check-code.php 파일입니다.

여기에서 넘어온 값이 정상이면 로그인 관련 세션이든 뭐든 생성해서 처리하고 true를 보냅니다. 이때 true면 관리자 페이지로 이동을 시키거나 하면 됩니다.

 

너무 두서가 없는데 코드 보시면 이해하시리라 봅니다.

어떻게 돌아가는지를 중점적으로 쓴 것이기 때문에 보안이나 다른 부분은 실제 사용하시는 페이지에 맞게 수정하시면 될 것 같습니다.

반응형