T
Trinh Digital
Xây dựng Hệ thống

Bảo mật website: 15 bước hardening mà developer thường bỏ qua

Trinh Digital · · 9 phút đọc

Bảo mật website không chỉ là cài SSL — đó chỉ là bước 1 trong 15 bước hardening cần thiết. Theo OWASP (2025), 94% website có ít nhất 1 lỗ hổng bảo mật, và 74% developer thừa nhận không biết hết các kỹ thuật hardening. Báo cáo của Sucuri cho thấy 53% website bị hack do lỗi cấu hình mà developer bỏ qua — không phải do lỗ hổng zero-day phức tạp.

Bài viết này liệt kê 15 bước hardening cụ thể với code mẫu — từ đơn giản đến nâng cao.

Lead magnet: Tải miễn phí Website Security Hardening Checklist — checklist 50 điểm kiểm tra bảo mật cho website.

Bước 1: Security Headers

Security headers là dòng lệnh HTTP server gửi cho browser, chỉ định cách browser xử lý nội dung. Đa số website thiếu headers quan trọng nhất.

Cấu hình Nginx

# Thêm vào server block
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
HeaderChức năngNếu thiếu
X-Frame-OptionsChặn website bị nhúng trong iframeClickjacking attack
X-Content-Type-OptionsNgăn browser đoán MIME typeMIME-type sniffing attack
X-XSS-ProtectionKích hoạt XSS filter của browserCross-site scripting
Referrer-PolicyKiểm soát thông tin referrerLeak URL nhạy cảm
Permissions-PolicyKiểm soát browser APIsCamera/mic hijacking
HSTSBắt buộc HTTPSSSL stripping attack

Bước 2: Content Security Policy (CSP)

CSP là security header mạnh nhất nhưng phức tạp nhất — kiểm soát browser được phép load resources từ đâu.

add_header Content-Security-Policy "
    default-src 'self';
    script-src 'self' 'unsafe-inline' https://www.googletagmanager.com https://www.google-analytics.com;
    style-src 'self' 'unsafe-inline' https://fonts.googleapis.com;
    img-src 'self' data: https:;
    font-src 'self' https://fonts.gstatic.com;
    connect-src 'self' https://www.google-analytics.com;
    frame-src 'self' https://www.youtube.com;
    object-src 'none';
    base-uri 'self';
    form-action 'self';
" always;

Mẹo: Bắt đầu với Content-Security-Policy-Report-Only (chỉ báo cáo, không block) để tìm lỗi trước khi enforce.

Bước 3: Rate Limiting

Ngăn brute-force attack và DDoS:

# Giới hạn request rate
limit_req_zone $binary_remote_addr zone=general:10m rate=10r/s;
limit_req_zone $binary_remote_addr zone=login:10m rate=5r/m;

server {
    # Giới hạn chung: 10 requests/giây
    location / {
        limit_req zone=general burst=20 nodelay;
    }

    # Giới hạn login: 5 lần/phút
    location /wp-login.php {
        limit_req zone=login burst=3 nodelay;
        limit_req_status 429;
    }

    # Giới hạn API: 30 requests/giây
    location /api/ {
        limit_req zone=general burst=50 nodelay;
    }
}

Bước 4: Ẩn thông tin server

# Nginx: Tắt hiển thị version
server_tokens off;

# PHP: Tắt expose PHP version
# php.ini
expose_php = Off

Trước: Server: nginx/1.24.0, X-Powered-By: PHP/8.3.0

Sau: Server: nginx, không có X-Powered-By

Hacker dùng version information để tìm lỗ hổng đã biết → ẩn version giảm attack surface.

Bước 5: Bảo vệ file nhạy cảm

# Chặn truy cập file nhạy cảm
location ~ /\. {
    deny all;  # .env, .git, .htaccess
}

location ~ /(wp-config\.php|readme\.html|license\.txt) {
    deny all;
}

location ~ /xmlrpc\.php {
    deny all;  # WordPress XML-RPC (thường bị exploit)
}

# Chặn truy cập thư mục
location ~ /(uploads|includes|wp-includes)/.*\.php$ {
    deny all;  # Không cho chạy PHP trong thư mục upload
}

