เนื้อหานี้จะเป็นแนวทางการใช้งานระบบยืนยันตัวตน 2 ชั้น
(Two-Factor Authentication, 2FA) เพื่อเพิ่มระดับความปลอดภัยในการเข้า
ถึงข้อมูลสมาชิก ในที่นี้จะมีการใช้ package 2 ตัวมาร่วม คือ Google2FA สำหรับสร้าง
TOTOP (Time-based One-time Password) กับ EndroidQrCode สำหรับ
สร้างรูป qrcode ไว้สำหรับแอปพลิเคชันยืนยันตัวตนอย่าง Google Authenticator
หรือ Microsoft Authenticator เพื่อสแกนเปิดใช้งาน
สำหรับ Google2FA และ EndroidQrCode จะใช้เป็นเวอร์ชั่นดังนี้
ติดตั้ง Google2FA และ EndroidQrCode ก่อนใช้งาน
composer require pragmarx/google2fa:8.0
composer require endroid/qr-code:3.8.1
โดย EndroidQrCode เวอร์ชั่นดังกล่าว สามารถดูวิธีการใช้งานแบบกระชับ
ได้ที่บทความด้านล่างนี้ หากใช้เวอร์ชั่นอื่น อาจจะต้องดูคู่มือหรือการกำหนดค่าใหม่
ตามเวอร์ชั่นนั้นๆ
สร้าง qrcode ด้วย php endroid qrcode อัพเดทปี 2020 http://niik.in/978
https://www.ninenik.com/content.php?arti_id=978 via @ninenik
เกี่ยวกับ Google2FA
Google2FA เป็น โมดูล PHP ที่รองรับการใช้งานร่วมกับ Google Two-Factor Authentication
หรือระบบยืนยันตัวตนสองชั้นของ Google สามารถใช้ได้ทั้ง (HOTP) และ (TOTP) อัลกอริทึม (Algorithm)
เนื่องจากระบบทั้งสองมีเรื่องของการใช้เวลามาเกี่ยวข้องดังนั้น ต้องให้มั่นใจว่าอุปกรณ์ที่เราใช้ยืนยันตัวตน (มือถือที่
ติดตั้งแอปพลิเคชันยืนยันตัวตน) กับเวลาของ server ต้องตรงกัน เพราะว่าจะมีการใช้งานรหัสข้อมูลค่าเดียวกันใน
เวลาที่กำหนดเอาไว้
TOTP ในระบบ 2FA คืออะไร
TOTP ย่อมาจาก "Time-Based One-Time Password" หรือ "การสร้างรหัสผ่านใช้ครั้งเดียวโดยใช้เวลา"
ซึ่งเป็นหนึ่งในวิธีการที่ใช้ในการสร้างรหัสผ่านสำหรับการยืนยันตัวตนสองชั้น (Two-Factor Authentication, 2FA)
หรือการยืนยันตัวตนหลายชั้น (Multi-Factor Authentication, MFA) โดยรหัสผ่านนี้มีอายุการใช้งานจำกัด
และเปลี่ยนไปตามเวลาปัจจุบัน โดยมักจะใช้เป็นหนึ่งในตัวเลือกในการยืนยันตัวตนเมื่อใช้แอปพลิเคชันยืนยันตัวตน
อย่างเช่น Google Authenticator หรือ Microsoft Authenticator
โดยวิธีการทำงานของ TOTP จะใช้การสร้างรหัสผ่านสำหรับยืนยันตัวตนจากข้อมูลหลายๆ อย่างที่เปลี่ยนไป
ตามเวลาปัจจุบัน (timestamp) โดยสมมติว่าเรามีข้อมูลรหัสลับ (secret key) และข้อมูลเวลาปัจจุบัน
(timestamp) ซึ่งสามารถปรับแต่งได้ทุกๆ 30 วินาที จากนั้นจะนำข้อมูลรหัสลับและข้อมูลเวลาปัจจุบัน
มาผ่านอัลกอริทึมแฮชเชียร์ เช่น HMAC-SHA1 เพื่อสร้างรหัสผ่านชุดใหม่ จากนั้นนำผลลัพธ์ที่ได้
มาตัดส่วนบางส่วนออก และเอาผลลัพธ์นั้นมาแปลงเป็นรหัสผ่านที่มีความยาวและของอักขระที่กำหนดไว้
เมื่อผู้ใช้ต้องการยืนยันตัวตน ระบบจะถามหารหัสผ่านใหม่จากแอปพลิเคชันยืนยันตัวตน และใช้ข้อมูล
เวลาปัจจุบัน (timestamp) เพื่อสร้างรหัสผ่านใหม่ขึ้นมา โดยรหัสผ่านที่ถูกสร้างนี้จะมีอายุการใช้งาน
เพียงแค่ไม่กี่วินาที จึงทำให้มันเป็นรหัสผ่านใช้ครั้งเดียวที่ปลอดภัยและทันสมัย
HOTP ในระบบ 2FA คืออะไร
HOTP ย่อมาจาก "HMAC-based One-Time Password" หรือ "การสร้างรหัสผ่านใช้ครั้งเดียวโดยใช้ HMAC"
ซึ่งเป็นวิธีการสร้างรหัสผ่านสำหรับการยืนยันตัวตนสองชั้น (Two-Factor Authentication, 2FA)
หรือการยืนยันตัวตนหลายชั้น (Multi-Factor Authentication, MFA) ที่ใช้การถอดรหัสด้วย
HMAC (Hash-based Message Authentication Code) เพื่อสร้างรหัสผ่านที่มีอายุการใช้งานจำกัด
และเปลี่ยนไปตามข้อมูลที่มีอยู่
วิธีการทำงานของ HOTP จะใช้การสร้างรหัสผ่านโดยใช้ข้อมูลรหัสลับ (secret key) และข้อมูลการตรวจสอบ
(counter) ที่เปลี่ยนไปเมื่อมีการใช้งาน โดยรหัสลับและข้อมูลการตรวจสอบนี้จะถูกส่งผ่านฟังก์ชันแฮช
(hash function) เช่น HMAC-SHA1 เพื่อสร้างรหัสผ่านที่มีความยาวและของอักขระที่กำหนดไว้
เมื่อผู้ใช้ต้องการยืนยันตัวตน ระบบจะถามหารหัสผ่านใหม่จากแอปพลิเคชันยืนยันตัวตน และใช้ข้อมูลการ
ตรวจสอบ (counter) เพื่อสร้างรหัสผ่านใหม่ขึ้นมา โดยรหัสผ่านที่ถูกสร้างนี้จะมีอายุการใช้งานเพียงแค่ครั้งเดียว
และหลังจากนั้นระบบจะเพิ่มค่า counter หรือข้อมูลการตรวจสอบเพื่อใช้สำหรับการยืนยันตัวตนครั้งถัดไป
สรุปคือ ทั้ง TOTP และ HOTP นั้นเป็นวิธีการสร้างรหัสผ่านใช้ครั้งเดียวที่มีความปลอดภัยและเปลี่ยนไป
ตามข้อมูลการตรวจสอบที่เปลี่ยนไป ซึ่งเป็นหนึ่งในวิธีการที่นิยมใช้ในการทำงานของระบบยืนยันตัวตนสองชั้น
(2FA) หรือหลายชั้น (MFA) เพื่อเพิ่มความปลอดภัยในการเข้าถึงข้อมูลและบริการออนไลน์
**ในที่นี้เราจะใช้เป็นรูปแบบ TOTP
แนวทางการประยุกต์การใช้งาน ระบบยืนยัน 2 ชั้น 2FA กับระบบสมาชิก
เมื่อเรารู้จักเครื่องมือที่จะใช้งานแล้ว ก็มีดูแนวทางการทำงานและการประยุกต์ สมมติเรามีระบบสมาชิกเดิมอยู่แล้ว
ปกติการทำงานก็คือ ผู้ใช้ล็อกอินเข้าใช้งานด้วย ชื่อผู้ใช้ และ รหัสผ่าน ก็จะเข้าสู่ระบบสมาชิกได้ ดังนั้น การที่จะ
เปิดใช้งานระบบยืนยัน 2 ชั้น ผู้ใช้จะต้องเข้ามาในหน้าจัดการสมาชิก หรือเข้าสู่ระบบก่อน สิ่งที่เราต้องเพิ่มเข้าไป
ในระบบสมาชิก คือฟิลด์ที่สำหรับ เปิดใช้งานยืนยัน เพื่อแยกว่าใครจะใช้หรือไม่ใช้ระบบยืนยัน 2 ชั้น และอีกฟิลด์
คือรหัสสำหรับปิดการใช้งานระบบยืนยัน 2 ชั้น เราไม่สามารถเข้าถึงอุปกรณ์ยืนยันตัวตนได้ ต้องการปิดระบบก็จะ
ใช้รหัสพิเศษนี้เพื่อปิดการใช้งาน ให้เข้าสู่ระบบแบบปกติ สมมติเราใช้ชื่อฟิลด์ทั้งสองเป็นดังนี้
member_2fa_active เก็บค่า 0 กับ 1 (ปิดใช้งาน กับ เปิดใช้งาน)
member_2fa_revoke_key เก็บ ข้อความรหัสพิเศษ (สร้างให้ตอนแรกที่เปิดใช้งาน แล้วให้บันทึกเก็บไว้)
โดยค่าแรก เมื่อเราเข้าสู่ระบบสมาชิกได้แล้ว ก็สามารถปิดเปิดได้ตามต้องการ
ส่วนค่าที่สอง เราจะใช้สำหรับเป็นรหัสยืนยัน กรณีที่ยังไม่ได้ล็อกอิน หรือล็อกอิน 2 ชั้นไม่ได้ ต้องการปิด ก็จะใช้
ค่าที่บันทึกไว้นี้เป็นรหัสเพื่อยืนยันว่าเราเป็นผู้ใช้ที่ต้องการปิดค่า member_2fa_active ให้เป็น 0 เพื่อให้
สามารถล็อกอินผ่านระบบสมาชิกปกติ
ลำดับภาพตัวอย่าง ขั้นตอนการทำงาน เมื่ออยู่ในหน้าสมาชิก
1. เริ่มต้น เปิดใช้งานครั้งแรก ในหน้าสมาชิก เมื่อเข้าสู่ระบบอยู่แล้ว มีหน้าจัดการ การเปิดใช้งาน 2FA
โดยเมื่อผู้ใช้เลือกเปิดใช้งาน เราก็จะทำการสร้าง qrcode สำหรับใช้แอปพลิเคชันยืนยันตัวตนสแกน
แต่ถ้าเปิดใช้งานอยู่แล้ว ต้องการปิด เราก็สามารถทำคำสั่งอัปเดทฐานข้อมูลใน member_2fa_active
ให้มีค่าเป็น 0

