Apakah opsi PHP 'cgi.fix_pathinfo' benar-benar berbahaya dengan Nginx + PHP-FPM?

51

Ada sebuah banyak dari berbicara tentang masalah keamanan relatif terhadap cgi.fix_pathinfopilihan PHP digunakan dengan Nginx (biasanya PHP-FPM, CGI cepat).

Akibatnya, file konfigurasi nginx default yang digunakan untuk mengatakan:

# NOTE: You should have "cgi.fix_pathinfo = 0;" in php.ini

Namun, sekarang, wiki Nginx "resmi" menyatakan bahwa PATH_INFO dapat ditangani dengan benar tanpa menonaktifkan opsi PHP di atas. Terus?

Pertanyaan

  • Bisakah Anda jelaskan apa fungsinya cgi.fix_pathinfo? ( dokumen resmi hanya mengatakan : "Untuk informasi lebih lanjut tentang PATH_INFO, lihat spesifikasi CGI")
  • Apa yang sebenarnya akan dilakukan PHP dengan variabel PATH_INFO- SCRIPT_FILENAMEvariabel ini dan ?
  • Mengapa dan bagaimana bisa berbahaya dengan Nginx? ( contoh terperinci )
  • Apakah masalah masih ada dalam versi terbaru dari program ini?
  • Apakah Apache rentan?

Saya mencoba memahami masalah di setiap langkah. Sebagai contoh, saya tidak mengerti mengapa menggunakan soket Unix php-fpm dapat menghindari masalah ini.

Totor
sumber
1
Anda mungkin menjawab pertanyaan Anda sendiri dengan memahami perbedaan antara PATH_INFO dan PATH_TRANSLATED: blogs.msdn.com/b/david.wang/archive/2005/08/04/...
Giovanni Tirloni

Jawaban:

79

TL; DR - perbaikannya (yang bahkan mungkin tidak Anda perlukan) SANGAT SEDERHANA dan di akhir jawaban ini.

Saya akan mencoba menjawab pertanyaan spesifik Anda, tetapi kesalahpahaman Anda tentang apa PATH_INFO itu membuat pertanyaan itu sendiri sedikit salah.

  • Pertanyaan pertama harus "Apa bisnis info jalur ini?"

  • Pertanyaan Anda berikutnya seharusnya: "Bagaimana PHP menentukan apa PATH_INFOdan apa SCRIPT_FILENAME?"

    • Versi PHP sebelumnya adalah naif dan secara teknis bahkan tidak mendukung PATH_INFO, jadi apa yang seharusnya menjadi PATH_INFOmunged SCRIPT_FILENAMEyang, ya, rusak dalam banyak kasus. Saya tidak memiliki versi PHP yang cukup lama untuk diuji, tetapi saya percaya PHP dilihat SCRIPT_FILENAMEsebagai keseluruhan shebang: "/path/to/script.php/THIS/IS/PATH/INFO" dalam contoh di atas (diawali dengan dokumen seperti biasa).
    • Dengan cgi.fix_pathinfo diaktifkan, PHP sekarang dengan benar menemukan "/ INI / IS / PATH / INFO" untuk contoh di atas dan memasukkannya ke dalam PATH_INFOdan SCRIPT_FILENAMEmendapatkan hanya bagian yang menunjuk ke skrip yang diminta (diawali dengan dokumen tentu saja).
    • Catatan: ketika PHP benar-benar mendukung PATH_INFO, mereka harus menambahkan pengaturan konfigurasi untuk fitur baru sehingga orang yang menjalankan skrip yang bergantung pada perilaku lama dapat menjalankan versi PHP baru. Itu sebabnya bahkan ada saklar konfigurasi untuk itu. Seharusnya sudah built-in (dengan perilaku "berbahaya") dari awal.
  • Tetapi bagaimana PHP tahu bagian mana dari skrip dan apa itu info jalur? Bagaimana jika URI itu seperti:

    http://example.com/path/to/script.php/THIS/IS/PATH/INFO.php?q=foo

    • Itu bisa menjadi pertanyaan kompleks di beberapa lingkungan. Apa yang terjadi di PHP adalah ia menemukan bagian pertama dari jalur URI yang tidak sesuai dengan apa pun di bawah dokumen server. Untuk contoh ini, ia melihat bahwa di server Anda Anda tidak memiliki "/docroot/path/to/script.php/THIS" tetapi Anda pasti memiliki "/docroot/path/to/script.php" jadi sekarang SCRIPT_FILENAMEtelah ditentukan dan PATH_INFOmendapat sisanya.
    • Jadi sekarang contoh yang baik dari bahaya yang diperinci dengan baik dalam dokumen Nginx dan dalam jawaban Hrvoje Špoljar (Anda tidak bisa cerewet tentang contoh yang jelas seperti itu) menjadi lebih jelas: diberi contoh Hrvoje (" http: // contoh. com / foo.jpg / nonexistent.php "), PHP melihat file di dokumen Anda" /foo.jpg "tetapi tidak melihat apa pun yang disebut" /foo.jpg/nonexistent.php "jadi SCRIPT_FILENAMEdapatkan" /foo.jpg " (lagi, diawali dengan docroot) dan PATH_INFOmendapat "/nonexistent.php".
  • Mengapa dan bagaimana itu bisa berbahaya sekarang harus jelas:

    • Server web benar-benar tidak bersalah - itu hanya proksi URI ke PHP, yang dengan polos menemukan bahwa "foo.jpg" sebenarnya mengandung konten PHP, jadi itu mengeksekusinya (sekarang Anda sudah dibajak!). Ini TIDAK khusus untuk Nginx per se.
  • Masalah NYATA adalah bahwa Anda membiarkan konten yang tidak tepercaya diunggah di suatu tempat tanpa sanitasi dan Anda mengizinkan permintaan sewenang-wenang lainnya ke lokasi yang sama, yang dengan senang hati dieksekusi oleh PHP ketika bisa.
  • Nginx dan Apache dapat dibangun atau dikonfigurasikan untuk mencegah permintaan menggunakan tipuan ini, dan ada banyak contoh cara melakukannya, termasuk dalam jawaban user2372674 . Artikel blog ini menjelaskan masalahnya dengan baik, tetapi tidak ada solusi yang tepat.

  • Namun, solusi terbaik adalah memastikan PHP-FPM dikonfigurasi dengan benar sehingga tidak akan pernah mengeksekusi file kecuali diakhiri dengan ".php". Perlu dicatat bahwa versi terbaru PHP-FPM (~ 5.3.9 +?) Memiliki ini sebagai default, jadi bahaya ini tidak begitu banyak masalah lagi.