Bước 6: Database Security

-- Không dùng root cho ứng dụng
CREATE USER 'app_user'@'localhost' IDENTIFIED BY 'StrongPassword';
GRANT SELECT, INSERT, UPDATE, DELETE ON app_db.* TO 'app_user'@'localhost';
-- KHÔNG cấp DROP, ALTER, CREATE cho app user

-- Bind MySQL chỉ localhost
-- /etc/mysql/mysql.conf.d/mysqld.cnf
-- bind-address = 127.0.0.1

Prepared Statements (chống SQL Injection)

// Sai: Vulnerable to SQL injection
$query = "SELECT * FROM users WHERE email = '$email'";

// Đúng: Prepared statement
$stmt = $pdo->prepare("SELECT * FROM users WHERE email = :email");
$stmt->execute(['email' => $email]);

Bước 7: Input Validation & Sanitization

// Validate email
$email = filter_var($_POST['email'], FILTER_VALIDATE_EMAIL);

// Sanitize HTML (chống XSS)
$comment = htmlspecialchars($_POST['comment'], ENT_QUOTES, 'UTF-8');

// Validate phone (Việt Nam)
if (!preg_match('/^(0[3|5|7|8|9])+([0-9]{8})$/', $_POST['phone'])) {
    throw new ValidationException('Số điện thoại không hợp lệ');
}

// Validate file upload
$allowed_types = ['image/jpeg', 'image/png', 'image/webp'];
$finfo = finfo_open(FILEINFO_MIME_TYPE);
$mime = finfo_file($finfo, $_FILES['upload']['tmp_name']);
if (!in_array($mime, $allowed_types)) {
    throw new ValidationException('File type not allowed');
}
// PHP session cookie settings
ini_set('session.cookie_secure', 1);      // Chỉ gửi qua HTTPS
ini_set('session.cookie_httponly', 1);     // JavaScript không đọc được
ini_set('session.cookie_samesite', 'Lax'); // Chống CSRF
ini_set('session.use_strict_mode', 1);    // Reject uninitialized session ID
# Nginx: Set cookie flags
proxy_cookie_path / "/; HTTPOnly; Secure; SameSite=Lax";

Bước 9: CSRF Protection

// Tạo CSRF token
session_start();
$csrf_token = bin2hex(random_bytes(32));
$_SESSION['csrf_token'] = $csrf_token;

// Trong form HTML
echo '<input type="hidden" name="csrf_token" value="' . $csrf_token . '">';

// Verify khi submit
if (!hash_equals($_SESSION['csrf_token'], $_POST['csrf_token'])) {
    die('CSRF token mismatch');
}

Bước 10: File Upload Security

$upload_dir = '/var/www/uploads/';
$max_size = 5 * 1024 * 1024; // 5MB

// Kiểm tra kích thước
if ($_FILES['file']['size'] > $max_size) {
    die('File too large');
}

// Kiểm tra MIME type thực sự (không tin extension)
$finfo = finfo_open(FILEINFO_MIME_TYPE);
$mime = finfo_file($finfo, $_FILES['file']['tmp_name']);

$allowed = ['image/jpeg', 'image/png', 'image/webp', 'application/pdf'];
if (!in_array($mime, $allowed)) {
    die('File type not allowed');
}

// Đổi tên file (tránh path traversal)
$filename = bin2hex(random_bytes(16)) . '.' . pathinfo($_FILES['file']['name'], PATHINFO_EXTENSION);

// Upload vào thư mục không có execute permission
move_uploaded_file($_FILES['file']['tmp_name'], $upload_dir . $filename);

Bước 11: Password Hashing

// Sai: MD5, SHA1 — crackable trong giây
$hash = md5($password);        // KHÔNG DÙNG
$hash = sha1($password);       // KHÔNG DÙNG

// Đúng: bcrypt hoặc Argon2
$hash = password_hash($password, PASSWORD_BCRYPT, ['cost' => 12]);

// Verify
if (password_verify($input_password, $stored_hash)) {
    // Login success
}

Bước 12: Two-Factor Authentication (2FA)

Thêm 2FA cho admin accounts:

  • WordPress: Plugin “Two Factor Authentication”
  • Laravel: laravel/fortify + Google Authenticator
  • Custom: Dùng thư viện TOTP (Time-based One-Time Password)

