Bỏ qua tới nội dung
Máy chủ & .htaccess

Những quy tắc bảo mật .htaccess cho WordPress thực sự có tác dụng

Phần lớn thứ được rao bán dưới tên bảo mật .htaccess cho WordPress chỉ là chắp vá. Những quy tắc thực sự thay đổi bề mặt tấn công của bạn lại rất ngắn — đây là cách phân biệt cái nào ra cái nào

Đã đăng

Phần lớn thứ được rao bán dưới tên “bảo mật .htaccess cho WordPress” chỉ là chắp vá. Những quy tắc thực sự thay đổi bề mặt tấn công của bạn lại rất ngắn: chặn thực thi PHP bên trong wp-content/uploads, cấm truy cập trực tiếp vào wp-config.php, và tắt liệt kê thư mục (directory indexing). Những cái phổ biến — chặn xmlrpc.php “để ngăn brute force”, giấu phiên bản WordPress, dán vào một danh sách 200 dòng user-agent của bot xấu — dao động từ ít tác dụng đến hoàn toàn là màn trình diễn. Dưới đây là cách phân biệt cái nào ra cái nào, và tại sao.

Có một điều cần làm rõ trước: .htaccess chỉ có tác dụng trên Apache (và LiteSpeed, vốn cũng đọc nó). Trên nginx thì nó bị bỏ qua hoàn toàn — file cứ nằm đó trong khi bạn tưởng mình đã được bảo vệ. Nếu hosting của bạn chạy nginx, thì không có điều nào ở đây áp dụng cả, và bạn cần dùng các khối server/location thay thế. Hãy kiểm tra bằng curl -I https://yoursite.com và xem header Server: trước khi bỏ ra cả tiếng đồng hồ chỉnh sửa một file mà server không bao giờ đọc tới.

Quy tắc thực sự quan trọng: không cho PHP chạy trong /uploads

Đây là quy tắc đáng làm. wp-content/uploads vốn được thiết kế để cả thế giới ghi vào được — mọi lần up media, mọi plugin lưu file đều ghi vào đó. Nếu kẻ tấn công đưa được một file .php vào thư mục này (thông qua một trình xử lý upload có lỗ hổng, một trường ảnh không kiểm tra kiểu MIME, hay một plugin bị xâm nhập), thì khoảng cách giữa một phiền toái nhỏ và một vụ xâm nhập thực thi mã từ xa (remote-code-execution) hoàn chỉnh nằm ở chỗ server có thực thi file đó khi được yêu cầu hay không. Cấm thực thi thì payload được up lên chỉ còn là một file trơ nằm trên ổ đĩa.

Bỏ đoạn này vào wp-content/uploads/.htaccess (tạo file nếu chưa có):

<FilesMatch "\.php$">
    Require all denied
</FilesMatch>

Đó là cú pháp của Apache 2.4. Trên bản cũ 2.2, cú pháp tương đương là Order Deny,Allow / Deny from all. Trộn lẫn hai phương ngữ này trong cùng một file là nguyên nhân phổ biến nhất gây ra 500 Internal Server Error đột ngột sau khi “hardening” — nếu cả trang chết ngay khi bạn lưu, thì gần như chắc chắn đó là lý do. Kiểm tra phiên bản của bạn bằng apachectl -v.

Bảo vệ wp-config.php

wp-config.php chứa thông tin đăng nhập cơ sở dữ liệu và các auth salt của bạn. Khi PHP đang chạy, một request trực tiếp tới nó sẽ trả về trang trắng — PHP thực thi file thay vì in nó ra. Rủi ro nằm ở trường hợp hỏng hóc: nếu PHP có lúc nào đó bị crash, bị cấu hình sai trong quá trình chuyển đổi (migration), hoặc trình xử lý bị vô hiệu hóa, thì Apache sẽ phục vụ file dưới dạng văn bản thuần và tuồn mật khẩu database của bạn cho bất kỳ ai hỏi. Cấm truy cập là khoản bảo hiểm rẻ tiền cho một khoảnh khắc tồi tệ:

<Files wp-config.php>
    Require all denied
</Files>

Tắt liệt kê thư mục

Nếu ai đó truy cập vào một thư mục không có index.php và Apache bật Options +Indexes, nó sẽ liệt kê nội dung — mọi file backup, mọi bản dump SQL lạc lối mà bạn quên bẵng. Hãy tắt nó trên toàn site:

Options -Indexes

Mức độ nghiêm trọng thấp, nhưng có thật, và chẳng tốn gì cả.

Đó là danh sách cốt lõi trung thực. Bạn có thể lắp ráp những quy tắc này — cộng với đúng cú pháp 2.4 so với 2.2 để không làm site bị 500 — bằng công cụ tạo .htaccess cho WordPress thay vì copy-paste từ một bài đăng trên diễn đàn viết cho phiên bản Apache sai.

Những gì KHÔNG nên làm

Chặn xmlrpc.php “để chống brute-force”. Đây là điều lớn nhất ai cũng lặp lại và nó sai như cách người ta phát biểu. Đúng là phương thức system.multicall của XML-RPC trong quá khứ cho phép kẻ tấn công gom nhiều lần đoán mật khẩu vào một request duy nhất — khuếch đại thật sự. Nhưng vector brute-force phổ biến áp đảo là các request POST thông thường tới wp-login.php, và chặn xmlrpc.php chẳng làm được gì cho chuyện đó cả. Brute force bị đánh bại bởi giới hạn tần suất (rate limiting), mật khẩu mạnh và 2FA — chứ không phải bằng việc giết một endpoint. một lý do chính đáng để tắt XML-RPC: tính năng pingback của nó có thể bị lạm dụng cho DDoS reflection, nên nếu bạn không dùng Jetpack, ứng dụng di động, hay pingback, thì đóng nó lại sẽ thu nhỏ bề mặt tấn công. Chỉ là đừng tự nhủ rằng đó là hàng phòng thủ brute-force của mình, vì không phải vậy.

Giấu phiên bản WordPress / gỡ thẻ generator. Xóa readme.html và thẻ <meta name="generator"> khiến cảm giác như đang hardening. Kẻ tấn công nhận diện phiên bản của bạn từ các chuỗi query trên asset được enqueue, markup của block-editor, và cả tá dấu vết khác chỉ trong vài giây. Bạn chẳng giấu được gì; bạn chỉ đang làm mình cảm thấy bận rộn.

Danh sách chặn khổng lồ user-agent và referrer của bot xấu. User agent chỉ là một header HTTP đơn có thể giả mạo. Những danh sách này lỗi thời ngay ngày bạn dán vào, chúng chẳng chặn được kẻ nào có nghề, và Apache phải đánh giá từng regex trên mọi request — bạn đang trả một khoản thuế hiệu năng thật sự để đổi lấy con số không về bảo mật. Bỏ qua đi.

Khóa wp-login.php theo IP. Tuyệt vời cho đến khi nhà mạng (ISP) đổi địa chỉ của bạn và bạn tự khóa mình khỏi admin của chính mình. Chỉ khả thi với một IP tĩnh thực sự.

Chuyển hướng liệt kê tác giả (?author=1). Đoạn rewrite .htaccess mà người ta dán vào cho việc này tự nó là chưa đủ — endpoint REST /wp-json/wp/v2/users vẫn liệt kê tên người dùng. Chặn một đường dẫn trong khi đường kia vẫn mở là màn trình diễn.

Vẫn bí?

Nếu một quy tắc làm site bị 500, thì đó là lỗi cú pháp — gỡ khối cuối cùng bạn vừa thêm và tải lại; việc đó khoanh vùng nó ngay lập tức. Nếu một quy tắc có vẻ chẳng làm gì, hãy xác nhận rằng bạn thực sự đang chạy trên Apache và rằng AllowOverride được bật cho thư mục đó (nhiều hosting managed hạn chế nó). Hãy dựng file từ một mẫu đã biết là tốt bằng công cụ tạo .htaccess, giữ lại ba quy tắc thực sự quan trọng, và bỏ phần còn lại.