댓글 검색 목록

[php] PHP MySQL에서 일회용 암호 (OTP)를 만드는 방법

페이지 정보

작성자 운영자 작성일 20-04-19 20:05 조회 1,132 댓글 0

추가 보안 


PHP와 MySQL에서 일회용 암호 (OTP)를 만드는 방법에 대한 단계별 자습서를 시작합니다. 시스템에 추가적인 보안이 필요하십니까? OTP는 그렇게 하는 방법 중 하나이며, 사용자 계정 재설정, 이메일 주소 확인, 지불 확인, 거래 보안 등 문자 그대로 어떤 용도로든 사용할 수 있습니다. OTP의 일반적인 프로세스는 다음과 같습니다.

  • 임의의 일회용 비밀번호를 생성하여 이메일 또는 메시지를 통해 사용자에게 보냅니다.
  • 사용자가 챌린지 페이지에 액세스하여 OTP를 시작합니다.
  • 마지막으로, 주어진 암호가 유효한지 확인합니다 – 유효한 경우 트랜잭션을 진행하십시오.

그러나 PHP에서 일회용 암호를 어떻게 구현합니까? 이 안내서는 정확한 단계를 안내합니다. 계속 읽으십시오.


ⓘ이 튜토리얼의 시작 부분에 모든 소스 코드가 포함 된 zip 파일이 포함되어 있으므로 모든 것을 복사하여 붙여 넣을 필요가 없습니다.


소스 코드 다운로드 


먼저 약속 한대로 소스 코드에 대한 다운로드 링크가 있습니다.


소스 코드 다운로드 


소스 코드를 다운로드하려면 여기를 클릭하십시오. MIT 라이센스에 따라 릴리스되었으므로 그 위에 빌드하거나 자신의 프로젝트에서 자유롭게 사용하십시오.


폴더 


다음은 zip 파일에서 폴더를 구성하는 방법에 대한 간략한 소개입니다.


  • lib : 모든 라이브러리 파일을 저장하는 곳.
  • SQL : 데이터베이스를 빌드하는 데 필요한 모든 SQL 파일 모두 가져온 후에 안전하게 삭제할 수 있습니다.


빠른 시작 


  • 폴더에 다운로드하여 압축을 풉니다.
  • 데이터베이스를 작성하고 sql / otp.sql 파일을 가져 오십시오.
  • lib / config.php의 데이터베이스 설정을 원하는 대로 변경하십시오.
  • 브라우저에서 1-initialize.php를 실행하십시오. 2-challenge.php 및 3-verify.php를 통해 자신의 시스템에 통합하는 방법을 확인하십시오.

데이터베이스 


먼저 생성 된 OTP, 타임 스탬프 및 항목을 저장하기 위해 간단한 데이터베이스 테이블을 설정해야 합니다.


OTP TABLE 


CREATE TABLE `otp` ( `id` int(11) NOT NULL, `otp_pass` varchar(12) NOT NULL, `otp_timestamp` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, `otp_tries` tinyint(1) NOT NULL DEFAULT '0' ) ENGINE=InnoDB DEFAULT CHARSET=latin1; ALTER TABLE `otp` ADD PRIMARY KEY (`id`); 


FieldDescription
idThe primary key, unique identifier. This can be the user ID, transaction ID, order ID – Whatever you want to track.
otp_passThe one-time password.
otp_timestampTime at which the OTP is generated, used to calculate the expiry time.
otp_triesThe number of challenge attempts. Used to stop brute force attacks.

요컨대, 자신의 프로젝트에 맞게 자유롭게 변경하십시오. 예를 들어, ID 대신 이메일은 사용자를 위해 OTP를 생성하는 일부 사람들에게 더 적합 할 수 있습니다.


라이브러리 파일 


다음으로, OTP 매직 및 프로세스를 처리하기 위해 PHP 라이브러리를 만들어야 합니다.


구성 파일 


<?php // MUTE NOTICES error_reporting(E_ALL & ~E_NOTICE); // PATH define('PATH_LIB', __DIR__ . DIRECTORY_SEPARATOR); // DATABASE SETTINGS - CHANGE THESE TO YOUR OWN! define('DB_HOST', 'localhost'); define('DB_NAME', 'test'); define('DB_CHARSET', 'utf8'); define('DB_USER', 'root'); define('DB_PASSWORD', ''); // ONE-TIME PASSWORD define('OTP_VALID', "15"); // VALID FOR X MINUTES define('OTP_TRIES', "3"); // MAX TRIES define('OTP_LEN', "6"); // PASSWORD LENGTH ?> 


이것은 모든 설정을 유지하기 위한 구성 파일일 뿐입니다. – 데이터베이스 및 OTP 설정을 원하는 대로 변경하십시오.


