Trong phần này, tôi sẽ tập trung viết về cách phân quyền thư mục web của nginx, cách xác thực cơ bản khi truy cập website và cách lọc truy cập đến website theo source IP.

Phân quyền thư mục web

Về cơ bản để website hoạt động được, tôi chỉ cần gán quyền owner đúng và gán permission là read cho owner đó là website hoạt động được. Việc gán quyền tối thiểu như vậy sẽ loại bỏ rất nhiều rủi ro không đáng có như việc up shell vẫn có thể xảy ra do code filter không đủ, bộ lọc bị lỗi logic nào đó chưa test hết… nhưng tuy nằm trên thư mục web của site mà shell đó không có quyền execute thì đâu có thể gây nguy hại cho website được.

Trước hết, quyền owner đúng là thế nào ? Trong hai phần trước, tôi đã thực hiện cấu hình vhost dùng nginx và vhost này cũng hỗ trợ chạy php nhờ vào một service php-fpm. Ở đây nginx service chạy dưới quyền của user nginx và php-fpm service chạy dưới quyền của user apache (mặc định là vậy, tôi có thể sửa lại trong config). Các file php bắt buộc cần có quyền read cho user apache để chạy được còn các file tĩnh khác như html, text/plain thì cần có quyền read cho user nginx để chạy được. Vậy tôi chỉ cần lọc ra các file php gán owner apache:apache và permission 440, các file còn lại owner nginx:nginx và permission 440 là được. Riêng với thư mục thì owner là nginx:nginx và permission là 550 (vì user nginx cần quyền execute để change dir)

Nhưng rủi ro ở đây là gì ?

Tất cả các website đang sử dụng chung owner hết. Website nào trên webserver cũng dùng apache và nginx user làm owner. Nếu user này bị nhân nhượng thì sao ? Các zero day vulnerability có thể xuất hiện bất cứ lúc nào trong các sản phẩm của nginx hay php-fpm và mức độ nguy hại sẽ bao trùm tất cả các website.

Vậy có thể làm gì ?

Tôi tạo ra cho mỗi website một user riêng. Ví dụ với site vhost.example.com thì tôi tạo ra user vhost_example_com

useradd -c “vhost.example.com user” -s /sbin/nologin -d /home/www/vhost.example.com 

Sau đó gán quyền owner và permission cho website như sau:

  • owner cho toàn bộ site
    chown -R vhost_example_com:vhost_example_com /home/www/vhost.example.com 
  • File nên gán permission 644
    find /home/www/vhost.example.com -type f -exec chmod 644 {} ; 
  • Directory nên gán permission 755
    find /home/www/vhost.example.com -type d -exec chmod 755 {} ; 

Như vậy, khi cần thiết tôi vẫn có thể dùng user vhost_example_com để sửa code của website và vẫn chỉ cho phép user nginx hay apache chỉ có quyền chỉ đọc trên site. Việc gán owner và permission cũng đơn giản hơn.

Mở rộng với thư mục upload

Nếu giờ client cần upload file lên website thì sao ? Thư mục upload thường là điểm yếu của website.

Cái này hơi khó. Tôi không chắc cách làm này tốt nhất nhưng hiện thời tôi đang gán như sau cho thư mục upload:

chown -R apache:apache /home/www/vhost.example.com/upload find /home/www/vhost.example.com/upload-type f -exec chmod 644 {} ; find /home/www/vhost.example.com/upload -type d -exec chmod 755 {} ; 

Bảo mật trong thư mục upload tôi tham khảo trong:
http://software-security.sans.org/blog/2009/12/28/8-basic-rules-to-implement-secure-file-uploads/

Hầu hết các hướng bảo mật trong tài liệu cần sự can thiệp của developer ở tầng application. Một trong các hướng bảo mật trong tài liệu là đặt authentication trên thư mục upload do vậy có thể khoanh vùng được ai là người upload các file khả nghi. Nếu thư mục upload này được dùng bởi client thì không thể giới hạn như vậy được.

Kiểm soát truy cập sử dụng basic authentication

Việc đầu tiên là tạo file htpasswd. Một cơ sở dữ liệu nhỏ để thực hiện xác thực. Tốt nhất file này không nên đặt trong thư mục chứa web hay thư mục cài đặt của nginx.

/usr/bin/htpasswd -c /usr/local/etc/.vhost.example.com.htpasswd test1 

Một file ẩn /usr/local/etc/.vhost.example.com.htpasswd được tạo ra.

Passwd của user test1 được mã hóa.
Nếu cần thêm một user, tôi dùng lệnh sau:

/usr/bin/htpasswd /usr/local/etc/.vhost.example.com.htpasswd test2 

