PHP-FPM Security là việc cấu hình FastCGI Process Manager để PHP chạy với đúng user/group, đúng pool, đúng socket/network boundary, environment tối thiểu và giới hạn resource phù hợp với từng application. Mục tiêu không phải “khóa PHP càng nhiều càng tốt”, mà là giảm blast radius khi một ứng dụng hoặc plugin bị compromise và tránh để một pool PHP có thể đọc/ghi tài nguyên của ứng dụng khác ngoài nhu cầu.
PHP manual cho biết FPM hỗ trợ pool configuration riêng, clear_env mặc định bật để loại bỏ environment variable ngoài những biến được khai báo, và security.limit_extensions dùng để giới hạn extension script FPM cho phép xử lý. Manual cũng cảnh báo không nên bind FPM ra địa chỉ có thể truy cập toàn Internet vì FastCGI có thể truyền các PHP configuration option. Vì vậy FPM hardening phải bắt đầu từ process identity + listen boundary + filesystem permission.
Pool identity và listen boundary
1. Hiểu mô hình master process và pool
PHP-FPM có master process quản nhiều worker pool. Mỗi pool có thể có user, group, socket, process manager và PHP settings riêng. Đây là boundary hữu ích khi host nhiều application.
Trước khi sửa, inventory:
php-fpm -tt 2>&1
ps -eo user,group,pid,cmd | grep '[p]hp-fpm'
ss -lntup | grep php
ss -lxnp | grep php
Tên binary/service thay đổi theo distro và PHP version, ví dụ php8.3-fpm. Dùng command phù hợp với package thực tế.
2. Tách pool theo application khi cần isolation
Nếu nhiều website chạy chung một pool/user như www-data, compromise một app có thể đọc/ghi file app khác nếu filesystem permission cho phép. Pool riêng cho từng application giúp gắn user/group, socket, log và resource limit riêng.
Pool isolation không phải container/sandbox hoàn chỉnh. Kernel, filesystem mount, root process và shared services vẫn là trust boundary chung.
3. Chạy worker bằng user/group tối thiểu
Trong pool:
[example]
user = app_example
group = app_example
User này chỉ nên có quyền đọc code và ghi các path ứng dụng thực sự cần như uploads/cache/session tùy framework. Không cho PHP worker sở hữu toàn bộ server config hoặc shared deployment tree nếu không cần.
Xem File Permission & Ownership trên Web Server để thiết kế owner/write path.
4. Unix socket hay TCP socket?
Nếu web server và FPM cùng host, Unix socket thường giúp boundary dễ quản lý bằng filesystem owner/mode. Ví dụ:
listen = /run/php/example.sock
listen.owner = www-data
listen.group = www-data
listen.mode = 0660
Đây chỉ là ví dụ; web server user/group và distro layout có thể khác. Socket mode phải đủ cho Nginx/Apache kết nối nhưng không mở rộng cho local user không cần thiết.
5. Nếu dùng TCP, tuyệt đối kiểm soát exposure
FPM có thể listen trên IP:port. PHP manual cảnh báo FPM không nên bind vào địa chỉ worldwide-accessible. Nếu dùng TCP, ưu tiên loopback/private network theo architecture, kết hợp host/cloud firewall.
listen.allowed_clients có thể giới hạn FastCGI client khi dùng TCP; nó không áp dụng theo cùng cách cho Unix socket. Không xem đây là thay thế firewall/network segmentation.
Environment, script execution và PHP policy
6. clear_env giúp giảm rò rỉ environment
PHP-FPM manual ghi clear_env mặc định là yes: environment worker được làm sạch trước khi các biến được khai báo cho pool được thêm vào. Đây là behavior an toàn cho nhiều deployment.
Nếu ứng dụng phụ thuộc environment variable, khai báo rõ hoặc dùng secret injection mechanism phù hợp thay vì clear_env = no một cách rộng rãi.
7. security.limit_extensions
Directive này giới hạn extension script FPM cho phép parse. Manual nêu đây là cách giảm rủi ro web-server misconfiguration làm PHP thực thi file extension ngoài intended set.
Ví dụ định hướng:
security.limit_extensions = .php
Nhưng một số ứng dụng có use case khác. Trước change cần inventory endpoint/file type và test.
8. PHP settings nên khóa ở đâu?
FPM pool hỗ trợ php_admin_value và php_admin_flag; những giá trị dạng admin không thể bị override bằng ini_set(). Điều này hữu ích với baseline như error logging hoặc resource setting cần enforce ở pool.
Không dùng disable_functions như sandbox chính. Application compromise có thể có nhiều đường khác để đọc/ghi dữ liệu hoặc gọi service; least privilege ở OS/pool/filesystem mạnh hơn.
9. open_basedir có phải security boundary?
open_basedir có thể giảm một số file access của PHP và hữu ích defense-in-depth, nhưng không nên xem nó là isolation boundary tương đương container hoặc Unix permission. Nó cũng có compatibility/performance implications tùy app.
Thiết kế chính vẫn phải là pool user riêng và filesystem permission đúng.
Resource, observability và web-server integration
10. Process manager và resource limit
FPM hỗ trợ pm modes và các giá trị như child count/request lifecycle. Số worker quá cao có thể cạn RAM; quá thấp gây queue và timeout. Không có con số “hardening chuẩn” cho mọi server.
Baseline nên đo memory mỗi worker, request concurrency, upstream timeout và headroom của host trước khi đặt limit.
11. pm.max_requests hữu ích khi nào?
Giới hạn số request một child xử lý trước khi respawn có thể giúp giảm ảnh hưởng memory leak của extension/application, nhưng không sửa nguyên nhân gốc. Giá trị cần dựa vào workload và monitoring.
12. Slowlog và request timeout
FPM có slowlog/request-slowlog và timeout-related settings có thể hỗ trợ troubleshooting. Nhưng timeout quá thấp có thể kill request hợp lệ như import/report; quá cao có thể giữ worker lâu khi backend/app treo.
Log phải tránh lộ secret/request data nhạy cảm và cần rotation/retention.
13. Status endpoint phải được bảo vệ
Nếu bật FPM status/ping endpoint, chỉ expose cho monitoring path được kiểm soát. Không public status page ra Internet vì nó có thể tiết lộ operational information và tạo endpoint không cần thiết.
14. Nginx/Apache phải forward đúng script
PHP-FPM chỉ là runtime; web server quyết định request nào được chuyển vào FastCGI. Nginx/Apache mapping sai có thể khiến file không intended bị parse hoặc path resolution sai.
Với Nginx, xem Nginx Security Hardening. Nếu Apache dùng mod_proxy_fcgi, kiểm tra mapping theo vhost/app.
15. Không để FPM socket/port thành public service
Attack surface inventory phải xác minh:
sudo ss -lntup
sudo ss -lxnp
Nếu thấy FPM TCP listen trên 0.0.0.0 hoặc public interface mà architecture không yêu cầu, đây là finding ưu tiên cao. Xác minh dependency trước khi đổi listen.
16. Permission config và secret
Pool config có thể chứa path/log/environment setting nhạy cảm; chỉ admin cần quyền ghi. Secret không nên nằm trong public web root. Web worker chỉ nên đọc đúng secret cần cho application.
17. Validation trước reload/restart
Tùy distro/version, dùng config test tương ứng, ví dụ:
php-fpm -tt
Sau PASS, reload/restart theo service manager. Rồi kiểm tra:
- FPM service active.
- Pool worker chạy đúng user/group.
- Socket/port đúng.
- Nginx/Apache kết nối được.
- Website/API trả đúng.
- Upload/cache/session/cron vẫn hoạt động.
- Không có error/slowlog bất thường mới.
Mẫu PHP-FPM Pool Review Record
Đây là template evidence, không phải FPM state thực tế của SecureWebOps.
| Control | Observed | Expected | Evidence | Result |
|---|---|---|---|---|
| Pool identity | User/group | App-scoped | Config + process list | PASS/FAIL |
| Listen | Unix/TCP | Local/private intended | Socket + config | PASS/FAIL |
| Environment | clear_env/settings | Minimal required vars | Pool config | PASS/FAIL |
| Extensions | Allowed extensions | App requirement only | security.limit_extensions | PASS/FAIL |
| Resource | pm/limits | Measured capacity | Metrics + config | PASS/FAIL |
Checklist PHP-FPM Security
- Package/version còn support.
- Pool inventory đầy đủ.
- Mỗi app có identity phù hợp; tách pool khi cần.
- Worker không chạy root.
- Unix socket owner/group/mode tối thiểu.
- TCP listen không public ngoài intended architecture.
listen.allowed_clientsreview nếu dùng TCP.clear_envkhông bị tắt rộng rãi thiếu lý do.security.limit_extensionsphản ánh file type intended.- php_admin settings dùng cho baseline cần enforce.
- Không xem disable_functions/open_basedir là sandbox chính.
- pm/resource limit dựa trên capacity.
- Status endpoint chỉ cho monitoring source.
- Web server FastCGI mapping đúng.
- Config/secret permission đúng.
- Config test PASS trước reload/restart.
- Functional test app sau change.
Nguồn kỹ thuật tham khảo
Kết luận
PHP-FPM hardening hiệu quả nhất khi pool identity, filesystem permission và listen boundary được thiết kế cùng nhau. Một pool chạy đúng user nhưng có socket public hoặc source tree world-writable vẫn chưa an toàn.
Workflow nên là pool-by-purpose → least-privilege user → local/private listen → minimal environment → measured resource limits → config test → functional read-back. FPM là một phần của web stack, nên mọi thay đổi phải được kiểm chứng cùng Nginx/Apache và application.