Solusinya

Jika Anda memiliki versi terbaru PHP-FPM (~ 5.3.9 +?), Maka Anda tidak perlu melakukan apa-apa, karena perilaku aman di bawah ini sudah merupakan default.

Kalau tidak, cari www.conffile php-fpm (mungkin /etc/php-fpm.d/www.conf, tergantung sistem Anda). Pastikan Anda memilikinya:

security.limit_extensions = .php

Sekali lagi, itu standar di banyak tempat hari ini.

Perhatikan bahwa ini tidak mencegah penyerang mengunggah file ".php" ke folder unggahan WordPress dan mengeksekusi yang menggunakan teknik yang sama. Anda masih harus memiliki keamanan yang baik untuk aplikasi Anda.

pengguna109322
sumber
5
Jawaban bagus! Untuk memperjelas: jika, seperti yang Anda katakan, PHP menentukan apa SCRIPT_FILENAME, mengapa ada fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;baris di nginxconf saya ? Apakah ini mengesampingkan upaya PHP untuk menemukan nilai SCRIPT_FILENAMEdengan sendirinya?
Totor
Apakah ada fungsi untuk mendapatkan nilai security.limit_extensions? Saya mencoba phpinfo(),, ini_get(security.limit_extensions)dan ini_get_all()tanpa hasil.
elbowlobstercowstand
Terima kasih, jika versi PHP-FPM terbaru (~ 5.3.9 +?) Memiliki ini sebagai default, mengapa php7.1 membutuhkannya? Atau artikel ini salah?
Yevgeniy Afanasyev
14

Intinya tanpa ini, Anda dapat mengunggah file dengan kode php bernama seperti 'foo.jpg' ke server web; kemudian memintanya seperti http: //domain.tld/foo.jpg/nonexistent.php dan tumpukan server web akan keliru mengatakan oh; ini adalah PHP; Saya perlu memproses ini, ia akan gagal menemukan foo.jpg / nonexistent.php sehingga akan kembali ke foo.jpg dan memproses foo.jpg sebagai kode php. Itu berbahaya karena membuka sistem untuk intrusi yang sangat mudah; aplikasi web apa pun yang memungkinkan pengunggahan gambar misalnya menjadi alat untuk mengunggah backdoor.

Mengenai menggunakan php-fpm dengan soket unix untuk menghindarinya; IMO itu tidak akan menyelesaikan masalah.

Hrvoje Špoljar
sumber
Anda hanya mengulangi apa yang bisa dibaca pada tautan yang saya berikan. Anda tidak menjelaskan mekanisme sebenarnya. Jawaban Anda membutuhkan nilai tambah IMHO.
Totor
6
Itu mungkin benar, tetapi gelar Anda memiliki pertanyaan dan jawaban untuk pertanyaan itu dalam tanggapan saya. Jika Anda menginginkannya secara eksplisit; ya itu berbahaya; sangat berbahaya.
Hrvoje Špoljar
1 / Jawaban saya tidak terbatas pada judulnya: ia memiliki tubuh. 2 / user109322 telah membuktikan Anda salah: nilai apa pun yang digunakan untuk cgi.fix_pathinfoadalah tidak berbahaya, karena conf default php-fpmaman (hanya akan mengeksekusi file dengan .phpekstensi).
Totor
2

Di wiki Nginx sebagai langkah pengamanan

if (!-f $document_root$fastcgi_script_name) {
    return 404;
}

termasuk dalam blok lokasi. Dalam tutorial lainnya

try_files $uri =404;

digunakan, yang seharusnya melakukan hal yang sama, tetapi dapat memberikan masalah menurut wiki Nginx. Dengan opsi-opsi ini, cgi.fix_pathinfo=1seharusnya tidak menjadi masalah lagi. Informasi lebih lanjut dapat ditemukan di sini .

pengguna2372674
sumber