Nếu cần xóa một user, tôi dùng lệnh sau:

/usr/bin/htpasswd -D /usr/local/etc/.vhost.example.com.htpasswd test2 

Danh sách các user trong htpasswd có thể xem trực tiếp bằng cách cat chính file htpasswd.

Tiếp đến, tôi chỉ cần thêm hai dòng vào server block trong

/usr/local/nginx/conf.d/vhost.example.com.conf

auth_basic "private site";
auth_basic_user_file /usr/local/etc/.vhost.example.com.htpasswd;

Lúc này /usr/local/nginx/conf.d/vhost.example.com.conf như sau:

server{ listen 80; server_name vhost.example.com www.vhost.example.com; root /home/www/vhost.example.com; error_log /var/log/nginx/vhost.example.com_error.log error; access_log /var/log/nginx/vhost.example.com_access.log main; auth_basic "private site"; auth_basic_user_file /usr/local/etc/.vhost.example.com.htpasswd; location /{ index index.html index.php; } location ~ .php { fastcgi_pass unix:/tmp/php_fpm.sock; fastcgi_index index.php; include /usr/local/nginx/conf/fastcgi_params; fastcgi_param SCRIPT_FILENAME $document_root/$fastcgi_script_name; } } 

Sau khi reload lại nginx service
service nginx reload

Chú ý: Nên reload service nginx để đảm bảo các connection hiện tại đến nginx không bị ngắt đột ngột.

Tôi truy cập vào http://vhost.example.com/test.php

alt text

Nhập user name test1, password của user này như trong

/usr/local/etc/.vhost.example.com.htpasswd;

để truy cập thành công.

Việc sử dụng auth_basic có thể áp dụng linh động trên từng location. Ở đây tôi áp dụng auth_basic theo nguyên tắc deny all, allow selected. Tôi mặc định deny trên toàn bộ vhost sau đó allow trong một số location. Ví dụ tôi muốn bất kỳ ai cũng truy cập được vào location public mà không có rào cản gì. Tôi làm như sau:

mkdir -p /home/www/vhost.example.com/public vi /home/www/vhost.example.com/public/test1.php <?php echo "test1.php" ?> 

Tôi thêm dòng sau vào config vhost:

 location /public/{ auth_basic off; } 

Lúc này /usr/local/nginx/conf.d/vhost.example.com.conf như sau:

server{ listen 80; server_name vhost.example.com www.vhost.example.com; root /home/www/vhost.example.com; error_log /var/log/nginx/vhost.example.com_error.log error; access_log /var/log/nginx/vhost.example.com_access.log main; auth_basic "private site"; auth_basic_user_file /usr/local/etc/.vhost.example.com.htpasswd; location /{ index index.html index.php; } location /public/{ auth_basic off; } location ~ .php { fastcgi_pass unix:/tmp/php_fpm.sock; fastcgi_index index.php; include /usr/local/nginx/conf/fastcgi_params; fastcgi_param SCRIPT_FILENAME $document_root/$fastcgi_script_name; } } 

Sau khi reload lại nginx service, tôi truy cập vào

http://vhost.example.com/public/test1.php
và không có rào cản gì.

Giới hạn truy cập dựa vào source IP

Cũng tuân theo nguyên tắc deny all, allow selected. Tôi thêm hai dòng sau vào vhost config:

allow 192.168.3.0/24;
deny all;

Như vậy mọi request từ subnet 192.168.3.0/24 sẽ được cho phép còn lại sẽ bị deny hết. Cũng như auth_basic. Viêc sử dụng các chỉ thị allow và deny cũng linh động theo từng location.

Cuối cùng, /usr/local/nginx/conf.d/vhost.example.com.conf sẽ như sau:

server{ listen 80; server_name vhost.example.com www.vhost.example.com; root /home/www/vhost.example.com; error_log /var/log/nginx/vhost.example.com_error.log error; access_log /var/log/nginx/vhost.example.com_access.log main; auth_basic "private site"; auth_basic_user_file /usr/local/etc/.vhost.example.com.htpasswd; allow 192.168.3.0/24; deny all; location /{ index index.html index.php; } location /public/{ auth_basic off; } location ~ .php { fastcgi_pass unix:/tmp/php_fpm.sock; fastcgi_index index.php; include /usr/local/nginx/conf/fastcgi_params; fastcgi_param SCRIPT_FILENAME $document_root/$fastcgi_script_name; } } 

Kết thúc phần 3. Trong phần 4, tôi sẽ trình bày cách config vhost hỗ trợ https.

Nguồn tham khảo:

http://nginx.com/resources/admin-guide/restricting-access/

Comments

comments