2. กรณีเป็นการเปิดใช้งาน ก็ให้ไปยังหน้าสร้าง qrcode สำหรับแอปพลิเคชันยืนยันตัวตน

เพื่อเปิดใช้งานผู้ใช้จะต้องใช้แอปพลิเคชันยืนยันตัวตนสแกน จากนั้นนำค่า ตัวเลข 6 หลักหรือ
otp มากรอก เพื่อยืนยันการเปิดใช้งาน
3. ในขั้นตอนนี้ ถ้ากรอกข้อมูลถูกต้อง ก็จะแจ้งว่าทำการ เปิดใช้งาน ระบบยืนยัน 2 ชั้น สำเร็จและ
ทำการอัปเดทฐานข้อมูลที่กล่าวไปแล้วข้างต้น รวมถึงแสดงรหัสสำหรับใช้ปลดล็อค เพื่อปิดระบบ
ยืนยัน 2 ชั้นกรณี ไม่สามารถใช้งานอุปกรณ์ยืนยันได้

ลำดับภาพตัวอย่าง ขั้นตอนการทำงาน เมื่อจะล็อกอินเข้าใช้งานแบบรองรับระบบยืนยัน 2 ชั้น
1. หน้าล็อกอินปกติ ผู้ใช้กรอก ชื้อผู้ใช้ และรหัสผ่านปกติ