OTP LIBRARY 


<?php class OTP { /* PART 1 - DATABASE SUPPORT FUNCTIONS */ protected $pdo = null; protected $stmt = null; public $error = ""; public $lastID = null; function __construct() { // __construct() : connect to the database // PARAM : DB_HOST, DB_CHARSET, DB_NAME, DB_USER, DB_PASSWORD // ATTEMPT CONNECT try { $str = "mysql:host=" . DB_HOST . ";charset=" . DB_CHARSET; if (defined('DB_NAME')) { $str .= ";dbname=" . DB_NAME; } $this->pdo = new PDO( $str, DB_USER, DB_PASSWORD, [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, PDO::ATTR_EMULATE_PREPARES => false ] ); return true; } // ERROR - DO SOMETHING HERE // THROW ERROR MESSAGE OR SOMETHING catch (Exception $ex) { print_r($ex); die(); } } function __destruct() { // __destruct() : close connection when done if ($this->stmt !== null) { $this->stmt = null; } if ($this->pdo !== null) { $this->pdo = null; } } function exec($sql, $data=null) { // exec() : run insert, replace, update, delete query // PARAM $sql : SQL query // $data : array of data try { $this->stmt = $this->pdo->prepare($sql); $this->stmt->execute($data); $this->lastID = $this->pdo->lastInsertId(); } catch (Exception $ex) { $this->error = $ex; return false; } $this->stmt = null; return true; } function fetch ($sql, $cond=null, $sort=null) { // fetch() : perform select query (single row expected) // returns an array of column => value // PARAM $sql : SQL query // $cond : array of conditions // $sort : custom sort function $result = []; try { $this->stmt = $this->pdo->prepare($sql); $this->stmt->execute($cond); if (is_callable($sort)) { while ($row = $this->stmt->fetch(PDO::FETCH_NAMED)) { $result = $sort($row); } } else { while ($row = $this->stmt->fetch(PDO::FETCH_NAMED)) { $result = $row; } } } catch (Exception $ex) { $this->error = $ex; return false; } // Return result $this->stmt = null; return count($result)==0 ? false : $result ; } /* PART 2 - GENERATE RANDOM OTP */ function generate ($id) { // generate() : create a new OTP // PARAM $id : user ID, order ID, or just any unique transaction ID // Create random password $alphabets = "abcdefghijklmnopqrstuwxyzABCDEFGHIJKLMNOPQRSTUWXYZ0123456789"; // Change this alphabets set as needed. For example, if you want numbers only // $alphabets = "0123456789"; $count = strlen($alphabets) - 1; $pass = ""; for ($i = 0; $i < OTP_LEN; $i++) { $pass .= $alphabets[rand(0, $count)]; } // Database entry $sql = "REPLACE INTO `otp` (`id`, `otp_pass`, `otp_timestamp`, `otp_tries`) VALUES (?, ?, ?, ?)"; $data = [$id, $pass, date("Y-m-d H:i:s"), 0]; $ok = $this->exec($sql, $data); // Result return [ "status" => $ok ? 1 : 0, "pass" => $ok ? $pass : "" ]; } /* PART 3 - CHALLENGE OTP */ function challenge ($id, $pass) { // challenge() : challenge the OTP // Get the OTP entry $sql = "SELECT * FROM `otp` WHERE `id`=?"; $data = [$id]; $otp = $this->fetch($sql, $data); // OTP entry not found!? if ($otp === false) { $this->error = "OTP transaction not found."; return false; } // Too many tries if ($otp['otp_tries'] >= OTP_TRIES) { $this->error = "Too many tries for OTP."; return false; } // Expired $validTill = strtotime($otp['otp_timestamp']) + (OTP_VALID * 60); if (strtotime("now") > $validTill) { $this->error = "OTP has expired."; return false; } // Incorrect password if ($pass != $otp['otp_pass']) { // Add a strike to number of tries $strikes = $otp['otp_tries'] + 1; $sql = "UPDATE `otp` SET `otp_tries`=? WHERE `id`=?"; $data = [$strikes, $id]; $this->exec($sql, $data); // Security lock down - Too many tries if ($strikes >= OTP_TRIES) { $this->lockdown(); } // Return result $this->error = "Incorrect OTP."; return false; } // OK - Correct OTP // You can delete the OTP at this point if you want /* * $sql = "DELETE FROM `otp` WHERE `id`=?"; * $data = [$id]; * $this->exec($sql, $data); */ return true; } function lockdown () { // lockdown() : failed challenge multiple times // DO YOUR OWN SECURITY LOCKDOWN! // Suspend the order? // Suspend the user account? // Maybe temporary lock for a few hours to prevent spam? // Send warning email + SMS? // All up to what you want to do. } } ?> 


설명 


이 라이브러리는 처음에는 복잡해 보이지만 실제로는 3 개의 파트 만 있습니다.


  • 데이터베이스를 다루는 기능을 지원합니다.
  • OTP를 생성하는 기능.
  • OTP를 검증하는 기능.

그러나 자체 PHP 프레임 워크를 사용하는 경우 데이터베이스 지원 기능을 제거하고 프레임 워크를 사용하도록 이 라이브러리를 수정하십시오.


Support Functions
FunctionDescription
__constructThe constructor. Automatically connects to the database when the OTP object is created.
__destructThe destructor. Automatically disconnects from the database when the OTP object is destroyed.
execRuns an insert, replace, update, or delete SQL query.
fetchPerform a select query.
OTP Functions
FunctionDescription
generateInitiates and creates a new OTP.
challengeChallenge the OTP.
lockdownSecurity lockdown. An empty function for you to fill in – This will run after the user has failed the OTP challenge too many times.

구현 단계 


기초가 확립되었으므로 마지막 단계는 프로젝트에 OTP 라이브러리를 사용하는 것입니다. 모든 사람이 OTP를 다르게 사용하므로 이 섹션에서는 매우 일반적인 "구현 방법"만 안내합니다.


1 단계) OTP 생성 


<?php // (1) INCLUDE OTP LIBRARY require __DIR__ . DIRECTORY_SEPARATOR . "lib" . DIRECTORY_SEPARATOR . "config.php"; require PATH_LIB . "lib-otp.php"; $libOTP = new OTP(); // (2) GENERATE OTP // Using a dummy ID of 999 here // It can be the user ID, order ID, whatever is applicable to your project $result = $libOTP->generate(999); // (3) SEND THE ONE-TIME PASSWORD TO THE USER // OTP generation OK if ($result['status'] == 1) { // Send message via email, SMS, messaging, or whatever communication gateway you use $message = "Please visit https://your-site.com/2-challenge.php to verify. This password is valid for " . OTP_VALID . " minutes - " . $result['pass']; // mail("john@doe.com", "Verify to continue", $message); // For testing - We will just output the OTP as text here. echo $message; } // OTP generation Failed else { echo "ERROR - ". $libOTP->error; } ?> 


프로세스의 첫 번째 단계는 generate() 함수를 사용하여 OTP를 생성하는 것입니다. 추적하려는 고유 ID를 전달하고 OTP 및 확인 페이지를 사용자에게 보내는 것을 잊지 마십시오.


2 단계) OTP 챌린지 방문 페이지 


