2009-12-16 29 views
24

Lời chào, Tôi hy vọng sẽ làm cho chương trình nhỏ bé của tôi được an toàn để người dùng độc hại tiềm ẩn không thể xem các tệp nhạy cảm trên máy chủ.Vệ sinh đường dẫn tập tin trong PHP

$path = "/home/gsmcms/public_html/central/app/webroot/{$_GET['file']}"; 


    if(file_exists($path)) { 
     echo file_get_contents($path); 
    } else { 
     header('HTTP/1.1 404 Not Found'); 
    } 

Tắt đầu tôi biết đầu vào như '../../../../../../etc/passwd' sẽ gặp sự cố, nhưng tự hỏi điều gì khác đầu vào bất tỉnh tôi nên mong đợi và làm thế nào để ngăn chặn chúng.

+1

Chỉ loại trừ bất kỳ đầu vào nào chứa ../ – halfdan

+2

Đồng ý rằng sẽ giải quyết được một vấn đề, nhưng tôi giả định rằng có nhiều mối nguy hiểm khác mà tôi cần tìm. Tôi đang tìm kiếm một giải pháp mạ sắt tốt cho tất cả chúng – SeanDowney

+3

@halfdan - luôn luôn cố gắng tránh một cách tiếp cận danh sách đen để bảo mật như thế này, sẽ luôn có một cái gì đó bạn bỏ lỡ. Chẳng hạn như sử dụng các ký tự backspace, tab, newlines, null chars, các ký tự unicode khác, hoặc cố ý phá vỡ các ký tự unicode mà sẽ vượt qua bộ lọc của bạn, nhưng vẫn gây ra chức năng PHP để làm một cái gì đó bạn đang cố gắng để bảo vệ nó. Kiểm tra những gì bạn thực sự muốn: con đường kết quả là dưới một vị trí an toàn. – Cheekysoft

Trả lời

33

realpath() sẽ cho phép bạn chuyển đổi bất kỳ đường dẫn nào có thể chứa thông tin tương đối thành đường dẫn tuyệt đối ... bạn có thể đảm bảo rằng đường dẫn đó nằm trong một thư mục phụ nhất định mà bạn muốn cho phép tải xuống.

+4

Đây là giải pháp cuối cùng của tôi: $ baseDir = "/ home/gsmcms/public_html/central/app/webroot/"; $ path = realpath ($ baseDir. $ _GET ['tệp']); // nếu baseDir không ở phía trước 0 == strpos, rất có thể là hack hacking nếu (strpos ($ path, $ baseDir)) { \t die ('Invalid Path'); } elseif (file_exists ($ path)) { \t echo file_get_contents ($ path); } khác { \t tiêu đề ('HTTP/1.1 404 Không tìm thấy '); \t echo "Không thể tìm thấy tệp được yêu cầu"; } – SeanDowney

+1

@SeanDowney có một lỗi trong giải pháp của bạn, cụ thể là bạn nên kiểm tra xem strpos không trả về false hoặc khác không, mà bạn không làm. – Michael

+1

realpath() là một người bạn tốt nhưng không đủ - nó chỉ trả lại đường dẫn đầy đủ nếu bạn đang đề cập đến một đường dẫn hiện có. Nếu không nó sẽ trả về false; trong một số trường hợp, điều này sẽ không gây ra bất kỳ sự cố nào nhưng xem xét thao tác "lưu dưới dạng" với tính năng tự động tạo thư mục con, nó có thể làm hỏng trò chơi. (Người dùng cho bạn biết nơi để đặt các tập tin nhưng bạn trả lời "đường dẫn không hợp lệ" kể từ khi realpath trả về false.) Nó có lẽ không phải những gì bạn đang làm; chỉ cần đề cập đến. – dkellner

11

Sử dụng basename thay vì cố gắng dự đoán tất cả các đường dẫn không an toàn mà người dùng có thể cung cấp.

+0

điều này có thể hoạt động trong một số trường hợp, tuy nhiên tôi dự kiến ​​sẽ bao gồm cả thư mục, ví dụ: '/js/jquery/jquery.js' – SeanDowney

6

Nếu bạn có thể, hãy sử dụng danh sách trắng như một loạt tệp được phép và kiểm tra đầu vào dựa vào đó: nếu tệp do người dùng yêu cầu không có trong danh sách đó, từ chối yêu cầu.

+0

Đây sẽ là ý tưởng tốt nhất, nhưng có lẽ nhiều công việc hơn tôi muốn làm :) – SeanDowney

+0

Trừ khi bạn muốn rò rỉ nguồn của tất cả các tệp của bạn trong webroot của bạn, bạn có thể muốn làm điều này. – Cheekysoft

4

Có rủi ro bảo mật bổ sung và đáng kể ở đây. Tập lệnh này sẽ chèn nguồn của tệp vào luồng đầu ra mà không có bất kỳ xử lý phía máy chủ nào. Điều này có nghĩa là tất cả mã nguồn của bất kỳ tệp có thể truy cập nào đều sẽ bị rò rỉ trên internet.

+0

điểm tốt, tôi sẽ thêm một danh sách trắng các tiện ích được phép như: js, css, jpg, gif ... – SeanDowney

7

Giải pháp của OP:

$baseDir = "/home/gsmcms/public_html/central/app/webroot/"; 
$path = realpath($baseDir . $_GET['file']); 

// if baseDir isn't at the front 0==strpos, most likely hacking attempt 
if(strpos($path, $baseDir) !== 0 || strpos($path, $baseDir) === false) { 
    die('Invalid Path'); 
} elseif(file_exists($path)) { 
    echo file_get_contents($path); 
} else { 
    header('HTTP/1.1 404 Not Found'); 
    echo "The requested file could not be found"; 
} 
+0

hãy tìm hiểu cách sử dụng các tính năng nâng cao của SO! :) nó đơn giản hóa * copy-paste * –

+0

FYI, tôi đã tìm thấy một lỗi trong giải pháp này, nhưng bản chỉnh sửa của tôi chứa bản sửa lỗi đã bị từ chối. – Michael

+0

Vui lòng kiểm tra ở trên. Nó phải rõ ràng hơn và hợp lý hơn cho mục đích sanity. –

1

Thậm chí nếu bạn đang sử dụng realpath, bạn vẫn nên gỡ bỏ tất cả ".." trước khi sử dụng nó. Nếu không, kẻ tấn công có thể đọc toàn bộ cấu trúc thư mục của máy chủ của bạn bằng sức mạnh vũ phu, ví dụ: "valid_folder /../../ test_if_this_folder_name_exists/valid_folder" - nếu ứng dụng chấp nhận đường dẫn này, kẻ tấn công biết rằng thư mục tồn tại.

0

Để xóa tất cả /. /.. hoặc \. \ .. và chuyển đổi thành tất cả dấu gạch chéo phía trước vì các môi trường khác nhau sẽ chấp nhận dấu gạch chéo. Điều này sẽ cung cấp một bộ lọc khá an toàn cho đầu vào đường dẫn. Trong mã của bạn, bạn nên so sánh nó với thư mục cha mà bạn không muốn truy cập chỉ trong trường hợp.

$path = realpath(implode('/', array_map(function($value) {return trim($value, '.');}, explode('/', str_replace('\\', '/', $path))))); 
+0

@Koby: Tại sao bạn chỉnh sửa câu trả lời? Các str_replace đặt tất cả các dấu gạch chéo về phía trước và đường dẫn thực hiện làm cho nó tất cả các dấu gạch chéo .. không chỉnh sửa mã của tôi –

Các vấn đề liên quan