ป้องกันหน้า Admin ด้วย Basic Auth และ Rate Limit บน Nginx
หน้า Admin ของเว็บไซต์ เช่น /wp-login.php ของ WordPress, /admin ของระบบหลังบ้าน หรือเครื่องมืออย่าง phpMyAdmin เป็นเป้าหมายหลักของบอทที่สุ่มเดารหัสผ่านตลอดทั้งวัน แม้รหัสผ่านจะแข็งแรงพอ การโดนยิงคำขอเข้ามาจำนวนมากก็ยังกิน CPU ของ PHP-FPM และฐานข้อมูลจนเว็บไซต์ช้าลงได้
บทความนี้แสดงวิธีเพิ่มการป้องกันสองชั้นที่ระดับ Nginx ก่อนคำขอจะไปถึงแอปพลิเคชัน ชั้นแรกคือ Basic Auth ให้เบราว์เซอร์ถามชื่อผู้ใช้และรหัสผ่านอีกชุดก่อนแสดงหน้า Admin ชั้นที่สองคือ Rate Limit ด้วยโมดูล limit_req เพื่อจำกัดจำนวนคำขอต่อ IP ทั้งสองอย่างมีมากับ Nginx อยู่แล้ว ไม่ต้องติดตั้งโมดูลเพิ่ม
สิ่งที่ต้องเตรียม
- Linux Server ที่ติดตั้ง Nginx แล้ว (Ubuntu 22.04/24.04 หรือ AlmaLinux/Rocky Linux 8-9) หากยังไม่มี ดู ติดตั้ง Nginx, PHP-FPM และ MariaDB (LEMP) บน Ubuntu
- สิทธิ์ root หรือผู้ใช้ที่ใช้
sudoได้ - เว็บไซต์ควรเปิดใช้ HTTPS แล้ว เพราะ Basic Auth ส่งรหัสผ่านแบบเข้ารหัส Base64 เท่านั้น ไม่ได้เข้ารหัสจริง ดู ติดตั้ง SSL ฟรีจาก Let's Encrypt ด้วย Certbot บน Nginx
- ทราบไฟล์ Server Block ของเว็บไซต์ เช่น
/etc/nginx/sites-available/example.com(Ubuntu) หรือ/etc/nginx/conf.d/example.com.conf(AlmaLinux/Rocky)
ขั้นตอนที่ 1: ติดตั้งเครื่องมือ htpasswd
คำสั่ง htpasswd ใช้สร้างไฟล์เก็บชื่อผู้ใช้และรหัสผ่านที่เข้ารหัสแล้ว อยู่คนละแพ็กเกจในแต่ละ Distro
Ubuntu:
sudo apt update
sudo apt install apache2-utils
AlmaLinux / Rocky Linux:
sudo dnf install httpd-tools
แพ็กเกจทั้งสองติดตั้งเฉพาะเครื่องมือ ไม่ได้ติดตั้ง Apache Web Server ลงมาด้วย
ขั้นตอนที่ 2: สร้างไฟล์ผู้ใช้สำหรับ Basic Auth
สร้างผู้ใช้คนแรกด้วยตัวเลือก -c (create) ระบบจะถามรหัสผ่านสองครั้ง
sudo htpasswd -c /etc/nginx/.htpasswd adminuser
เพิ่มผู้ใช้คนถัดไป ห้ามใส่ -c อีก เพราะจะเขียนทับไฟล์เดิมและผู้ใช้เก่าหายทั้งหมด
sudo htpasswd /etc/nginx/.htpasswd seconduser
ลบผู้ใช้ด้วย -D
sudo htpasswd -D /etc/nginx/.htpasswd seconduser
ตั้งสิทธิ์ไฟล์ให้ Nginx อ่านได้แต่ผู้ใช้อื่นอ่านไม่ได้ บน Ubuntu Nginx ทำงานในกลุ่ม www-data ส่วน AlmaLinux/Rocky ใช้กลุ่ม nginx
# Ubuntu
sudo chown root:www-data /etc/nginx/.htpasswd
sudo chmod 640 /etc/nginx/.htpasswd
# AlmaLinux / Rocky Linux
sudo chown root:nginx /etc/nginx/.htpasswd
sudo chmod 640 /etc/nginx/.htpasswd
เมื่อเปิดไฟล์ดู จะเห็นบรรทัดลักษณะ adminuser:$apr1$... ซึ่งเป็นรหัสผ่านที่ผ่านการ Hash แล้ว (รูปแบบ apr1 ที่ Nginx รองรับโดยตรง) ไม่มีใครอ่านรหัสผ่านจริงจากไฟล์นี้ได้ แต่ก็ไม่ควรวางไฟล์ไว้ในโฟลเดอร์เว็บที่เปิดดาวน์โหลดได้
ขั้นตอนที่ 3: ใส่ Basic Auth ให้เส้นทาง Admin
แก้ไฟล์ Server Block ของเว็บไซต์ แล้วเพิ่ม auth_basic ไว้ใน location ที่ต้องการป้องกัน ตัวอย่างนี้ป้องกันโฟลเดอร์ /admin/ ของแอปทั่วไป
location /admin/ {
auth_basic "Restricted Area";
auth_basic_user_file /etc/nginx/.htpasswd;
try_files $uri $uri/ /index.php?$args;
}
สำหรับ WordPress ต้องระวังเป็นพิเศษ เพราะไฟล์ /wp-admin/admin-ajax.php ถูกเรียกจากหน้าเว็บฝั่งผู้เข้าชมด้วย (เช่น ฟอร์มหรือปลั๊กอินตะกร้าสินค้า) ถ้าล็อกไว้ ผู้เข้าชมทั่วไปจะเจอกล่องถามรหัสผ่าน วิธีที่ปลอดภัยกว่าคือล็อกเฉพาะ wp-login.php ซึ่งเป็นประตูเข้าสู่ระบบ
location = /wp-login.php {
auth_basic "Restricted Area";
auth_basic_user_file /etc/nginx/.htpasswd;
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/run/php/php8.3-fpm.sock;
}
สังเกตว่า location = /wp-login.php เป็นการจับคู่แบบตรงตัว Nginx จะไม่ส่งต่อไปยัง location ~ \.php$ อีก จึงต้องใส่คำสั่ง FastCGI ซ้ำไว้ในบล็อกนี้ด้วย ไม่เช่นนั้นเบราว์เซอร์จะดาวน์โหลดไฟล์ PHP แทนการรัน ปรับค่าให้ตรงกับเครื่องของคุณ
| ระบบ | บรรทัด include | fastcgi_pass |
|---|---|---|
| Ubuntu (PHP 8.3) | include snippets/fastcgi-php.conf; | unix:/run/php/php8.3-fpm.sock |
| Ubuntu 22.04 (PHP 8.1) | include snippets/fastcgi-php.conf; | unix:/run/php/php8.1-fpm.sock |
| AlmaLinux/Rocky | include fastcgi_params; และ fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; | unix:/run/php-fpm/www.sock |
ดูค่าที่ถูกต้องได้จากบล็อก location ~ \.php$ ที่มีอยู่แล้วในไฟล์เดียวกัน แล้วคัดลอกมาใช้
อนุญาต IP ของสำนักงานโดยไม่ต้องใส่รหัสผ่าน
ถ้าผู้ดูแลเข้าจาก IP คงที่ ใช้ satisfy any เพื่อให้ผ่านได้ด้วยเงื่อนไขใดเงื่อนไขหนึ่ง คือมาจาก IP ที่อนุญาต หรือใส่รหัสผ่านถูก
location /admin/ {
satisfy any;
allow 203.0.113.10;
deny all;
auth_basic "Restricted Area";
auth_basic_user_file /etc/nginx/.htpasswd;
try_files $uri $uri/ /index.php?$args;
}
ถ้าต้องการให้ต้องผ่าน ทั้งสอง เงื่อนไข ให้ใช้ satisfy all; (ค่าเริ่มต้น) ซึ่งเข้มงวดกว่าแต่เข้าจากที่อื่นไม่ได้เลย
ขั้นตอนที่ 4: ตั้งค่า Rate Limit
Rate Limit แบ่งเป็นสองส่วน ส่วนแรกคือประกาศ Zone ในบล็อก http ส่วนที่สองคือเรียกใช้ Zone ใน location ทั้ง Ubuntu และ AlmaLinux/Rocky โหลดไฟล์ใน /etc/nginx/conf.d/*.conf ภายในบล็อก http อยู่แล้ว จึงสร้างไฟล์ใหม่ได้เลย
sudo nano /etc/nginx/conf.d/ratelimit.conf
ใส่เนื้อหา
limit_req_zone $binary_remote_addr zone=login:10m rate=10r/m;
limit_req_status 429;
$binary_remote_addrนับตาม IP ของผู้เรียกzone=login:10mตั้งชื่อ Zone ว่า login และใช้หน่วยความจำ 10 MB ซึ่งเก็บสถานะได้ราว 160,000 IPrate=10r/mอนุญาต 10 คำขอต่อนาทีต่อ IP (ใช้r/sสำหรับต่อวินาที)limit_req_status 429ตอบกลับ 429 Too Many Requests แทนค่าเริ่มต้น 503 ซึ่งทำให้อ่าน Log ง่ายกว่า
จากนั้นเพิ่มบรรทัด limit_req ใน location ของหน้า Login
location = /wp-login.php {
limit_req zone=login burst=5 nodelay;
auth_basic "Restricted Area";
auth_basic_user_file /etc/nginx/.htpasswd;
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/run/php/php8.3-fpm.sock;
}
burst=5 ยอมให้มีคำขอเกินอัตราได้ชั่วคราว 5 ครั้ง เพื่อรองรับการโหลดหน้าแล้วกดส่งฟอร์มติดกัน ส่วน nodelay ทำให้คำขอที่อยู่ใน burst ถูกประมวลผลทันทีแทนการหน่วงเวลา คำขอที่เกินจากนั้นจะได้ 429 ทันที
หมายเหตุ: ถ้าเซิร์ฟเวอร์อยู่หลัง Cloudflare หรือ Load Balancer ค่า
$binary_remote_addrจะเป็น IP ของ Proxy ทำให้ผู้ใช้ทุกคนถูกนับรวมกันและโดนบล็อกพร้อมกัน ต้องตั้งset_real_ip_fromและreal_ip_headerให้ Nginx รู้ IP จริงของผู้ใช้ก่อน
ขั้นตอนที่ 5: ทดสอบไฟล์คอนฟิกและ Reload
sudo nginx -t
ผลลัพธ์ที่ถูกต้องคือ
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
จากนั้นโหลดคอนฟิกใหม่โดยไม่ตัดการเชื่อมต่อเดิม
sudo systemctl reload nginx
ถ้า nginx -t แจ้ง Error ห้าม Reload ให้แก้ตามบรรทัดที่แจ้งก่อน เพราะ Nginx จะยังทำงานด้วยคอนฟิกเดิมต่อไปได้ตามปกติ
ตรวจสอบผลลัพธ์
ทดสอบ Basic Auth ด้วย curl คำขอที่ไม่มีรหัสผ่านต้องได้ 401
curl -I https://example.com/wp-login.php
HTTP/2 401
www-authenticate: Basic realm="Restricted Area"
เมื่อส่งรหัสผ่านถูกต้อง ต้องได้ 200
curl -I -u adminuser https://example.com/wp-login.php
ทดสอบ Rate Limit ด้วยการยิงคำขอติดกัน 20 ครั้ง แล้วดูรหัสสถานะ
for i in $(seq 1 20); do curl -s -o /dev/null -w "%{http_code}\n" https://example.com/wp-login.php; done
ช่วงแรกจะเห็น 401 (เพราะไม่ได้ส่งรหัสผ่าน) จากนั้นจะเปลี่ยนเป็น 429 เมื่อเกินอัตรา ใน Error Log จะมีบรรทัดลักษณะ limiting requests, excess: 5.xxx by zone "login"
sudo tail -f /var/log/nginx/error.log
ปัญหาที่พบบ่อย
ใส่รหัสผ่านถูกแต่ยังได้ 500 Internal Server Error
มักเกิดจาก Nginx อ่านไฟล์ .htpasswd ไม่ได้ ดู Error Log จะพบ open() "/etc/nginx/.htpasswd" failed (13: Permission denied) ให้ตรวจเจ้าของกลุ่มและสิทธิ์ตามขั้นตอนที่ 2 บน AlmaLinux/Rocky หากย้ายไฟล์ไปไว้ที่อื่นนอก /etc/nginx อาจติด SELinux ด้วย ดู ใช้งาน SELinux บน AlmaLinux และ Rocky Linux โดยไม่ต้องปิด
เปิด wp-login.php แล้วเบราว์เซอร์ดาวน์โหลดไฟล์แทน
บล็อก location = ไม่มีคำสั่ง FastCGI ให้คัดลอก include และ fastcgi_pass จากบล็อก PHP เดิมมาใส่ตามขั้นตอนที่ 3
ผู้ใช้ทั่วไปโดน 429 ทั้งที่ไม่ได้ทำอะไรผิด
อาจตั้ง rate ต่ำเกินไป ใช้ Zone เดียวกับหน้าที่ถูกเรียกบ่อย หรือ IP ที่เห็นเป็นของ Proxy ให้ใช้ Rate Limit เฉพาะหน้า Login ไม่ใช่ทั้งเว็บ และตั้ง Real IP ให้ถูกต้อง
Error "zero size shared memory zone" หรือ "unknown limit_req_zone"
เรียกใช้ limit_req zone=login ก่อนประกาศ Zone หรือประกาศ limit_req_zone ผิดที่ (ต้องอยู่ในบล็อก http ไม่ใช่ใน server) ตรวจว่าไฟล์ใน conf.d ถูกโหลดจริงด้วย sudo nginx -T | grep limit_req_zone
Basic Auth และ Rate Limit เป็นชั้นป้องกันเสริม ไม่ได้แทนที่รหัสผ่านที่แข็งแรงและการยืนยันตัวตนสองขั้นตอน สำหรับ WordPress ดูเพิ่มเติมที่ เปิด Two-Factor Authentication สำหรับหน้า Login WordPress และ เช็กลิสต์ความปลอดภัยเว็บไซต์ WordPress หากติดปัญหาในการตั้งค่า ติดต่อทีมงาน THAI DATA CLOUD ได้ที่ https://thaidata.cloud/contact/
- Categories:
- Cloud
- Tags:
- Cloud
- Cloud Server
Related Posts
หมวดหมู่ที่น่าสนใจ
- Account Settings
- AD Server
- AI
- Alibaba Cloud
- Anti-Spam Gateway
- AWS Amazon Web Services
- Campaign
- CentOS/AlmaLinux
- Cloud
- Cloud Backup
- Cloud Communication
- Cloud Migration
- Cloud Security
- Cloud Server Management
- Cloud Solution
- Cloud Solution for Government
- Cloud Solutions by Industry
- Cloud Storage
- Cloud VPS App Plus +
- Cloud VPS DirectAdmin
- Cloud VPS Plesk
- CSR
- Cyber Security
- Cybersecurity
- Data Sovereignty
- Database Server
- DDoS
- Digital Tranformation
- Digital Transformation
- Direct Mail
- Directadmin
- Domainname
- Ecommerce
- ERP
- Generative AI
- Getting Started
- Google Cloud
- Google G Suite
- Huawei Cloud
- IT News
- Linux Server
- Managed Cloud Services
- Managed Service Provider
- Manual
- Microsoft
- Microsoft 365
- Microsoft Azure
- News
- On-premise
- Private Mail Server
- Promotion
- Recommend Solution (Enterprise)
- Server
- Sovereign Cloud
- THAI DATA CLOUD Platform
- Ubuntu
- Ubuntu
- Uncategorized
- VMware
- VPS Server
- Web Design
- Web Hosting
- Web Hosting (DirectAdmin)
- Web Hosting (Plesk)
- Web Technologies
- Windows Server
- Wordpress
- Zimbra
- เรื่องราวความประทับใจ
- โซลูชันสำหรับธุรกิจการผลิตและยานยนต์
- โซลูชันสำหรับธุรกิจการศึกษา
- โซลูชันสำหรับธุรกิจการเงิน
- โซลูชันสำหรับธุรกิจขนส่งและกระจายสินค้า
- โซลูชันสำหรับธุรกิจค้าปลีก
- โซลูชันสำหรับธุรกิจท่องเที่ยว
- โซลูชันสำหรับธุรกิจบริการสุขภาพและโรงพยาบาล
- โซลูชันสำหรับธุรกิจประกันภัย
- โซลูชันสำหรับธุรกิจพลังงานและสาธารณูปโภค
- โซลูชันสำหรับธุรกิจสื่อสารมวลชนและเอ็นเตอร์เทนเมนท์
- โซลูชันสำหรับธุรกิจอสังหาริมทรัพย์
- โซลูชันสำหรับธุรกิจเทคโนโลยี


