Vì sao xoá file trong PHP rất dễ toang
Những đoạn code kiểu này xuất hiện nhan nhản:
<?php
unlink($_GET['file']);
Chỉ cần user truyền vào:
../../../../index.php
Thế là xong. Đây là path traversal – lỗi kinh điển nhưng vẫn có người dẫm.
Nguyên tắc xoá file an toàn
- Không bao giờ tin đường dẫn do người dùng gửi
- Chỉ cho phép xoá trong thư mục được chỉ định
- Dùng
realpath()để resolve đường dẫn thật - Không xoá thư mục, chỉ xoá file
- Kiểm tra file có tồn tại và có quyền xoá
Thư mục gốc được phép xoá
Đầu tiên, cần xác định rõ: chỉ xoá file trong một thư mục cụ thể, ví dụ thư mục upload:
<?php
define('UPLOAD_DIR', __DIR__ . '/uploads');
Mọi thao tác xoá đều phải nằm trong khu vực này. Ra ngoài là cấm.
Hàm xoá file an toàn
<?php
function safe_delete_file(string $relativePath): bool {
// 1. không cho phép path rỗng
if ($relativePath === '') {
return false;
}
// 2. chuẩn hoá path tuyệt đối
$baseDir = realpath(UPLOAD_DIR);
if ($baseDir === false) {
return false;
}
$targetPath = realpath($baseDir . DIRECTORY_SEPARATOR . $relativePath);
// 3. file không tồn tại
if ($targetPath === false) {
return false;
}
// 4. đảm bảo file nằm trong thư mục cho phép
if (strpos($targetPath, $baseDir) !== 0) {
return false;
}
// 5. chỉ cho phép xoá file, không xoá thư mục
if (!is_file($targetPath)) {
return false;
}
// 6. kiểm tra quyền ghi
if (!is_writable($targetPath)) {
return false;
}
return unlink($targetPath);
}
Vì sao realpath là vũ khí sống còn
realpath() resolve toàn bộ:
../- symlink
- đường dẫn ảo
Nhờ đó, bạn so sánh được đường dẫn thật trên hệ thống, không bị đánh lừa bởi chuỗi path giả mạo.
Ví dụ sử dụng thực tế
<?php
$file = $_POST['file'] ?? '';
if (!safe_delete_file($file)) {
die('Không thể xoá file');
}
echo 'Đã xoá file thành công';
Lưu ý: biến $file chỉ nên là đường dẫn tương đối bên trong thư mục upload, ví dụ:
<?php
avatars/user123.png
Những sai lầm cần tránh tuyệt đối
- Dùng
unlink()trực tiếp với input người dùng - Cho phép truyền path tuyệt đối
- Xoá cả thư mục bằng
rmdir()mà không kiểm soát - Không giới hạn thư mục gốc
Có nên cho xoá theo id thay vì path
Câu trả lời là: nên.
Thay vì nhận path từ client, hãy:
- Nhận
file_id - Lấy path từ database
- Rồi mới gọi hàm xoá
Cách này giảm đáng kể rủi ro bị path traversal.
Kết luận
Xoá file trong PHP không khó, nhưng xoá an toàn mới là chuyện đáng nói. Chỉ cần một hàm cẩn thận, dùng realpath đúng chỗ và giới hạn thư mục rõ ràng, bạn có thể tránh được những tai nạn kiểu “deploy xong xoá luôn production”.
Nhớ kỹ một câu: xoá file sai một lần, debug cả đời!
Bình luận