# Cách ly tài khoản hosting là gì: để một website bị hack không lây sang website khác

> Cách ly tài khoản hosting hoạt động thế nào: user Linux, PHP-FPM riêng, open_basedir, CageFS, cgroups và cách tự kiểm tra trên server của bạn.

Source: https://zopanel.net/vi/blog/shared-hosting-account-isolation  
Updated: 2026-10-21

## Ý chính

- Cách ly tài khoản nghĩa là mỗi tài khoản hosting chạy bằng user Linux riêng, có tiến trình PHP, giới hạn tài nguyên và file riêng.
- Khi mọi website chạy chung một user web, một webshell có thể đọc mọi file wp-config.php trên server.
- open_basedir chỉ là lưới an toàn phụ; namespace hệ thống file, CageFS và cgroups mới là lớp bảo vệ chính.
- Hãy tự kiểm tra: từ một tài khoản, thử đọc file và xem tiến trình của tài khoản khác.

Cách ly tài khoản hosting nghĩa là mỗi tài khoản trên server chạy bằng một user Linux riêng, có tiến trình PHP riêng, giới hạn CPU và RAM riêng, và không thể đọc file hay nhìn thấy tiến trình của tài khoản khác. Làm đúng, một website WordPress bị hack chỉ gây hại trong tài khoản của chính nó: kẻ tấn công lấy được file và database của tài khoản đó, không phải năm mươi website còn lại trên server. Làm sai, một webshell đọc được mọi file `wp-config.php` trên máy và mã độc lan ra từ đó.

Bài này giải thích các lớp tạo nên cách ly, lớp nào chắc chắn, lớp nào chỉ là lưới an toàn phụ, ZoPanel làm thế nào, và cách bạn tự kiểm tra cách ly trên server của mình trong năm phút.

## Vì sao một website bị hack lại lây sang các website khác?

Kịch bản quen thuộc trên shared hosting: mọi website chạy PHP bằng cùng một user, thường là `www-data` hoặc `nobody`. Một khách hàng dùng plugin cũ, kẻ tấn công tải lên webshell, và webshell chạy với đúng quyền của PHP mọi website khác. Từ đó nó có thể:

- đọc file cấu hình của các website khác, kể cả mật khẩu database;
- ghi vào thư mục website khác và cài thêm backdoor;
- xem tiến trình của khách hàng khác cùng dòng lệnh đã khởi chạy chúng;
- chiếm hết CPU, RAM hoặc ổ đĩa, làm sập mọi website;
- gửi spam từ server cho đến khi IP bị đưa vào blacklist.

Không cần lỗ hổng kernel nào cả. Chỉ cần các website dùng chung một danh tính. Cách ly loại bỏ danh tính chung đó, từng lớp một.

## Các lớp cách ly tài khoản hosting

| Lớp | Ngăn chặn điều gì | Công cụ thường dùng |
| --- | --- | --- |
| Mỗi tài khoản một user Linux | Đọc, ghi file của tài khoản khác | `useradd`, quyền thư mục home |
| Tiến trình PHP theo user | Webshell chạy với quyền của website khác | Pool PHP-FPM với `user =`, suEXEC, LSAPI |
| Giới hạn hệ thống file | PHP mở file ngoài tài khoản | `open_basedir`, mount namespace, CageFS |
| Ẩn tiến trình | Nhìn thấy tiến trình và tham số của khách khác | Mount `/proc` với `hidepid` |
| Giới hạn tài nguyên | Một website làm nghẽn cả server | cgroups (systemd slice), CloudLinux LVE |
| Hạn mức ổ đĩa | Một tài khoản làm đầy ổ đĩa | Disk quota của Linux |
| Tách dịch vụ | Dùng chung user database, gửi mail không giới hạn | Mỗi database một user, giới hạn gửi mail theo tài khoản |

Mỗi lớp vá một lỗ hổng mà các lớp khác để lại. Hai lớp đầu là nền móng: thiếu chúng, các lớp còn lại chỉ để trang trí.

## Lớp 1: mỗi tài khoản một user Linux

Mỗi tài khoản hosting nên là một user Linux riêng, sở hữu thư mục home của mình. Thư mục home không được để user khác đọc (quyền `0750` hoặc `0711` thay vì `0755` như mặc định của nhiều bản phân phối), và file cấu hình chứa mật khẩu chỉ chủ sở hữu được đọc.

