การสร้างโปรแกรมประยุกต์เว็บ > ระบบการรับรองของ PHP และ MySQL
การระบุผู้ใช้
เว็บเป็นตัวกลางปิดบังชื่อ แต่สามารถรู้ว่าใครเข้ามาถึงเว็บ
การติดต่อผ่านอินเตอร์เน็ตเป็นการ "สนทนา" ระหว่างคอมพิวเตอร์ผ่าน IP address ซึ่งระบบที่เป็นอยู่ไม่ใช่ identifier ถาวร ในขณะที่ แม่ข่ายสามารถค้นพบข้อมูลจำนวนมากเกี่ยวกับคอมพิวเตอร์และเครือข่ายที่เชื่อมต่อ
โชคดีสำหรับผู้ใช้เว็บ ไม่มีสารสนเทศที่ browser ของเขาต้องระบุตัวเอง ถ้าต้องการทราบถึงชื่อหรือรายละเอียดของผู้เยี่ยมชม จึงต้องมีการถาม
ควบคุมการเข้าถึง
การควบคุมการเข้าถึงอย่างง่ายใช้ได้ไม่ยาก ตัวอย่างชุดนี้ประกอบด้วยฟอร์ม HTML สำหรับการเข้าสู่ระบบ สคริปต์ประมวลผลที่จะพิจารณาว่าชื่อผู้ใช้และรหัสผ่านถูกต้องหรือไม่ ด้วยการเรียกไฟล์ ที่เก็บชุดคำสั่งสำหรับการประมวลผลการเข้าสู่ระบบ ถ้าถูกต้องจะไปยังเพจต้อนรับ กรณีอื่นส่งกลับไปยังฟอร์ม HTML พร้อมข่าวสารความผิดพลาดและให้ป้อนชื่อและรหัสผ่านให้ ภาพ 2.2.1 แสดงหน้าเข้าสู่ระบบ
ภาพ 2.2.1 หน้าจอป้อนชื่อและรหัสผ่าน
ถ้าข้อมูลนำเข้าไม่ถูกต้อง สคริปต์ประมวลผลจะส่งกลับมาหน้านี้ใหม่พร้อมข่าวสารความผิดพลาดตามภาพ 2.2.2
ภาพที่ 2.2.2 แสดงข่าวสารความผิดพลาดจากการเข้าสู่ระบบไม่ถูกต้อง
ถ้าชื่อและรหัสผ่าน สคริปต์ประมวลผลจะส่งไปหน้าต้อนรับตามภาพ 2.2.3
ภาพ 2.2.3 หน้าต้อนรับ ถ้าผ่านการประมวลผล
ภาพ 2.2.4 แสดงความผิดพลาดจากการเข้าสู่หน้าต้อนรับโดยตรง
เพจแรก basic_login.php ตามภาพที่ 2.2.1 และ 2.2.2 อยู่ในรายการคำสั่ง 2.2.1 ที่คุณลักษณะ action เป็น basic_process.php และอยู่ในรายการคำสั่ง 2.2.2 สำหรับเพจต้อนรับอยู่ในรายการคำสั่ง 2.2.3
ในสคริปต์ basic_process.php รวมไฟล์ basic_manager.inc สำหรับประมวลผลการเข้าสู่ระบบ ด้วย class UserManager ได้ แสดงในรายการคำสั่ง 2.2.1
รายการคำสั่ง 2.2.1 สคริปต์ basic_login.html
<h1>Widebase</h1>
<form action="basic_process.php" method="post">
<table width="300" border="0">
<tr>
<td width="75">ชื่อ</td>
<td><input name="username" type="text" size="30" maxlength="50"></td>
</tr>
<tr>
<td>รหัสผ่าน</td>
<td><input name="password" type="password" size="30" maxlength="50"></td>
</tr>
<tr>
<td> </td>
<td><input name="Login" type="submit" id="Login" value="Login"></td>
</tr>
</table>
</form> |
รายการคำสั่ง 2.2.2 สคริปต์ basic_process.php
<?php
require_once('basic_manager.inc');
// 1. เริ่มต้น session()
session_start();
$_SESSION['valid'] = TRUE;
$_SESSION['msg'] = "prepare login";
// 2. ตรวจสอบการนำเข้า
if (!isset($_POST['username']) || $_POST['username'] == ''
|| !isset($_POST['password']) || $_POST['password'] == '')
{
$islogin[1] = "ไม่ได้ป้อนชื่อหรือรหัสผ่าน";
$islogin[0] = FALSE;
}
else
{
$user_name = $_POST['username'];
$user_pass = $_POST['password'];
$islogin[0] = TRUE;
}
// 3. เรียก User Manager ประมวลผลการนำเข้า
if ($islogin[0] == TRUE)
{
$usermgr = new UserManager();
$islogin = $usermgr->processLogin($user_name, $user_pass);
}
// 4. ส่งต่อเพจ ถ้าสำเร็จไป basic_welcome.php ถ้าล้มเหลวกลับไป basic_login.php
$_SESSION['valid'] = $islogin[0];
$_SESSION['msg'] = $islogin[1];
if ($islogin[0] == FALSE)
header("Location:basic_login.php");
else
header("Location:basic_welcome.php");
?>
|
รายการคำสั่ง 2.2.3 สคริปต์ basic_welcome.php
<?php
session_start();
if (!isset($_SESSION) )
{
echo "ท่านไม่ได้เข้าสู่ระบบ โปรดกลับไปหน้าเข้าสู่ระบบใหม่<br/>";
echo "<a href='basic_login.html'><font color=blue>เข้าสู่ระบบ</font></a>";
}
else if($_SESSION['valid'] == TRUE)
{
echo "<h1>ยินดีต้อนรับ</h1>";
echo "ท่านได้เข้าสู่ระบบเรียบร้อย";
}
else
{
echo "การเข้าสู่ระบบไม่ถูกต้อง โปรดกลับไปหน้าเข้าสู่ระบบใหม่<br/>";
echo "<a href='basic_login.html'><font color=blue>เข้าสู่ระบบ</font></a>";
}
?>
|
รายการคำสั่ง 2.2.4 สคริปต์ basic_manager.inc
<?php
define('USERNAME', 'myuser');
define('PASSWORD', 'password');
class UserManager
{
public function __construct()
{
// no code
}
public function processLogin($in_user_name, $in_user_passwd)
{
$valid = array();
// 1. ตรวจสอบอากิวเมนต์
if ($in_user_name == '' || $in_user_passwd == '')
{
$valid[1] = "เรียกฟังก์ชันด้วยพารามิเตอร์ไม่ถูกต้อง<br/>";
$valid[0] = FALSE;
exit;
}
// 2. ตรวจสอบชื่อและรหัสผ่าน
$valid = $this->confirmLogin($in_user_name, $in_user_passwd);
return $valid;
}
private function confirmLogin($in_uname, $in_upasswd)
{
// ชื่อเป็นตัวพิมพ์มีผล
$in_user_name = strtolower($in_uname);
$in_user_passwd = strtolower($in_upasswd);
$valid = array();
if ($in_user_name == USERNAME && $in_user_passwd == PASSWORD)
{
$valid[0] = TRUE;
}
else
{
$valid[0] = FALSE;
$valid[1] = "ชื่อผู้ใช้หรือรหัสผ่านไม่ถูกต้อง<br/>";
}
return $valid;
}
}
?> |
ชุดคำสั่งนี้ จะให้กลไกรับรองอย่างง่ายเพื่อยอมให้รับรองผู้ใช้เพื่อดูเพจ แต่มีบางปัญหาสำคัญ สคริปต์นี้
- มี 1 ผู้ใช้และรหัสผ่าน เป็นคำสั่งตายตัวในสคริปต์
- เก็บรหัสผ่านเป็นข้อความปกติ
- ส่งผ่านรหัสผ่านเป็นข้อความปกติ
ประเด็นเหล่านี้สามารถแก้ไขได้เพิ่มระดับการเขียนคำสั่ง
การเก็บรหัสผ่าน
มีหลายที่สำหรับการเก็บชื่อผู้ใช้และรหัสผ่านที่ดีกว่าภายในสคริปต์ ภายในสคริปต์มีความลำบากในการปรับปรุงข้อมูล อาจจะเป็นไปได้แต่มีความคิดที่ไม่ดี ในการเขียนสคริปต์ให้ปรับปรุงตัวเอง หมายความว่าต้องมีบนแม่ข่ายซึ่งประมวลผลบนแม่ข่าย แต่เขียนหรือปรับปรุงโดยอีกสคริปต์ การเก็บข้อมูลในอีกไฟล์บนแม่ข่ายจะช่วยให้เขียนโปรแกรมเพื่อการเพิ่มหรือลบผู้ใช้ และแก้ไขรหัสผ่านได้ง่าย
ภายในสคริปต์หรือไฟล์ข้อมูลอีกไฟล์ มีข้อจำกัดด้านจำนวนของผู้ใช้โดยปราศจากผลกระทบความเร็วของสคริปต์ ถ้ากำลังพิจารณาการจัดเก็บและค้นหารายการจำนวนมากในไฟล์ควรพิจารณาการใช้ฐานข้อมูลแทน ถ้าต้องการเก็บและค้นหามากกว่า 100 รายการ ควรเก็บในฐานข้อมูลแทนที่ไฟล์ข้อความ
การใช้ฐานข้อมูลเพื่อเก็บชื่อและรหัสผ่านไม่ทำให้สคริปต์ซับซ้อนขึ้น แต่ยอมให้รับรองผู้ใช้หลายรายอย่างรวดเร็ว สิ่งนี้ยอมให้เขียนสคริปต์เพื่อเพิ่มผู้ใช้ใหม่ ลบผู้ใช้และยอมให้ผู้ใช้เปลี่ยนรหัสผ่านของตัวเอง
การปรับปรุงเป็นแก้ไขใน class UserManager ให้ทำให้กับฐานข้อมูล และเพิ่มไฟล์รวมที่เก็บสารสนเทศการเชื่อมต่อกับฐานข้อมูล สำหรับคำสั่งในสคริปต์อื่นไม่มีการเปลี่ยนแปลง
การเข้ารหัส
ถ้าไม่เก็บข้อมูลในฐานข้อมูลหรือไฟล์มีความเสี่ยงที่ไม่จำเป็นในเก็บรหัสข้อความปกติ อัลกอริทึมแฮชทางเดียว สามารถให้ความปลอดภัยมากขึ้นเล็กน้อยด้วยความพยายามเพิ่มเล็กน้อย
ฟังก์ชัน crypt () ของ PHP เป็นฟังก์ชันแฮชเข้ารหัสทางเดียวไวยากรณ์ของฟังก์ชันนี้คือ
string crypt (string str [,string salt])
string str ส่งออกข้อความรหัส pseudo เป็น "pauONM/HSu9pM" ข้อความนี้ไม่สามารถได้รับถอดรหัสและส่งออกกลับเป็น "pass" โดยผู้ส่งออก คุณสมบัตินี้ทำให้ crypt () มีประโยชน์คือ ผลลัพธ์สิ้นสุด ถ้าให้ข้อความ salt เดียวกัน เช่น xyz ฟังก์ชัน crypt () จะส่งออกผลลัพธ์เดียวกันทุกครั้งที่เรียกใช้ ตัวอย่าง
if (crypt($password, 'xyz') == "pauONM/HSu9pM");
นอกจากนี้สามารถใช้ฟังก์ชัน md5()
ถ้าใช้ฐานข้อมูล MYSQL เพื่อเก็บข้อมูลเข้ารหัส สามารถใช้ฟังก์ชัน crypt () หรือ md5() ของ PHP หรือ ฟังก์ชัน PASSWORD () ของ MySQL ฟังก์ชันเหล่านี้สร้างผลลัพธ์ไม่เหมือนกันแต่ให้บริการวัตถุประสงค์เดียวกันทั้ง crypt () และ PASSWORD () ใช้ข้อความและประยุกต์อัลกอริทึมแฮชย้อนกลับไม่ได้ (Non Reversible Hash Algorithm)
การใช้ PASSWORD () สามารถเขียนประโยคคำสั่งคิวรี่
SELECT count (*) FROM users
WHERE name = '$name' AND pass = PASSWORD('$password')
คิวรี่นี้จะนับจำนวนแถวในตารางข้อมูล users ที่มีค่าตาม WHERE Clause โดยฟังก์ชัน PASSWORD () ที่ประยุกต์กับ $password สมมติว่าบังคับให้มีชื่อผู้ใช้ไม่ซ้ำ ผลลัพธ์ของคิวรี่นี้จะเป็น 0 หรือ 1
การป้องกันหลายเพจ
การสร้างสคริปต์ป้องกันมากกว่า 1 เพจ ยากขึ้นเล็กน้อย เนื่องจาก HTTP ไม่มีสถานะคือไม่มีการเชื่อมโยงหรือเกี่ยวข้องกันอย่างอัตโนมัติระหว่างคำขอต่อเนื่องจากบุคคลเดียวกัน สิ่งนี้ทำให้ยากกับการส่งผ่านข้อมูล เช่น สารสนเทศการรับรองว่าผู้ใช้ได้เข้ามาข้ามเพจ
วิธีการง่ายที่สุดในการป้องกันหลายเพจคือ ใช้กลไกควบคุมการเข้าถึงที่ให้โดยแม่ข่ายเว็บที่จะดูต่อไป
ในการสร้างการทำงานขึ้นเอง สามารถรวมส่วนของสคริปต์ตามรายการคำสั่ง 2.2.1 ในทุกเพจ ที่ต้องการป้องกัน การใช้ auto_prepend_file และ auto_append_file ทำให้สามารถเติมหน้าและต่อท้ายคำสั่งที่ต้องการกับทุกไฟล์ ในไดเรคทอรีเฉพาะ คำสั่งนี้ได้รับอธิบายแล้วในบทที่ 6 "คำสั่งใช้ใหม่และฟังก์ชัน"
ถ้าใช้วิธีการนี้จะไม่มีความต้องการให้ผู้เยี่ยมชมป้อนชื่อและรหัสผ่านสำหรับทุกเพจที่ต้องการดู
ในการเพิ่มรายละเอียดในการเข้าทุก hyperlink บนเพจ ผู้ใช้อาจจะมี space หรือตัวอักษรอื่นที่ไม่ได้รับอนุญาตใน URL ควรใช้ฟังก์ชัน urlencode () เพื่อเข้ารหัสตัวอักษรเหล่านี้อย่างปลอดภัย
|