ในขั้นตอนการล็อกอินปกติ เมื่อผู้ใช้กรอกข้อมูลถูกต้อง เราก็จะทำการสร้าง session ข้อมูล เพื่อเป็นเงื่อนไข
การเข้าใช้งาน แต่กรณีให้รองรับระบบยืนยัน 2 ชั้น เราจะต้องเพิ่มเงื่อนไขการตรวจสอบเข้าไปอีกชั้นก่อน
ตัวอย่าง
1 2 3 4 5 6 7 8 9 10 | if ( $login_pass ==true){ if ( $have_2fa ==1){ // ตรวจสอบก่อนว่า มีการเปิดใช้งานล็อกอิน 2 ชั้นหรือไม่ // สร้างตัวแปร session เฉพาะที่จำเป็นชั่วคราว เพื่อใช้งานกับการยืนยัน 2 ชั้น // ในที่นี้คือเราจะต้องดึงข้อมูลสมาชิกที่บันทึกไว้ 2 ค่ามาใช้งาน คือ userid กับ ค่า // google2fa_secret ที่สร้างและบันทึกในฐานข้อมูลครั้งแรกที่เปิดใช้งาน // ให้ทำการ redirect ไปหน้าสำหรับกรอก otp } else { // กรณีไม่ได้เปิดใช้งาน 2FA ก็ให้ทำงานปกติ // สร้าง session ข้อมูลสมาชิก สำหรับใช้งาน } } |
2. ในหน้าสำหรับกรอก otp ผู้ใช้จะต้องใช้รหัส otp จากแอปพลิเคชันยืนยันตัวตนเพื่อทำการ
ยืนยันการเข้าใช้งาน หน้านี้จะเป็นแค่หน้าฟอร์มทำธรรมดาสำหรับส่งข้อมูลเท่านั้น โดยจะทำการส่ง
ค่าไปยังไฟล์ verify.php หรือไฟล์ที่เรากำหนด เพื่อทำการตรวจสอบ ข้อมูล คล้ายๆกับขึ้นตอนการ
เปิดใช้งานครั้งแรก แต่ครั้งนี้ จะใช้ google2fa_secret จากฐานข้อมูลที่เราได้บันทึกไว้มาใช้งาน

หากทำการกรอกข้อมูลถูกต้อง ก็เข้าสู่ระบบสมาชิกและสร้าง session ข้อมูลทั้งหมดที่จำเป็น
3. ในหน้าขั้นตอนที่ 2 เราอาจจะมีลิ้งค์สำหรับกรณี ผู้ใช้ไม่สามารถใช้อุปกรณ์ยืนยันตัวตนได้ และต้อง
การปิดการใช้งาน และผู้ใช้ได้บันทึกรหัส google2fa_secret ไว้แล้ว ก็สามารถทำการลิ้งค์ไปยังหน้า
ปิดการใช้งานระบบยืนยัน 2 ชั้นได้

เมื่อกรอกรหัสถูกต้อง ก็ทำการแจ้งว่าปิดระบบเรียบร้อยแล้ว และให้ผู้ใช้ทำการเข้าสู่ระบบใหม่อีกครั้ง
ทั้งหมดก็เป็นขั้นตอนการทำงานของรูปแบบการใช้งาน ระบบยืนยัน 2 ชั้น
ตัวอย่างโค้ดการประยุกต์ระบบยืนยัน 2 ชั้น 2FA
โค้ดตัวอย่าง แยกทีละไฟล์ เพื่อให้เห็นภาพ ไม่ได้ประยุกต์หรือเพิ่มโค้ดทั้งหมด
ไฟล์ member.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 | <?php session_start(); // สำหรับทดสอบ กรณียังไม่มีข้อมูล if (!isset( $_SESSION [ 'userinfo' ])){ $_SESSION [ 'userinfo' ] = [ 'userid' => 'userid3456789' , ]; } // จำลองการปิดการใช้งานระบบ 2 ชั้น สามารถประยุกต์แบบปิดชั่วคราว และปิดถาวรได้ if (isset( $_GET [ 'revoke' ])){ unset( $_SESSION [ 'userinfo' ][ '2fa_active' ]); } $_2fa_active = (isset( $_SESSION [ 'userinfo' ][ '2fa_active' ]) && $_SESSION [ 'userinfo' ][ '2fa_active' ]==1)? " checked" : "" ; ?> <!DOCTYPE html> <html lang= "en" > <head> <meta charset= "UTF-8" > <meta name= "viewport" content= "width=device-width, initial-scale=1.0" > <title>Member Settings</title> <!-- Bootstrap CSS --> <link href= "https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" rel= "stylesheet" > <style> .login-container { max-width: 400px; margin: 0 auto; margin-top: 100px; } .form-container { margin: 0 auto; margin-top: 100px; } </style> </head> <body> <div class = "container login-container" > <h2 class = "text-center mb-4" >Settings</h2> <div class = "form-group" > <label for = "2faToggle" >Two-Factor Authentication</label> <div class = "custom-control custom-switch" > <input type= "checkbox" class = "custom-control-input" id= "2faToggle" <?= $_2fa_active ?> onchange= "toggle2FA(this.checked)" > <label class = "custom-control-label" for = "2faToggle" >Enable 2FA</label> </div> </div> </div> <div class = "container login-container" > <pre> <?php print_r( $_SESSION [ 'userinfo' ]); ?> </pre> <a href= "login.php" > Login Page.</a> </div> <!-- JavaScript --> <script> function toggle2FA(enabled) { if (enabled) { window.location = 'activate_2fa.php' ; // Send request to enable 2FA console.log( '2FA enabled' ); } else { window.location = 'member.php?revoke=1' ; // Send request to disable 2FA console.log( '2FA disabled' ); } } </script> </body> </html> |
ไฟล์ activate_2fa.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 | <?php use PragmaRX\Google2FA\Google2FA; use Endroid\QrCode\QrCode; // Start Server Session session_start(); require '../vendor/autoload.php' ; $google2fa = new Google2FA(); $_SESSION [ 'userinfo' ] = [ 'userid' => 'userid3456789' , 'google2fa_secret' => '' // กรณีเปิดใช้งาน ]; $_SESSION [ 'userinfo' ][ 'google2fa_secret' ] = $google2fa ->generateSecretKey(); // เรียกใช้ session ผ่านตัวแปร $user = $_SESSION [ 'userinfo' ]; // $user['google2fa_secret'] // ค่านี้จะต้องถูกบันทึกในฐานข้อมูลเพื่อใช้งาน ถ้าเปิดใช้ // กำหนดชื่อแอป ที่จะใช้งาน $app_name = 'Ninenik.com' ; // ข้อมูลที่จะใช้สร้างคิวอาร์โค้ด $qrCodeUrl = $google2fa ->getQRCodeUrl( $app_name , $user [ 'userid' ], // ใช้ค่าที่เป็นเฉพาะ อาจจะเป็นเบอร์โทรหรืออีเมลก็ได้ ต้องไม่ซ้ำกัน $user [ 'google2fa_secret' ] ); // กำหนดข้อคาม ค่า หรือข้อมูลที่ต้องการแสดงใน qrcode $qrCode = new QrCode( $qrCodeUrl ); $qrCode ->setSize(200); // กำหนดขนาดตามต้องการ หน่วย pixel ถ้าไม่กำหนดค่าเริ่มต้นเท่ากับ 300 // สร้างรูปภาพคิวอาร์โค้ดสำหรับแสดงเพื่อสแกน เปิดใช้งาน $encoded_qr_data = $qrCode ->writeDataUri(); // ค่า otp ที่ถูกสร้างทันทีใช้เรียกใช้งาน $current_otp = $google2fa ->getCurrentOtp( $user [ 'google2fa_secret' ]); ?> <!DOCTYPE html> <html lang= "en" > <head> <meta charset= "UTF-8" > <meta name= "viewport" content= "width=device-width, initial-scale=1.0" > <title>Two-Factor Authentication</title> <!-- Bootstrap CSS --> <link href= "https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" rel= "stylesheet" > <style> .login-container { max-width: 400px; margin: 0 auto; margin-top: 100px; } .form-container { margin: 0 auto; margin-top: 100px; } </style> </head> <body> <div class = "container form-container text-center" > หน้านี้จะทำการสร้างคิวอาร์โค้ด และสแกนแค่ครั้งแรกเท่านั้น ถ้าจะสแกนใหม่<br> ต้องทำการลบข้อมูลเดิมในแอปออกก่อน <h1>Two-Factor Authentication</h1> <h2>QR Code</h2> <p><img src= "<?= $encoded_qr_data; ?>" alt= "QR Code" ></p> Enter TOTP: <input type= "number" name= "otp" id= "otp" required /> <input type= "button" value= "Verify" onclick= "verify_otp();" /> <br> <p> <a href= "member.php" > Member.</a> </p> </div> <script> let input_otp = document.getElementById( 'otp' ); const verify_otp = async () => { let otp = document.getElementById( 'otp' ).value; const payload_data = { active: 1, otp: otp }; const queryParams = new URLSearchParams(payload_data).toString(); fetch( 'verify.php?' + queryParams) .then((response) => response.json()) .then((data) => { console.log(data) if (data.result == true) { alert( "Valid OTP" ); window.location= 'success_2fa.php' ; } else { alert( "Invalid OTP !!!" ); input_otp.value = '' ; } }); } </script> </body> </html> |
ไฟล์ success_2fa.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 | <?php session_start(); $user = $_SESSION [ 'userinfo' ]; ?> <!DOCTYPE html> <html lang= "en" > <head> <meta charset= "UTF-8" > <meta name= "viewport" content= "width=device-width, initial-scale=1.0" > <title>Two-Factor Authentication Enabled</title> <!-- Bootstrap CSS --> <link href= "https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" rel= "stylesheet" > <style> .login-container { max-width: 400px; margin: 0 auto; margin-top: 100px; } .form-container { margin: 0 auto; margin-top: 100px; } </style> </head> <body> <div class = "container login-container" > <h2 class = "text-center mb-4" >Two-Factor Authentication Enabled</h2> <div class = "alert alert-success" role= "alert" > Two-Factor Authentication has been successfully enabled. </div> <p>Your reset code is: <?= $user [ 'google2fa_secret' ]?></p> <p id= "resetCode" ></p> <div class = "alert alert-info" role= "alert" > <strong>Important:</strong> Please copy or save this reset code in a safe place. You will need it to recover your account if you lose access to your authenticator app. </div> <br> <p> <a href= "member.php" > Member</a> </p> </div> </body> </html> |
ไฟล์ login.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 | <!DOCTYPE html> <html lang= "en" > <head> <meta charset= "UTF-8" > <meta name= "viewport" content= "width=device-width, initial-scale=1.0" > <title>Login Form</title> <!-- Bootstrap CSS --> <link href= "https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" rel= "stylesheet" > <style> .login-container { max-width: 400px; margin: 0 auto; margin-top: 100px; } .form-container { margin: 0 auto; margin-top: 100px; } </style> </head> <body> <div class = "container login-container" > <h2 class = "text-center mb-4" >Login</h2> <form action= "2fa_verify.php" method= "post" > <div class = "form-group" > <label for = "username" >Username</label> <input type= "text" class = "form-control" id= "username" name= "username" value= "userid3456789" required> </div> <div class = "form-group" > <label for = "password" >Password</label> <input type= "password" class = "form-control" id= "password" name= "password" value= "test" required> </div> <button type= "submit" class = "btn btn-primary btn-block" >Login</button> </form> </div> </body> </html> |
ไฟล์ 2fa_verify.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 | <?php session_start(); // ถ้ามาหน้านี้ ต้องมีข้อมูลสองค่านี้เสมอ เพื่อใช้สำหรับการตรวจสอบ // ดึงจากฐานข้อมูลที่ได้บันทึกไว้ $_SESSION [ 'userinfo' ] = [ 'userid' => 'userid3456789' , 'google2fa_secret' => 'RUIAW5PTZZ5DVWDR' // สมมติค่าตัวอย่างที่บันทึกไว้แล้ว ]; ?> <!DOCTYPE html> <html lang= "en" > <head> <meta charset= "UTF-8" > <meta name= "viewport" content= "width=device-width, initial-scale=1.0" > <title>Two-Factor Authentication</title> <!-- Bootstrap CSS --> <link href= "https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" rel= "stylesheet" > <style> .login-container { max-width: 400px; margin: 0 auto; margin-top: 100px; } .form-container { margin: 0 auto; margin-top: 100px; } </style> </head> <body> <div class = "container login-container" > <h2 class = "text-center mb-4" >Two-Factor Authentication</h2> <form action= "verify_totp.php" method= "post" onsubmit= "return false;" > <div class = "form-group" > <label for = "totp" >Enter TOTP Token</label> <input type= "number" class = "form-control" id= "otp" name= "otp" required> </div> <button type= "submit" class = "btn btn-primary btn-block" onclick= "verify_otp();" >Verify</button> <br> <p> <a href= "disabled_2fa.php" > if you lose access to your authenticator app.</a> </p> </form> </div> <script> let input_otp = document.getElementById( 'otp' ); const verify_otp = async () => { let otp = document.getElementById( 'otp' ).value; const payload_data = { login: 1, otp: otp }; const queryParams = new URLSearchParams(payload_data).toString(); fetch( 'verify.php?' + queryParams) .then((response) => response.json()) .then((data) => { console.log(data) if (data.result == true) { alert( "Valid OTP" ); window.location= 'member.php' ; } else { alert( "Invalid OTP !!!" ); input_otp.value = '' ; } }); } </script> </body> </html> |
ไฟล์ disabled_2fa.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 | <!DOCTYPE html> <html lang= "en" > <head> <meta charset= "UTF-8" > <meta name= "viewport" content= "width=device-width, initial-scale=1.0" > <title>Disable Two-Factor Authentication</title> <!-- Bootstrap CSS --> <link href= "https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" rel= "stylesheet" > <style> .login-container { max-width: 400px; margin: 0 auto; margin-top: 100px; } .form-container { margin: 0 auto; margin-top: 100px; } </style> </head> <body> <div class = "container login-container" > <h2 class = "text-center mb-4" >Disable Two-Factor Authentication</h2> <form action= "disable_2fa.php" method= "post" > <div class = "form-group" > <label for = "resetCode" >Reset Code</label> <input type= "text" class = "form-control" id= "resetCode" name= "resetCode" required> <small id= "resetCodeHelp" class = "form-text text-muted" >Enter the reset code sent to your email or phone.</small> </div> <button type= "submit" class = "btn btn-danger btn-block" >Disable 2FA</button> <br> <p> <a href= "login.php" > Login Page.</a> </p> </form> </div> </body> </html> |
ไฟล์ verify.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | <?php use PragmaRX\Google2FA\Google2FA; session_start(); require '../vendor/autoload.php' ; $google2fa = new Google2FA(); // ใช้ข้อมูลผู้ใช้งานจาก session $user = $_SESSION [ 'userinfo' ]; // รับค่า otp ที่กรอกเข้ามา $otp = (isset( $_GET [ 'otp' ]))? $_GET [ 'otp' ]: '' ; // ตรวจสอบความถูกต้องของรหัส otp ถ้าถูกต้องส่งกลับค่าเป็น true / false $valid = $google2fa ->verifyKey( $user [ 'google2fa_secret' ], $otp ); $response = [ 'input_otp' => $otp , 'result' => $valid ]; if (isset( $_GET [ 'active' ]) && $_GET [ 'active' ]==1 && $valid ==true){ // ทำคำสั่งอัพเดทฟิลด์ข้อมูลในฐานข้อมูลของสมาชิก // member_2fa_active เท่ากับ 1 (เปิดใช้งาน) // member_2fa_revoke_key ให้ใช้ค่าเป็น $user['google2fa_secret'] $_SESSION [ 'userinfo' ][ '2fa_active' ]=1; } if (isset( $_GET [ 'login' ]) && $_GET [ 'login' ]==1 && $valid ==true){ // สร้าง session ที่เกี่ยวกับระบบสมาชิก $_SESSION [ 'userinfo' ][ '2fa_active' ]=1; } $response = json_encode( $response ); echo $response ; |
หวังว่าเนื้อหาต่อไปนี้จะมีประโยชน์ และเป็นแนวทางในการนำไปประยุกต์ใช้งานต่อไป