Cẩn thận với symlink. Khách hàng có thể tạo một liên kết trong thư mục của mình trỏ tới file của khách khác. Nếu web server hoặc công cụ chạy bằng root đi theo liên kết đó, ranh giới coi như mất. nginx có thiết lập `disable_symlinks if_not_owner` dành cho đúng trường hợp này, và công cụ chạy bằng root khi chạm vào thư mục khách (backup, sửa quyền) không được đi theo liên kết.

## Lớp 2: pool PHP-FPM riêng cho từng user

PHP là nơi code của khách chạy, nên PHP phải chạy bằng user của khách. Với PHP-FPM, nghĩa là mỗi tài khoản một pool, mỗi pool có `user` và `group` riêng:

```ini
[alice]
user = alice
group = alice
listen = /run/php/alice.sock
listen.owner = www-data
listen.mode = 0660
```

Lúc này webshell trên website của Alice chạy bằng `alice`, và kernel từ chối cho nó đọc file của Bob. Đây là bước quan trọng nhất, và cũng là lý do `mod_php` (PHP chạy trong tiến trình Apache, bằng user của Apache) không hợp với shared hosting.

Một chi tiết hay bị bỏ qua: các pool dùng chung một tiến trình master PHP-FPM cũng dùng chung một OPcache. PHP có tuỳ chọn [`opcache.validate_permission`](https://www.php.net/manual/en/opcache.configuration.php), mặc định tắt, để kiểm tra quyền của file đã cache theo user hiện tại. Cách chắc hơn là chạy một master FPM riêng cho mỗi tài khoản, để không có gì dùng chung.

## Lớp 3: open_basedir là lưới an toàn, không phải bức tường

`open_basedir` bảo PHP từ chối truy cập file ngoài danh sách thư mục cho phép. Thiết lập này rất phổ biến và chặn được việc dòm ngó thông thường. Nhưng [tài liệu PHP](https://www.php.net/manual/en/ini.core.php#ini.open-basedir) nói thẳng về giới hạn của nó:

- open_basedir "chỉ là một lưới an toàn bổ sung, hoàn toàn không đầy đủ, và vì vậy không thể dựa vào khi cần bảo mật".
- Nó không giới hạn truy cập Unix socket, nên PHP vẫn kết nối được tới socket như `/var/run/docker.sock`.
- Bật nó sẽ tắt realpath cache của PHP, làm chậm các ứng dụng đọc nhiều file như WordPress.

Ngoài ra, `open_basedir` chỉ ràng buộc PHP. Khách có shell, cron job hay ứng dụng Node.js hoàn toàn không bị nó ảnh hưởng. Hãy coi nó là lớp phụ bên trên PHP theo user, không bao giờ là bản thân sự cách ly.

## Lớp 4: CageFS và namespace hệ thống file

Cách mạnh hơn là thay đổi những gì tiến trình nhìn thấy, ngay ở cấp kernel, thay vì nhờ PHP kiểm tra đường dẫn.

**CageFS**, một phần của hệ điều hành thương mại CloudLinux OS, là ví dụ nổi tiếng nhất. [Tài liệu của CloudLinux](https://docs.cloudlinux.com/cloudlinuxos/cloudlinux_os_components/) mô tả nó là hệ thống file ảo cùng bộ công cụ để nhốt mỗi user vào "cái lồng" riêng. User chỉ có các chương trình an toàn, không nhìn thấy user khác và tên của họ, không đọc được file cấu hình server, và chỉ thấy một phần `/proc`. CageFS bao phủ PHP chạy qua các handler Apache phổ biến và LiteSpeed, cùng SSH và cron job. Đây là giải pháp trưởng thành, dùng rộng rãi trên server cPanel, DirectAdmin và Plesk.

**Sandbox của systemd** đạt kết quả tương tự bằng tính năng chuẩn của Linux. Một dịch vụ có thể khởi động với `ProtectHome=tmpfs`, theo [tài liệu systemd.exec](https://www.freedesktop.org/software/systemd/man/latest/systemd.exec.html) là gắn hệ thống file trống, chỉ đọc lên các thư mục home, rồi `BindPaths=` để chỉ hiện lại đúng một thư mục home. Với tiến trình đó, các home khác đơn giản là không tồn tại, nên mẹo đường dẫn hay symlink không có gì để với tới.

## Lớp 5: ẩn tiến trình

Mặc định, mọi user Linux đều liệt kê được toàn bộ tiến trình trên server, kèm dòng lệnh đầy đủ. Điều đó có thể làm lộ mật khẩu database truyền qua tham số, đường dẫn file và tên user của khách khác. Mount `/proc` với `hidepid=invisible` (hoặc `2`) sẽ khắc phục: theo [tài liệu kernel](https://docs.kernel.org/filesystems/proc.html), user không truy cập được thư mục `/proc/<pid>/` của người khác, và các thư mục đó cũng biến mất khỏi tầm nhìn.

## Lớp 6: giới hạn tài nguyên bằng cgroups

Cách ly còn là công bằng. Một website bị hack chạy trình đào coin, hoặc một website hợp lệ bị tăng truy cập đột ngột, chỉ nên làm chậm tài khoản của nó, không phải cả server. Control group của Linux thực thi điều này. Với systemd, mỗi tài khoản có một slice, và mọi tiến trình của tài khoản (PHP, cron, phiên SSH, ứng dụng) chạy trong đó với các giới hạn như `CPUQuota=`, `MemoryMax=`, `TasksMax=` và trần I/O, mô tả trong [systemd.resource-control](https://www.freedesktop.org/software/systemd/man/latest/systemd.resource-control.html). Khi một tài khoản vượt giới hạn RAM, kernel chỉ dừng một tiến trình bên trong tài khoản đó.

LVE của CloudLinux làm cùng việc này trên server CloudLinux. Chúng tôi so sánh hai cách và hướng dẫn chọn mức giới hạn trong bài [giới hạn tài nguyên tài khoản hosting](/vi/blog/hosting-resource-limits).

## ZoPanel cách ly tài khoản thế nào?

ZoPanel được làm cho shared hosting trên Ubuntu và Debian, nên dùng tính năng chuẩn của Linux thay vì kernel riêng. Theo [tài liệu bảo mật](/vi/docs/security) và [gói và giới hạn](/vi/docs/packages-limits):

- **Mỗi tài khoản hosting là một user Linux riêng**, khách không đọc được thư mục `domains` của nhau. nginx từ chối đi theo symlink trỏ tới file của người khác, và SFTP bị chroot trong thư mục home.
- **Mỗi tài khoản có pool PHP-FPM riêng cho từng phiên bản PHP**, chạy bằng user của tài khoản, có áp `disable_functions`. PHP của mỗi tài khoản chạy dưới master FPM riêng, trong sandbox systemd mà ở đó thư mục home của tài khoản khác bị ẩn, còn các đường dẫn hệ thống như `/etc/nginx`, `/etc/ssh` và thư mục database không thể truy cập.
- **Mỗi tài khoản chạy trong systemd slice riêng** (`zp-<user>.slice`) với giới hạn CPU, RAM, số tiến trình và tốc độ ổ đĩa theo gói. Pool PHP, ứng dụng, cron job và phiên terminal hoặc SSH đều chạy trong slice đó. Lệnh cron được chạy bằng chính tài khoản, bên trong slice.
- **Disk quota** được bộ cài bật sẵn, áp dụng dung lượng và số file theo gói.
- **Tiến trình được ẩn** giữa các tài khoản bằng `hidepid`.
- **Terminal trên trình duyệt và shell SSH** bị giới hạn trong thư mục home, phần còn lại của hệ thống chỉ đọc.
- **Giới hạn gửi mail** được áp ở một cổng mà tài khoản không vượt qua được, nên website bị hack không làm IP server bị blacklist.
- **Root không thao tác file của khách bằng quyền root**: thao tác file trong thư mục home chạy bằng chính tài khoản đó, nên symlink do khách cài không thể dẫn root sang file của người khác.

Bạn xem slice của một tài khoản trên server bằng:

```bash
systemctl status zp-alice.slice
systemd-cgls -u zp-alice.slice          # mọi tiến trình của tài khoản
```

Điểm ZoPanel còn yếu hơn: ZoPanel không ảo hoá `/etc/passwd` như CageFS, nên một tài khoản vẫn thấy danh sách tên user hệ thống. ZoPanel không chạy trên CloudLinux, nên nếu mô hình kinh doanh của bạn phụ thuộc vào CageFS và LVE, bộ CloudLinux với cPanel hoặc DirectAdmin là lựa chọn đã được kiểm chứng lâu năm. Và không lớp cách ly nào bảo vệ được những gì chính tài khoản đó đang giữ: website bị hack thì file, database và hộp thư của tài khoản đó bị lộ, nên [backup](/vi/docs/backups) và [dọn mã độc](/vi/blog/wordpress-malware-cleanup) vẫn cần thiết. Lý do thiết kế phía panel có trong bài [vì sao control panel không nên chạy bằng root](/vi/blog/secure-hosting-panel-architecture).

## Cách tự kiểm tra cách ly trên server của bạn

Dù dùng panel nào, hãy tự kiểm tra. Tạo hai tài khoản thử, `testa` và `testb`, rồi đặt một file PHP vào website của `testa`:

```php
<?php
// isolation-test.php: xoá sau khi kiểm tra
echo "Running as: " . posix_getpwuid(posix_geteuid())['name'] . "\n";
foreach (['/home/testb', '/etc/nginx', '/root'] as $d) {
    echo $d . ': ' . (@scandir($d) ? 'READABLE' : 'blocked') . "\n";
}
echo '/etc/shadow: ' . (@file_get_contents('/etc/shadow') !== false ? 'READABLE' : 'blocked') . "\n";
echo 'Processes visible: ' . count(glob('/proc/[0-9]*')) . "\n";
```

Mở file trên trình duyệt. Kết quả mong muốn: user là `testa`, mọi đường dẫn đều `blocked`, và chỉ thấy vài tiến trình. Sau đó thử lại từ shell bằng `testa`:

```bash
sudo -u testa ls /home/testb          # mong đợi: Permission denied hoặc No such file
sudo -u testa ps aux | wc -l          # mong đợi: chỉ tiến trình của testa
sudo -u testa cat /home/testb/domains/*/public_html/wp-config.php
```

Nếu bất kỳ lệnh nào thành công, website bị hack tiếp theo trên server đó sẽ không chịu ở yên một mình. Nhớ xoá file thử sau khi xong.

## Kết luận

Cách ly tài khoản hosting tốt là cả một chồng lớp: mỗi tài khoản một user Linux, PHP chạy bằng user đó, hệ thống file che mọi thứ khác, tiến trình bị ẩn, giới hạn cgroups và disk quota. `open_basedir` giúp ở phần rìa nhưng không gánh nổi một mình. Dù dùng panel nào, hãy tự kiểm tra ranh giới thay vì tin vào danh sách tính năng.

Nếu bạn muốn các lớp này được dựng sẵn trên Ubuntu hoặc Debian, [gói Free của ZoPanel](/vi/pricing) cho 3 tài khoản hosting, 10 website và 10 database, không giới hạn thời gian, đủ để chạy bài kiểm tra ở trên với website thật. Chi tiết các lớp bảo mật có ở [trang tính năng bảo mật](/vi/features/security).

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

### Một website bị hack trên shared hosting có lây sang website khác không?

Có, nếu mọi website chạy PHP bằng cùng một user. Khi đó webshell có quyền như mọi website khác, đọc được file cấu hình và cài backdoor vào chúng. Với user Linux riêng, PHP-FPM riêng và thư mục home bị ẩn, mã độc chỉ nằm trong một tài khoản.

### Chỉ dùng open_basedir có đủ để cách ly tài khoản hosting không?

Không. Tài liệu PHP gọi nó là "lưới an toàn bổ sung" không thể dựa vào khi cần bảo mật. Nó chỉ ràng buộc PHP, không áp cho Unix socket, và không có tác dụng với shell, cron hay Node.js. Hãy dùng nó bên trên PHP theo user và cách ly hệ thống file, không thay thế chúng.

### Có cần CloudLinux và CageFS để làm shared hosting an toàn không?

Không bắt buộc. CageFS là cách tiện và trưởng thành để có cách ly mạnh trên server cPanel, DirectAdmin hoặc Plesk. Các mục tiêu tương tự có thể đạt bằng tính năng chuẩn của Linux: user riêng, PHP-FPM theo user, sandbox systemd, `hidepid` và cgroups. Điều quan trọng là mọi lớp thực sự có mặt, và bạn kiểm tra được.

### Làm sao biết các tài khoản hosting đã được cách ly?

Tải một file PHP nhỏ lên một tài khoản, thử liệt kê thư mục home của tài khoản khác, `/etc/nginx` và `/root`, rồi đếm số tiến trình nhìn thấy. Sau đó thử tương tự từ shell của tài khoản đó. Mọi lần thử chạm vào tài khoản khác đều phải thất bại.

### Cách ly tài khoản có làm website chậm đi không?

Rất ít nếu làm cẩn thận. Pool PHP riêng tốn thêm một chút RAM, và các panel giảm chi phí này bằng cách chỉ khởi động pool khi có truy cập và tắt pool khi rảnh. `open_basedir` có chi phí đo được vì tắt realpath cache của PHP, đó cũng là một lý do nên ưu tiên cách ly ở cấp kernel.
