จะเขียน device cookie ยังไงครับ? ยังไม่เข้าใจกระบวนการของมันหลายอย่างด้วยครับ?
อ้างอิงจากบทความนี้
https://www.owasp.org/index.php/Slow_Down_Online_Guessing_Attacks_with_Device_Cookies
ผมลองพยายามแกะเป็นโค้ดได้ดังนี้
Code (PHP)
<?php
session_start();
?>
<form method="post" action="login.php">
<input type="text" name="username" value="" placeholder="Username"><br>
<input type="password" name="password" value=""><br>
<button type="submit">Login</button>
</form>
form.php
Code (PHP)
<?php
/**
* Device cookies with authenticate user to prevent brute-force attack
*
* @link https://www.owasp.org/index.php/Slow_Down_Online_Guessing_Attacks_with_Device_Cookies OWASP reference.
*/
session_start();
// assume that this username & password is in DB.
$username = 'admin';
$password = '$2y$10$/FRHJxB0GuW82R9uo2wNu.Hq.FADk522Nvg.BY90wwkbomjBRE6kK';// pass
if (!$_POST) {
header('Location: form.php');
exit();
}
if ($_POST) {
// Entry point for authentication request ----------------------------------------------
if (isset($_COOKIE['deviceCookie'])) {
// 1. if the incoming request contains a device cookie
// --- a. validate device cookie
if (validateDeviceCookie() === true) {
/*if (?HOW TO CHECK DEVICE COOKIE IN LOCKOUT LIST?) {
// c. if the device cookie is in the lockout list
} else {
// else
// authenticate user
authenticateUser();
}*/
} else {
// b. if device cookie is not valid then proceed to step 2.
// reject authentication attempt
rejectAuthentication();
}
} /*elseif (?HOW TO AUTHENTICATE UNTRUSTED CLIENTS?) {
// 2. if authentication from untrusted clients is locked out for the specific user
// --- reject authentication attempt
rejectAuthentication();
}*/ else {
// 3. else
// --- authenticate user
authenticateUser();
}
}// endif;
/**
* Authenticate user.
*/
function authenticateUser()
{
global $username, $password;
// 1. check user credentials
if (isset($_POST['username']) && $_POST['username'] == $username) {
if (isset($_POST['password']) && password_verify($_POST['password'], $password) === true) {
// รหัสผ่านถูก
$credentialValid = true;
} else {
// รหัสผ่านผิด
$credentialValid = false;
}
} else {
// ชื่อผู้ใช้ผิด
$credentialValid = false;
}
if ($credentialValid === true) {
// 2. if credentials are valid
// --- a. issue new device cookie to user’s client
issueNewDeviceCookie();
// --- b. proceed with authenticated user
echo 'login success.';
exit();
} else {
// 3. else
// --- a. register failed authentication attempt
// --- ?HOW TO REGISTER FAILED AUTH?
// --- b. finish with failed user’s authentication
echo 'wrong username or password!';
exit();
}
}// authenticateUser
/**
* Issue new device cookie to user’s client.
*/
function issueNewDeviceCookie()
{
$login = ($_POST['username'] ?? 0);
$nonce = md5(random_bytes(32));
$secretKey = generateRandomString();
$signature = hash_hmac('sha256', $login . ',' . $nonce, $secretKey);
// assume that signature will be in more secure place.
setcookie('deviceCookie', $login . ',' . $nonce . ',' . $signature . ',' . $secretKey, (time() + (120 * 24 * 60 * 60)), '/');
}// issueNewDeviceCookie
/**
* Reject authentication attempt.
*/
function rejectAuthentication()
{
echo 'Unable to authenticate (rejected).';
exit();
}// rejectAuthentication
/**
* Validate device cookie.
*/
function validateDeviceCookie()
{
global $username;
if (isset($_COOKIE['deviceCookie'])) {
$cookieValue = $_COOKIE['deviceCookie'];
list($login, $nonce, $signature, $secretKey) = explode(',', $cookieValue);
if ($_POST['username'] . ',' . $nonce . ',' . $signature . ',' . $secretKey === $cookieValue) {
// 1. Validate that the device cookie is formatted as described above
if (
hash_equals(
hash_hmac('sha256', $_POST['username'] . ',' . $nonce, $secretKey),
$signature
)
) {
// 2. Validate that SIGNATURE == HMAC(secret-key, “LOGIN,NONCE”)
if ($login === $username) {
// 3. Validate that LOGIN represents the user who is actually trying to authenticate
return true;
}
}
}
}
return false;
}// validateDeviceCookie
/**
* Generate random string
*
* @link https://stackoverflow.com/a/4356295/128761 Original source code.
*/
function generateRandomString($length = 10)
{
$characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
$charactersLength = strlen($characters);
$randomString = '';
for ($i = 0; $i < $length; $i++) {
$randomString .= $characters[rand(0, $charactersLength - 1)];
}
return $randomString;
}// generateRandomString
login.php
ทีนี้ยังติดขัดหลายๆส่วนเลย คือไม่เข้าใจข้อ
Entry point
1. c.
2.
Authenticate
3. a.
Register failed authentication attempt
อันนี้ไม่รู้เรื่องเลย จะเอาไป register กับอะไร? กับฐานข้อมูล?
สุดท้ายแล้วกลับมางงตรงกระบวนการ ถ้าผู้ใช้ไม่มี cookie อะไรเลย (ส่วนใหญ่พวก brute force attack ก็ใช้โปรแกรม auto คงไม่มาเก็บ cookie ไว้ให้) แล้วมันจะไปติดขัดตรงขั้นตอนไหนได้ ในเมื่อ entry point ก็ปล่อยยาวไป 3. else authenticate เลย
ใครเข้าใจช่วยอธิบายหรือต่อเติมโค้ดให้ทีครับ?Tag : PHP
ประวัติการแก้ไข 2019-07-04 11:34:30 2019-07-04 13:42:21
Date :
2019-07-04 11:28:03
By :
mr.v
View :
1092
Reply :
9
ผมว่า มันแค่เป็นการกำหนดค่าเฉพาะให้กับ browser ที่เปิดหน้า login โดยเฉพาะ และถ้าไม่เปิดหน้า login จะไม่ได้ค่านี้มา
และเมื่อ ทำการ submit เพื่อ login ถ้าไม่มีค่านี้ ก็จะไม่ให้ login
หรือ ถ้าจำนวนครั้งในการใช้ค่านี้เพื่อ login มากเกินไปก็ให้หยุด
เป็นแค่การป้องกันการใช้ bot สุ่มหา password เท่านั้นอะครับ
Date :
2019-07-05 07:26:42
By :
Chaidhanan
ใช่ครับ มันทำเพื่อกัน brute force นั่นแหละ แต่ว่าเงื่อนไขของมันผมดูแล้วไม่เข้าใจจะกันยังไง คือถ้า login ผ่านมันถึงจะสร้าง cookie ให้ (issueNewDeviceCookie) นอกนั้นไม่สร้าง
แล้วทีนี้ถ้าเป็นผู้โจมตี มันจะเหมือนส่งเข้าหน้า login เลยจาก entry point ดูไม่เหมือนจะเข้าข้อ 1 2 เลยแล้วไม่รู้จะกันได้ยังไง?
Date :
2019-07-05 10:46:47
By :
mr.v
ไม่ใช่มั้งครับ จะสร้างตอน เข้าหน้า เพื่อพิมพ์ user / password เท่านั้นครับ
พอส่ง request login ตรวจสอบผ่านก็ลบ มันออก แต่ status เป็น login แล้ว ก็ทำงานอื่นๆ ได้ต่อไป
แต่ถ้ายังส่ง มา request login อยู่อีก ก็ไม่ได้แล้ว เพราะ มันถูกลบออกไปแล้ว
ประวัติการแก้ไข 2019-07-05 10:54:09
Date :
2019-07-05 10:51:41
By :
Chaidhanan
มันไม่มีลบออกนะครับ ลองดู https://www.owasp.org/index.php/Slow_Down_Online_Guessing_Attacks_with_Device_Cookies หัวข้อ Protocol กระบวนการของมันอยู่ตรงนั้นทั้งหมด เพียงแต่ไม่เข้าใจ
แล้วมันไม่ได้สร้าง cookie ตอนเข้าหน้าด้วย มันสร้างตอน authenticate user
เริ่มจาก Entry point for authentication request
ถ้าไม่เคยใช้งานเลยมันจะเข้าข้อ 3. else authenticate user∎
แล้วใน Authenticate user เมื่อตรวจ user+pass ข้อ 1. แล้ว ข้อ 2. ถ้าผ่านจึงจะ issue new device cookie
มันไม่มีการบอกให้ลบเลยครับ
อันนี้หาตัวอย่างเจอแต่เป็น python อ่านไม่รู้เรื่องภาษานี้เลย
https://github.com/mkromkamp/Device-cookies-example
edit:
ตอนนี้พอจะเข้าใจคร่าวๆแล้วว่า issue new device cookie คือสำหรับคนที่ login ผ่าน เป็น cookie ที่อยู่ยาวเป็นพิเศษ ไว้ login ครั้งต่อไปจะไม่ติดล็อค ในขณะที่ผู้โจมตีจะใช้การสุ่มรหัสไปเรื่อยๆจนติดล็อค แต่ถ้ามีคุกกี้นี้ก็จะไม่ติดเพราะเคยเป็น user เจ้าของจริงที่ login ผ่านมาแล้ว.
ส่วนกรณี user เจ้าของจริง แต่ clear browser ใหม่แล้วมาติด lock เพราะโดนผู้โจมตีสุ่มจน account lock ก็ให้ใช้ password reset link ส่งเข้า email ให้ login แทน
ประวัติการแก้ไข 2019-07-05 11:03:33 2019-07-05 11:14:24 2019-07-05 11:16:39
Date :
2019-07-05 11:00:28
By :
mr.v
Issue new device cookie to user’s client Issue a browser cookie with a value like “LOGIN,NONCE,SIGNATURE”, where
ตรงนี้ ผมตีความหมายว่า ให้ ออกคุ๊กกี้ เมืออยุ่หน้า ที่มีข้อความ ดังต่อไปนี้ LOGIN,NONCE,SIGNATURE
หน้านี้จะยังไม่ได้ทำการ login นะครับ แต่แค่ เตรียม แค่ใส่ข้อมูล ยังไม่ได้ submit
พอมีการ ส่ง request login เข้ามา
Entry point for authentication request
1. if the incoming request contains a device cookie:
ถ้า ข้อมูลที่เข้ามา มีข้อมูลของ device cookie ที่เรา gen
a. validate device cookie
ตรวจสอบ
b. if device cookie is not valid then proceed to step 2.
ถ้าไม่ถูกต้องไป 2
c. if the device cookie is in the lockout list reject authentication attempt∎
ถ้าข้อมูล อยูในส่วนที่ต้องยกเลิก (Lockout List) อะไรก็ได้ที่ต้องยกเลิก ให้ ลบ
d. else
authenticate user∎
สร้าง session ว่า login แล้ว
***********************************
2. if authentication from untrusted clients is locked out for the specific user
reject authentication attempt∎ ตรงนี้ก็ให้ลบออก reject authentication attempt
3. else
authenticate user∎
Date :
2019-07-05 11:22:36
By :
Chaidhanan
issue cookie อยู่ในข้อ 2 ของการ authen คือ if credentials are valid ถ้า login ถูกต้องแล้วเท่านั้นถึงส่ง cookie ไปครับ
ทีนี้ส่วนนี้พอได้ละ แต่ตรง register failed auth นี่น่าจะต้องอาศัยฐานข้อมูลหรือเปล่าครับ?
ยังงงๆอยู่ว่า Entry point 1. > c. if the device cookie is in the lockout list กับ
Entry point 2. กับ
Register failed authentication attempt
มันจะออกมายังไง? ต้องทำตารางเก็บรายการ failed auth?
ประวัติการแก้ไข 2019-07-05 11:33:14 2019-07-05 11:36:13 2019-07-05 11:37:01
Date :
2019-07-05 11:30:33
By :
mr.v
ผมว่าโดยหลักการ(จากที่เข้าใจคร่าวๆ)ของเขาดีมากเลยนะ.
คือถ้าเคยเข้าสู่ระบบแล้วผ่าน จะถือว่าคือตัวจริง จะมี device cookie ติดตัวไปยาวๆ สมมุติสัก 2 ปี
ทีนี้ถ้าผู้โจมตีไม่เคยมี device cookie เข้ามาก็จะผ่านไป authen ได้เลย ลอง login จนกว่าจะหมดโควต้าแล้วติดล็อค (lock)
และถ้าผู้เคย login ผ่านแต่มาไม่ผ่านอีก เช่น คอมพิวเตอร์เครื่องเดียวใช้ในครอบครัว ของพี่จะทะลึ่งมาเข้าของน้องเป็นต้น ก็จะลองได้จนหมดโควต้าแล้วล็อค. กรณีนี้จัดเป็น invalid device cookie.
กรณีผู้โจมตีลอง login อีกแล้วมันยังไม่หมดโควต้าปลดล็อค ก็ติดล็อคมันไปเรื่อยๆ. กรณีนี้จัดเป็น untrusted clients.
กรณีผู้ใช้จริง ถ้าเคย login ผ่านแล้วไม่กลายเป็น invalid device cookie ก็ผ่านเข้าไป authenticate ได้สบายๆ. เงื่อนไขนี้ช่วยให้ตัวจริงไม่ต้องเดือดร้อน ดีมากเลยครับผมว่า.
แต่กรณีผู้ใช้จริงมาใหม่แบบสด เช่น ล้างเบราเซอร์ใหม่, ลง os ใหม่ ไม่มี device cookie เลยแล้วติดล็อคเพราะโดนผู้โจมตีสุ่มจนมัน lock (DoS) ก็ให้ใช้วิธีส่งลิ้งค์ login ผ่าน email แทน. คล้ายๆ reset password link.
เดี๋ยวจะลองไล่ส่วนที่เหลือดู ถ้าทำได้จะเอาโค้ดมาแจก ถ้าติดขัดเดี๋ยวมาถามอีก เพราะน่าจะเป็นประโยชน์กับหลายคนทีเดียว.
Date :
2019-07-05 12:05:03
By :
mr.v
Load balance : Server 01