<!DOCTYPE html> <html> <head> <title> OTP Challenge Demo Page </title> <script> function verify () { // verify() : verify OTP // APPEND FORM DATA var data = new FormData(); data.append('pass', document.getElementById("otp_pass").value); data.append('id', document.getElementById("otp_id").value); // INIT AJAX var xhr = new XMLHttpRequest(); xhr.open('POST', "3-verify.php", true); // WHEN THE PROCESS IS COMPLETE xhr.onload = function(){ var res = JSON.parse(this.response); if (res.status) { // OK - DO SOMETHING - REDIRECT USER TO ANOTHER PAGE OR CONTINUE TRANSACTION alert("OK"); } else { // ERROR - DO SOMETHING alert(res.message); } }; // SEND xhr.send(data); return false; } </script> </head> <body> <form onsubmit="return verify();"> OTP Password <input type="password" id="otp_pass" required/> <br> ID (This should be hidden or kept in the session) <input type="text" id="otp_id" readonly value="999"/> <br> <input type="submit" value="Go"/> </form> </body> </html> 


사용자가 OTP를 입력 할 수 있는 랜딩 페이지의 예입니다. 이는 간단한 HTML 양식일뿐입니다.


3 단계) OTP 검증 


<?php // (1) INCLUDE OTP LIBRARY require __DIR__ . DIRECTORY_SEPARATOR . "lib" . DIRECTORY_SEPARATOR . "config.php"; require PATH_LIB . "lib-otp.php"; $libOTP = new OTP(); // (2) CHECK GIVEN OTP $result = false; if (isset($_POST['pass']) && isset($_POST['id'])) { $result = $libOTP->challenge($_POST['id'], $_POST['pass']); } else { $libOTP->error = "Invalid OTP password and ID"; } // (3) RESULTS echo json_encode([ "status" => $result ? 1 : 0, "message" => $result ? "OK" : $libOTP->error ]); ?> 


마지막으로 사용자가 제공 한 비밀번호만 확인하면 됩니다.



댓글목록 0

등록된 댓글이 없습니다.

웹학교 로고

온라인 코딩학교

코리아뉴스 2001 - , All right reserved.