สรุป PHP Web Application Security

1. ต้อง Validate input หรือตรวจสอบความถูกต้องของข้อมูลทุกอย่างที่นำเข้ามา เช่น ต้องตรวจสอบว่ามีข้อมูล หรือใช่ข้อมูลที่เราต้องการจริงๆ หรือใหม่

เช่น การใช้ isset(), ?? operator, is_*() อาจเขียนเอง หรือใช้ ctype Extension หรือ ฟังก์ชัน filter_var, filter_input

ส่วนใน PHP 7 ให้ใช้ความสามารถของ Type Declarations โดยต้องประกาศคำสั่ง

declare(strict_types=1);

ไว้บนสุดของไฟล์ เพื่อ strict ชนิดของตัวแปรครับ
2. การป้องกัน Cross-site Scripting (XSS) ต้องมีการกรอง หรือ Filtering Input เสมอ เช่น

– ใช้คำสั่ง strip_tags($s) เพื่อเอาคำสั่ง HTML ออกไป
หรือจะใช้คำสั่ง filter_var($s, FILTER_SANITIZE_STRING) เพื่อเอาคำสั่ง HTML ออกก็ได้เหมือนกัน

– ใช้คำสั่ง preg_replace($, ‘/…/’, ”) เพื่อกรองและป้องกันด้วย regular expressions (เขียนเอง)

2.1 การป้องกัน Cross-site Scripting อีกอย่าง คือ การใช้ฟังก์ชัน htmlspecialchars($s) ยกตัวอย่างที่ควรทำ

เช่น

//ตัวอย่าง string
$s = ‘<p class=”c”>Let\’s go!</p>’;

//วิธีการเขียนที่ดี
echo htmlspecialchars($s);
echo htmlspecialchars($s, ENT_QUOTES);
echo htmlentities($s, ENT_QUOTES);
echo filter_var($s,FILTER_SANITIZE_FULL_SPECIAL_CHARS);

2.2 การป้องกัน Cross-site Scripting ด้วยการส่งไฟล์ชนิด JSON เข้ามา

$data = [
‘term’ => $_GET[‘q’]
];

header(‘Content-type: application/json’);

echo json_encode(
$data,
JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT |
JSON_HEX_AMP);
3. การป้องกัน SQL Injection

3.1 อย่างแรกให้ escape string ที่เข้ามา ยกตัวอย่าง เช่น

//สำหรับ MySQL/MariaDB
mysqli_real_escape_string($db, $value);
หรือ $db->real_escape_string($value);

//สำหรับ PostgreSQL
pg_escape_string($db, $value);

//สำหรับ SQLite
SQLITE3::escapeString($value);

3.2 ใช้ Prepared Statements ของ PDO (PHP Data Objects) ตัวอย่าง เช่น

$db = new PDO(…);
$sql = ‘INSERT INTO searches (term) VALUES (:term)’;
$cmd = $db->prepare($sql);
$cmd->bindParam(‘:term’, $term);
$cmd->execute();

3.3 ใช้ Prepared Statements ของ MySQLi

$db = new MySQLi(…); //mysqli_connect()
$sql = ‘INSERT INTO searches (term) VALUES (?)’;
$cmd = $db->prepare($sql); //mysqli_prepare()
$cmd->bind_param(‘s’, $term); //mysqli_stmt_bind_param()
$cmd->execute(); //mysqli_stmt_execute()

ป.ล. สำหรับคนที่ไม่ได้เขียนในแนว OOP ลองค้นเพิ่มได้ครับ วิธีการคำสั่งคล้ายกัน
4. เพิ่มความปลอดภัยให้กับ Cookies แนะนำให้ใช้ HTTPS และใช้ฟังก์ชัน setcookie ให้ถูกต้อง ด้วยการ set ค่าพารามิเตอร์ secure ให้เป็น true (กรณีใช้ https) และค่า httponly ให้เป็น true
ดูฟังก์ชัน setcokkieได้ที่นี่ http://goo.gl/4QulZg

ตัวอย่างการเขียน
//ปลอดภัย
setcookie(‘Cookie1’, rand(100, 999), 0, ‘/’, ”, true, true);

//ไม่ปลอดภัย
setcookie(‘Cookie2’, rand(100, 999), 0, ‘/’, ”, false, false);
5. การป้องกัน Cross-site Request Forgery (CSRF)

5.1 สร้างและเก็บค่า token โดยการสุ่มขึ้นมา
$name = ‘token-‘ . mt_rand();
$token = random_bytes(32);

$_SESSION[$name] = $token;

5.2 นำค่าตัวแปร token ไปใส่ไว้ในทุกๆฟอร์ม เช่น

<input type=”hidden” name=”_csrfname” value=”token-123456″>
<input type=”hidden” name=”_csrfvalue” name=”a1b2c3d4e5f6″>

5.3 จากฟอร์มข้อ 5.2 เมื่อผู้ใช้คลิก Submit ฟอร์มแล้วก็อย่าลืม isset เช็คตัวแปร และค่าด้วยนะครับว่าตรงกันกับที่ส่งมามั้ย

ป.ล. จากข้อ 5.2 นี้ถ้าใครเคยเขียน Framework จะเห็นว่าเกือบทุก Framework จะมีมาให้แล้ว
6. เข้ารหัส Password ด้วยวิธีที่ถูกต้อง

6.1 Hash passwords ทุกครั้ง

6.2 อย่าใช้ algorithms เช่น MD5 หรือ SHA1 (มันเก่าแล้ว และไม่ปลอดภัย)

6.3 ให้ใช้ Password Hashing API ของ PHP เช่น
password_hash(), password_get_info(), password_needs_rehash(), password_verify()