Admin account + 2FA = giảm 99.9% rủi ro bị hack qua password.

Bước 13: Regular Updates

ComponentTần suất kiểm traTự động?
OS (Ubuntu) security patchesHàng ngàyCó (unattended-upgrades)
CMS (WordPress)Hàng tuầnMinor: có, Major: không
Plugins/packagesHàng tuầnKiểm tra, update thủ công
PHP versionMỗi 6 thángKhông (cần test)
SSL certificateTự động (Let’s Encrypt)
Dependencies (npm, composer)Hàng thángnpm audit, composer audit

Bước 14: Logging & Monitoring

# Nginx: Detailed access log
log_format security '$remote_addr - $remote_user [$time_local] '
    '"$request" $status $body_bytes_sent '
    '"$http_referer" "$http_user_agent" '
    '$request_time $upstream_response_time';

access_log /var/log/nginx/security.log security;

Log những gì cho security:

  • Failed login attempts
  • 4xx/5xx errors
  • Unusual request patterns
  • File upload attempts
  • Admin panel access
  • Database query errors

Bước 15: Web Application Firewall (WAF)

Cloudflare WAF (miễn phí — cơ bản)

Cloudflare Free Plan tự động bảo vệ khỏi:

  • SQL injection cơ bản
  • XSS attacks
  • Known exploit patterns

ModSecurity (miễn phí — nâng cao)

sudo apt install libmodsecurity3 libnginx-mod-security -y

# Thêm OWASP Core Rule Set
git clone https://github.com/coreruleset/coreruleset /etc/nginx/modsecurity/crs

Bảng tổng hợp 15 bước

#BướcThời gianĐộ khóImpact
1Security Headers10 phútDễCao
2CSP30 phútTrung bìnhCao
3Rate Limiting15 phútDễCao
4Ẩn server info5 phútDễThấp
5Bảo vệ file nhạy cảm10 phútDễCao
6Database security20 phútTrung bìnhCao
7Input validation1–4 giờTrung bìnhCao
8Secure cookies10 phútDễTrung bình
9CSRF protection30 phútTrung bìnhCao
10File upload security1 giờTrung bìnhCao
11Password hashing30 phútDễCao
122FA30 phútDễCao
13Regular updatesOngoingDễCao
14Logging30 phútTrung bìnhTrung bình
15WAF1 giờTrung bìnhCao

FAQ — Câu hỏi thường gặp

Bước nào quan trọng nhất?

Nếu chỉ làm 3 bước: (1) Security Headers + HSTS, (2) Rate Limiting cho login, (3) Regular Updates. Ba bước này ngăn được 70% attacks phổ biến.

Bảo mật website tốn bao nhiêu?

15 bước trong bài viết đều miễn phí (chỉ tốn thời gian). Nếu thuê chuyên gia hardening: 5–15 triệu VND (one-time). Ongoing security monitoring: 3–8 triệu VND/tháng. So với thiệt hại khi bị hack (50–500 triệu VND), đây là đầu tư nhỏ.

WordPress có cần tất cả 15 bước không?

Có. WordPress là CMS phổ biến nhất → cũng là target phổ biến nhất. 70% WordPress bị hack do: plugin outdated, password yếu, hoặc thiếu security headers. 15 bước này đặc biệt quan trọng cho WordPress.

Kết luận

Bảo mật website không phải là “1 lần rồi xong” — đó là quy trình liên tục. 15 bước trong bài viết này là foundation cần thiết. Bắt đầu từ bước 1 (security headers — 10 phút), và từ từ implement các bước còn lại.

Nếu bạn cần security audit hoặc dịch vụ bảo mật website chuyên nghiệp, hãy liên hệ Trinh Digital — chúng tôi audit và hardening website theo chuẩn OWASP.

#website#security#hardening#bảo mật
Chia sẻ: Z

Sẵn sàng chuyển đổi số cùng Trinh Digital?

Liên hệ ngay để nhận tư vấn miễn phí. Đội ngũ chuyên gia sẽ phân tích nhu cầu và đề xuất giải pháp tối ưu.

Zalo