petikan melarikan diri Mysql. Dan paling sedikit bantuan adalah daripada fungsi yang anda bangunkan. Watak tidak berguna melarikan diri


Pertama, sedikit tentang mengapa garis miring ini diperlukan secara umum.
Jika kita menggantikan sebarang data ke dalam pertanyaan, maka untuk membezakan data ini daripada arahan SQL, ia mesti diletakkan dalam petikan.
Sebagai contoh, jika anda menulis
SELECT * FROM table WHERE nama = Bil
maka pangkalan data akan memutuskan bahawa Bil ialah nama medan lain, tidak akan menemuinya, dan akan membuang ralat. Oleh itu, data yang digantikan (dalam kes ini, nama Bill) mesti disertakan dalam petikan - maka pangkalan data akan menganggapnya sebagai rentetan, yang nilainya mesti diberikan kepada medan nama:
PILIH * DARI jadual WHERE nama = "Bil"
Walau bagaimanapun, petikan juga mungkin muncul dalam data itu sendiri. Sebagai contoh,
PILIH * DARI jadual WHERE nama = "D"Artagnan"
Di sini pangkalan data akan memutuskan bahawa "D" ialah data, dan Artagnan ialah arahan yang tidak diketahuinya, dan juga akan membuang ralat. Oleh itu, adalah perlu untuk mengesan semua data untuk menjelaskan kepada pangkalan data bahawa tanda petikan (dan beberapa aksara khas lain) yang terdapat di dalamnya merujuk kepada data.
Akibatnya, kami akan menerima permintaan yang betul yang tidak akan menyebabkan ralat:
PILIH * DARI jadual WHERE nama = "D\"Artagnan"

Oleh itu, kami mendapati bahawa apabila menggantikan data rentetan ke dalam pertanyaan, dua peraturan harus dipatuhi:
- semua data rentetan yang dimasukkan mesti disertakan dalam petikan (tunggal atau dua, tetapi yang tunggal lebih mudah dan lebih kerap digunakan).
- watak istimewa mesti dilepaskan dengan garis miring.

Ia harus diberi perhatian khusus: garis miring yang ditambahkan TIDAK masuk ke dalam pangkalan data. Mereka hanya diperlukan dalam permintaan. Apabila memukul pangkal, garis miring dibuang. Sehubungan itu, kesilapan biasa ialah menggunakan stripslash apabila mendapatkan data daripada pangkalan data.

Semua perkara di atas digunakan pada data rentetan dan tarikh. Nombor boleh dimasukkan tanpa mengekori atau mengelilinginya dengan tanda petikan. Jika anda melakukan ini kemudian SEMESTINYA! memaksa data kepada jenis yang dikehendaki sebelum memasukkannya ke dalam pertanyaan, contohnya:
$id = intval ($id);
Walau bagaimanapun, untuk kesederhanaan (dan kebolehpercayaan), anda boleh bekerja dengan nombor seperti dengan rentetan (kerana mysql masih menukarnya kepada jenis yang dikehendaki). Sehubungan itu, kami akan mengesan sebarang data yang dimasukkan ke dalam permintaan dan menyertakannya dalam tanda petikan.

Selain itu, terdapat satu lagi peraturan - pilihan, tetapi ia harus diikuti untuk mengelakkan ralat:
Nama medan dan jadual hendaklah disertakan dalam petikan tunggal belakang - "`" (kunci dengan simbol ini terletak pada papan kekunci standard di sebelah kiri kekunci "1" Lagipun, nama medan boleh bertepatan dengan mysql kata kunci, tetapi jika kita menggunakan petikan belakang, maka MySQL akan memahami semuanya betul:
PILIH * DARI `jadual` DI MANA `tarikh` = "2006-04-04"
Anda harus membezakan antara tanda petikan ini dan jangan mengelirukan satu dengan yang lain. Anda juga harus ingat bahawa backtick tidak terlepas dengan garis miring.

Jadi, kami telah mempelajari cara menggantikan data dengan betul kepada permintaan.
TAPI! Pembinaan pertanyaan dinamik tidak terhad kepada penggantian data. Selalunya kita perlu menggantikan arahan SQL dan nama medan ke dalam pertanyaan. Dan di sini kita beralih kepada topik keselamatan:

SQL Injection ialah kaedah serangan penggodam apabila data yang dipindahkan ke skrip diubah suai sedemikian rupa sehingga pertanyaan yang dijana dalam skrip ini mula melakukan sesuatu yang sama sekali berbeza daripada apa yang dimaksudkan untuknya.
Peraturan untuk melindungi daripada serangan sedemikian boleh dibahagikan kepada dua perkara:
1. Bekerja dengan data.
2. Bekerja dengan kawalan pertanyaan.

Kami membincangkan perkara pertama secara terperinci di atas. Ia boleh dikatakan bahawa ia bukan, sebenarnya, pembelaan. Pematuhan dengan peraturan untuk menambah data pada pertanyaan ditentukan, pertama sekali, oleh keperluan SQL SYNTAX. Dan sebagai kesan sampingan, kami juga mempunyai perlindungan daripada penggodaman.

Perkara kedua adalah lebih sukar, kerana tiada peraturan universal tunggal untuk data - tanda belakang tidak akan melindungi nama medan daripada diubah suai oleh penggodam. Anda tidak boleh menggunakan petikan untuk melindungi nama jadual, pernyataan SQL, parameter arahan LIMIT atau pernyataan lain.
Oleh itu, peraturan asas apabila menggantikan elemen kawalan ke dalam pertanyaan ialah:
Jika anda perlu memasukkan penyata SQL atau nama medan, pangkalan data, jadual secara dinamik ke dalam pertanyaan, maka dalam keadaan apa pun anda tidak boleh memasukkannya terus ke dalam pertanyaan.
Semua pilihan untuk tambahan tersebut mesti ditulis ADVANCE dalam skrip anda dan dipilih berdasarkan apa yang dimasukkan oleh pengguna.
Sebagai contoh, jika anda perlu menghantar nama medan kepada pesanan oleh pengendali, maka dalam keadaan apa pun anda tidak boleh menggantikannya secara langsung. Kita perlu menyemaknya terlebih dahulu. Sebagai contoh, buat tatasusunan nilai yang sah dan gantikannya ke dalam permintaan hanya jika parameter yang diluluskan hadir dalam tatasusunan ini:
$orders =array("nama" , "harga" , "qty" );
$key = array_search($_GET["sort"], $orders));
$orderby = $orders [ $key ];
$query = "PILIH * DARI `jadual` PESANAN OLEH$orderby " ;

Kami mencari tatasusunan pilihan yang telah diterangkan untuk perkataan yang dimasukkan oleh pengguna, dan jika kami menemuinya, kami memilih elemen tatasusunan yang sepadan. Jika tiada padanan ditemui, elemen pertama tatasusunan akan dipilih.
Oleh itu, apa yang digantikan ke dalam permintaan bukanlah apa yang dimasukkan pengguna, tetapi apa yang ditulis dalam skrip kami.
Perkara yang sama mesti dilakukan dalam semua kes lain.
Sebagai contoh, jika klausa WHERE dijana secara dinamik:
jika (!kosong($_GET [ "harga" ])) $where .= "price="" . mysql_real_escape_string ($_GET [ "price" ]). """ ;
$query = "PILIH * DARI `jadual` WHERE $where " ;

Sukar untuk saya membayangkan kes di mana nama jadual boleh dimasukkan ke dalam pertanyaan secara dinamik, tetapi jika ini berlaku, maka nama itu juga perlu dimasukkan hanya daripada set yang telah ditetapkan dalam skrip.
Parameter pengendali LIMIT harus dipaksa kepada jenis integer menggunakan operasi aritmetik atau fungsi intval().
Jangan fikir bahawa contoh yang disenaraikan di sini menghabiskan semua pilihan untuk pembinaan pertanyaan dinamik. Anda hanya perlu memahami prinsip dan menerapkannya dalam semua kes sedemikian.


Pertama, sedikit tentang mengapa garis miring ini diperlukan secara umum.
Jika kita menggantikan sebarang data ke dalam pertanyaan, maka untuk membezakan data ini daripada arahan SQL, ia mesti diletakkan dalam petikan.
Sebagai contoh, jika anda menulis
SELECT * FROM table WHERE nama = Bil
maka pangkalan data akan memutuskan bahawa Bil ialah nama medan lain, tidak akan menemuinya, dan akan membuang ralat. Oleh itu, data yang digantikan (dalam kes ini, nama Bill) mesti disertakan dalam petikan - maka pangkalan data akan menganggapnya sebagai rentetan, yang nilainya mesti diberikan kepada medan nama:
PILIH * DARI jadual WHERE nama = "Bil"
Walau bagaimanapun, petikan juga mungkin muncul dalam data itu sendiri. Sebagai contoh,
PILIH * DARI jadual WHERE nama = "D"Artagnan"
Di sini pangkalan data akan memutuskan bahawa "D" ialah data, dan Artagnan ialah arahan yang tidak diketahuinya, dan juga akan membuang ralat. Oleh itu, adalah perlu untuk mengesan semua data untuk menjelaskan kepada pangkalan data bahawa tanda petikan (dan beberapa aksara khas lain) yang terdapat di dalamnya merujuk kepada data.
Akibatnya, kami akan menerima permintaan yang betul yang tidak akan menyebabkan ralat:
PILIH * DARI jadual WHERE nama = "D\"Artagnan"

Oleh itu, kami mendapati bahawa apabila menggantikan data ke dalam pertanyaan, dua peraturan harus dipatuhi:
- semua data yang dimasukkan ke dalam permintaan mesti disertakan dalam tanda petikan (tunggal atau dua, tetapi yang tunggal lebih mudah dan lebih kerap digunakan).
- dalam semua pembolehubah rentetan, aksara khas mesti dilepaskan dengan garis miring.

Ia harus diberi perhatian khusus: garis miring yang ditambahkan TIDAK masuk ke dalam pangkalan data. Mereka hanya diperlukan dalam permintaan. Apabila memukul pangkal, garis miring dibuang. Sehubungan itu, kesilapan biasa ialah menggunakan stripslash apabila mendapatkan data daripada pangkalan data.

Malah, semua perkara di atas digunakan untuk data rentetan dan tarikh. Nombor boleh dimasukkan tanpa mengekori atau mengelilinginya dengan tanda petikan. Jika anda melakukan ini kemudian SEMESTINYA! memaksa data kepada jenis yang dikehendaki sebelum memasukkannya ke dalam pertanyaan, contohnya:
$id = intval ($id);
Walau bagaimanapun, untuk kesederhanaan (dan kebolehpercayaan), anda boleh bekerja dengan nombor seperti dengan rentetan (kerana mysql masih menukarnya kepada jenis yang dikehendaki). Sehubungan itu, kami akan mengesan sebarang data yang dimasukkan ke dalam permintaan dan menyertakannya dalam tanda petikan.

Selain itu, terdapat satu lagi peraturan - pilihan, tetapi ia harus diikuti untuk mengelakkan ralat:
Nama medan dan jadual hendaklah disertakan dalam petikan tunggal belakang - "`" (kunci dengan simbol ini terletak pada papan kekunci standard di sebelah kiri kekunci "1" Lagipun, nama medan boleh bertepatan dengan mysql kata kunci, tetapi jika kita menggunakan petikan belakang, maka MySQL akan memahami semuanya betul:
PILIH * DARI `jadual` DI MANA `tarikh` = "2006-04-04"
Anda harus membezakan antara tanda petikan ini dan jangan mengelirukan satu dengan yang lain. Anda juga harus ingat bahawa backtick tidak terlepas dengan garis miring.

Jadi, kami telah mempelajari cara menggantikan data dengan betul kepada permintaan.
TAPI! Pembinaan pertanyaan dinamik tidak terhad kepada penggantian data. Selalunya kita perlu menggantikan arahan SQL dan nama medan ke dalam pertanyaan. Dan di sini kita beralih kepada topik keselamatan:

SQL Injection ialah kaedah serangan penggodam apabila data yang dipindahkan ke skrip diubah suai sedemikian rupa sehingga pertanyaan yang dijana dalam skrip ini mula melakukan sesuatu yang sama sekali berbeza daripada apa yang dimaksudkan untuknya.
Peraturan untuk melindungi daripada serangan sedemikian boleh dibahagikan kepada dua perkara:
1. Bekerja dengan data.
2. Bekerja dengan kawalan pertanyaan.

Kami membincangkan perkara pertama secara terperinci di atas. Ia boleh dikatakan bahawa ia bukan, sebenarnya, pembelaan. Pematuhan dengan peraturan untuk menambah data pada pertanyaan ditentukan, pertama sekali, oleh keperluan SQL SYNTAX. Dan sebagai kesan sampingan, kami juga mempunyai perlindungan daripada penggodaman.

Perkara kedua adalah lebih sukar, kerana tiada peraturan universal tunggal untuk data - tanda belakang tidak akan melindungi nama medan daripada diubah suai oleh penggodam. Anda tidak boleh menggunakan petikan untuk melindungi nama jadual, pernyataan SQL, parameter arahan LIMIT atau pernyataan lain.
Oleh itu, peraturan asas apabila menggantikan elemen kawalan ke dalam pertanyaan ialah:
Jika anda perlu memasukkan penyata SQL atau nama medan, pangkalan data, jadual secara dinamik ke dalam pertanyaan, maka dalam keadaan apa pun anda tidak boleh memasukkannya terus ke dalam pertanyaan.
Semua pilihan untuk tambahan tersebut mesti ditulis ADVANCE dalam skrip anda dan dipilih berdasarkan apa yang dimasukkan oleh pengguna.
Sebagai contoh, jika anda perlu menghantar nama medan kepada pesanan oleh pengendali, maka dalam keadaan apa pun anda tidak boleh menggantikannya secara langsung. Kita perlu menyemaknya terlebih dahulu. Sebagai contoh, buat tatasusunan nilai yang sah dan gantikannya ke dalam permintaan hanya jika parameter yang diluluskan hadir dalam tatasusunan ini:
$orders =array("nama" , "harga" , "qty" );
$key = array_search($_GET["sort"], $orders));
$orderby = $orders [ $key ];
$query = "PILIH * DARI `jadual` PESANAN OLEH $orderby";
Kami mencari tatasusunan pilihan yang telah diterangkan untuk perkataan yang dimasukkan oleh pengguna, dan jika kami menemuinya, kami memilih elemen tatasusunan yang sepadan. Jika tiada padanan ditemui, elemen pertama tatasusunan akan dipilih.
Oleh itu, apa yang digantikan ke dalam permintaan bukanlah apa yang dimasukkan pengguna, tetapi apa yang ditulis dalam skrip kami.
Perkara yang sama mesti dilakukan dalam semua kes lain.
Sebagai contoh, jika klausa WHERE dijana secara dinamik:
jika (!kosong($_GET [ "harga" ])) $where .= "price="" . mysql_real_escape_string ($_GET [ "price" ]). """ ;
$query = "PILIH * DARI `jadual` DI MANA $where";
Sukar untuk saya membayangkan kes di mana nama jadual boleh dimasukkan ke dalam pertanyaan secara dinamik, tetapi jika ini berlaku, maka nama itu juga perlu dimasukkan hanya daripada set yang telah ditetapkan dalam skrip.
Parameter pengendali LIMIT harus dipaksa kepada jenis integer menggunakan operasi aritmetik atau fungsi intval().
Jangan fikir bahawa contoh yang disenaraikan di sini menghabiskan semua pilihan untuk pembinaan pertanyaan dinamik. Anda hanya perlu memahami prinsip dan menerapkannya dalam semua kes sedemikian.

Ciri-ciri bekerja dengan pengendali LIKE
Kes yang berasingan sepenuhnya ialah pengendali LIKE.
Pertama, sebagai tambahan kepada pengesanan biasa, garis miring mesti digandakan dalam pembolehubah yang digantikan dalam LIKE. Iaitu, jika pembolehubah mengandungi aksara \, maka ia mesti digandakan, dan kemudian pelarian biasa mesti dilakukan melalui mysql_real_escape_string.
Sebagai contoh, jika kita sedang mencari rentetan
aksara \ dipanggil "backslash" dan kami memerlukan padanan tepat, kemudian kami hanya menggunakan mysql_real_escape_string dan pertanyaannya adalah standard:
SELECT * FROM test WHERE medan = "simbol \\ dipanggil \"backslash\"" Jika kita ingin menggantikan rentetan ini dalam LIKE, maka kita perlu menggantikan setiap slash dengan dua, dan kemudian gunakan mysql_real_escape_string. Hasilnya akan menjadi
PILIH * DARI jadual DI MANA medan SEPERTI "%character \\\\ dipanggil \"backslash\"%"
Perkara kedua yang perlu diambil perhatian ialah tiada satu pun fungsi yang menambah garis miring menambahkannya pada aksara meta carian "%" dan "_" yang digunakan dalam pengendali LIKE. Oleh itu, jika anda menggunakan operator ini dan tidak mahu aksara _ dan % digunakan sebagai kad bebas, kemudian tambah garis miring secara manual. Ini boleh dilakukan dengan arahan
$data = addCslashes($data, "%_");

Perhatian - ini tidak menambah bulu mata! Nama fungsi ini mempunyai tambahan "c" dalam namanya.
Oleh itu, ternyata kita mesti memproses pembolehubah yang digunakan dalam operator LIKE secara berasingan.
mula-mula gantikan satu slash dengan dua, menggunakan, sebagai contoh, kod seperti ini:
$var = str_replace ("\\" , "\\\\" , $var ); kemudian (anda boleh, bersama dengan semua data lain yang masuk ke dalam permintaan), jejak:
$var = mysql_real_escape_string ($var);
dan kemudian jika kita mahu _ dan % sepadan dengan diri mereka sendiri, kita lakukan
$var = addCslashes($var, "_%"); Akibatnya, jika kita mencari, sebagai contoh, untuk rentetan berikut
aksara \ dipanggil "sentak belakang" dan aksara _ dipanggil "garis bawah" kemudian selepas diproses, dalam permintaan ia sepatutnya kelihatan seperti ini:

"%simbol \\\\ dipanggil \"slash belakang\" dan simbol \_ dipanggil \"garis bawah\"
Iaitu, garis miring yang asalnya dalam barisan telah meningkat empat kali ganda. Watak yang tinggal dikesan seperti biasa. Tambahan - watak garis bawah kelihatan.
Ini berlaku disebabkan tetapan PHP khas, biasanya didayakan pada pengehosan secara lalai. Secara teorinya, tetapan ini boleh meningkatkan keselamatan skrip yang berfungsi dengan pangkalan data. Dalam praktiknya, penambahan garis miring secara automatik sering mengakibatkan kekeliruan dan kesulitan, kedua-duanya semasa bekerja dengan pangkalan data dan ketiadaannya.
Di bawah ini kita akan meneliti kedua-dua kes ini secara terperinci.

Arahan php.ini, yang secara kolektif dipanggil "petikan ajaib", bertanggungjawab untuk menambah garis miring secara automatik:
magic_quotes_gpc dan magic_quotes_runtime Jika yang pertama didayakan, maka PHP secara automatik menambah garis miring pada data yang datang daripada pengguna - daripada POST, GET permintaan dan kuki (serta log masuk dan kata laluan yang diterima melalui Kebenaran HTTP).
Jika yang kedua, maka garis miring ditambahkan pada data yang diterima semasa pelaksanaan skrip - contohnya, daripada fail atau pangkalan data.

Jika anda bekerja tanpa pangkalan data, atau bekerja dengan pangkalan data dengan betul (yang akan dibincangkan di bawah), garis miring tambahan hanya mengganggu anda, dan anda perlu menyingkirkannya. Cara yang paling mudah dan betul ialah melumpuhkan penambahan automatik dalam tetapan PHP.
Ini boleh dilakukan sama ada dengan membetulkan arahan yang sepadan dalam php.ini, jika anda mempunyai akses kepadanya, atau dengan mencipta fail .htaccess dalam direktori akhir tapak dan menambah baris padanya
php_flag magic_quotes_gpc 0
php_flag magic_quotes_runtime 0

Jika anda tidak boleh melumpuhkannya dengan cara ini, anda perlu menulis kod pelbagai tahap kerumitan untuk mengosongkan data masuk daripada garis miring. (Namun, jika anda ingin menulis aplikasi mudah alih yang tidak bergantung pada tetapan PHP, maka anda masih perlu menulisnya. Dan masukkannya sebagai blok berasingan pada permulaan skrip anda).

Cara paling mudah untuk memahami data yang diperoleh semasa operasi adalah dengan menulis pada permulaan skrip:
set_magic_quotes_runtime(0);

  • Untuk data yang diterima daripada pengguna, semuanya jauh lebih rumit. Untuk kod ini kita memerlukan dua fungsi:
  • Anda boleh menyemak sama ada PHP telah menambahkannya menggunakan fungsi get_magic_quotes_gpc.
    Fungsi stripslash menghilangkan garisan.
    Oleh itu, anda perlu menyemak menggunakan yang pertama, dan jika PHP telah ditambahkan, kemudian pergi melalui semua pembolehubah masuk dan kosongkannya menggunakan yang kedua.
    Jika anda bekerja dengan betul, dengan register_globals = off , maka sudah cukup untuk menggunakan stripslash pada semua tatasusunan yang mengandungi data yang datang daripada penyemak imbas.
    sebagai contoh, anda boleh memasukkan kod berikut dalam semua skrip tapak:
    jalur fungsi (& $el ) (
    jika (is_array($el))
    foreach($el sebagai $k => $v )
    jalur($el[$k]);
    }
    lain $el = stripslash ($el );
    jika (get_magic_quotes_gpc()) (
    jalur($_POST);
    jalur($_COOKIE);
    jalur($_REQUEST);
    jika (isset($_SERVER [ "PHP_AUTH_USER" ])) jalur ($_SERVER [ "PHP_AUTH_USER" ]);
    jika (isset($_SERVER [ "PHP_AUTH_PW" ])) jalur ($_SERVER [ "PHP_AUTH_PW" ]);
    }
    Dalam kes tetapan register_globals yang salah, sukar untuk mencari penyelesaian yang boleh diterima, jadi lebih baik - saya ulangi - untuk segera bekerja dengan tetapan yang betul.

    Nota

    • Antara sebab mengapa anda tidak perlu bergantung pada "petikan ajaib" adalah satu lagi. Sangat tidak mungkin, tetapi masih. "Petikan ajaib" sebenarnya merujuk kepada bukan dua arahan, tetapi tiga. Yang ketiga ialah magic_quotes_sybase. Ia bukan sahaja menambah petikan dan bukannya garis miring, tetapi ia juga membatalkan kesan magic_quotes_gpc. Jika dengan beberapa keajaiban kedua-dua arahan ini mempunyai status "hidup", maka yang terakhir tidak akan berfungsi! Iaitu, dengan bergantung pada "petikan ajaib", dalam kes ini kita akan mendapat semua keseronokan pertanyaan yang disusun secara tidak betul. Secara amnya, secara teori semata-mata, kita mesti mengambil kira kehadiran arahan ini, kerana ia juga memberikan kejutan seperti... mengubah tingkah laku fungsi tanda garis dan garis garis! Jika magic_quotes_sybase = on , maka fungsi ini mula menambah dan mengalih keluar petikan tunggal dan bukannya slash, masing-masing.
    • Semua contoh yang diberikan hanya membimbangkan pangkalan data Mysql. Peraturan khusus untuk mengarang pertanyaan mungkin berbeza untuk DBMS lain, tetapi prinsip umum tetap sama:
      • jika API untuk bekerja dengan pangkalan data atau perpustakaan pihak ketiga menyediakan fungsi khas untuk mengarang pertanyaan, dan terdapat kemungkinan untuk menggunakannya, maka anda perlu menggunakannya terlebih dahulu.
      • Jika tiada fungsi sedemikian, maka anda harus melihat dalam dokumentasi untuk fungsi untuk melarikan diri aksara khas untuk DBMS ini.
    Nota: borang
    Apabila memaparkan nilai dalam teg input borang, garis miring tidak membantu.
    Untuk memaparkan keseluruhan teks dalam medan sedemikian, nilai mesti disertakan dalam petikan, dan kepada data output gunakan fungsi htmlspecialchars().
    Contoh:

    Ia juga harus diperhatikan (walaupun ini tiada kaitan dengan petikan dan garis miring) bahawa fungsi htmlspecialchars harus digunakan apabila mengeluarkan ke penyemak imbas secara umum kepada semua data yang diterima daripada pengguna yang tidak disahkan. Mengapa ini perlu dilakukan, anda boleh membaca di Google atas permintaan tentang apa itu kelemahan XSS
    oleh phpfaq.ru
  • Disebabkan sifat kerja saya, saya perlu melaksanakan audit keselamatan kod sumber aplikasi web.
    Banyak aplikasi web dan banyak kod...

    Bukan rahsia lagi bahawa kelemahan suntikan SQL adalah yang paling biasa daripada semua kelemahan aplikasi web sebelah pelayan. Terdapat platform dan rangka kerja di mana perkara sedemikian hampir dikecualikan sepenuhnya, contohnya ORM dan sebagainya Tetapi statistik secara berterusan memberitahu kami tentang penguasaan mutlak aplikasi web dengan pertanyaan SQL yang ringkas di Internet Selain itu, terdapat kes di mana ORM boleh digunakan secara am Ia tidak boleh Contohnya, apabila bukan sahaja parameter ungkapan, tetapi juga logik pertanyaan itu sendiri di peringkat operator mesti bergantung pada data pengguna.

    Jadi mari kita mulakan.

    Watak tidak berguna melarikan diri
    Ditemui dalam 83% aplikasi web PHP yang terdedah kepada suntikan SQL
    Menggunakan fungsi melarikan diri untuk aksara seperti
    mysql_escape_string
    mysql_real_escape_string
    menambah sebatan
    tanpa tanda petikan. Selalunya ia menunjukkan dirinya dalam parameter berangka (semua jenis *_id).
    Contoh
    $sql = "PILIH pengguna DARI senarai pengguna WHERE userid=".mysql_real_escape_string($_GET["uid"]);

    Ini nampaknya kod selamat, tetapi hanya pada permukaan. Corak suntikan SQL yang paling biasa dalam PHP dalam amalan saya merayap di sini. Untuk menyerang kelemahan ini, penyerang hanya perlu mengelak daripada menggunakan " " \x00 \r \n \x1a aksara dalam vektor serangan.
    Contohnya:
    /index.php?uid=-777 KESATUAN PILIH kata laluan DARI senarai pengguna

    Cari dalam kod
    Dirumitkan oleh semantik bahasa. Untuk carian mudah anda boleh menggunakan egrep:
    egrep -Rin "(pilih|kemas kini|masukkan|padam|ganti).*(dari|set|ke dalam).*(mysql_escape_string|mysql_real_escape_string|addslashes)" . | grep -v "[\""]["\"]"

    Logik ungkapan carian adalah seperti berikut: cari semua baris yang tidak terdapat jujukan aksara petikan ("", "", "", "") di sebelah kiri fungsi penapisan. Kaedahnya, tentu saja, jauh dari 100%, tetapi adalah mustahil untuk memerlukan ungkapan biasa untuk melakukan analisis semantik.
    Untuk memudahkan untuk memaparkan maklumat, anda boleh menyerlahkan fungsi dalam warna dalam konsol:
    egrep -Rin "(pilih|kemas kini|masukkan|padam|ganti).*(dari|set|ke dalam).*(mysql_escape_string|mysql_real_escape_string|addslashes)" . | grep -v "[\""]["\"]" | egrep --color "(mysql_escape_string|mysql_real_escape_string|addslashes)"

    Untuk melindungi daripada kerentanan kad bebas ini, sebaiknya gunakan penghantaran jenis.
    Ini sentiasa berfungsi lebih pantas dan lebih dipercayai daripada semua jenis penapisan dan penyaringan.
    Untuk contoh di atas, tampalan mungkin seperti ini:
    $sql = "PILIH pengguna DARI senarai pengguna WHERE userid=".intval($_GET["uid"]);

    Ini menyimpulkan esei pendek. Saya menggesa semua pembangun web untuk cuba menyemak sumber mereka untuk reka bentuk sedemikian. Lebih baik lagi, kembangkan skrip carian yang diberikan untuk orang.

    (PHP 4 >= 4.3.0, PHP 5)

    mysql_real_escape_string — Melarikan diri aksara khas dalam rentetan untuk digunakan dalam pernyataan SQL

    Penerangan

    mysql_real_escape_string (rentetan $unescaped_string [, sumber $link_identifier = NULL]): rentetan

    Melarikan diri daripada aksara khas dalam unescaped_string , dengan mengambil kira set aksara semasa sambungan supaya selamat untuk meletakkannya dalam mysql_query(). Jika data binari hendak dimasukkan, fungsi ini mesti digunakan.

    mysql_real_escape_string() memanggil fungsi perpustakaan MySQL mysql_real_escape_string, yang menambahkan garis miring ke belakang kepada aksara berikut: \x00, \n, \r, \ , " , " dan \x1a.

    Fungsi ini mesti sentiasa (dengan beberapa pengecualian) digunakan untuk memastikan data selamat sebelum menghantar pertanyaan kepada MySQL.

    Berhati-hati

    Keselamatan: set aksara lalai

    Set aksara mesti ditetapkan sama ada pada peringkat pelayan atau dengan fungsi API mysql_set_charset() untuk memberi kesan mysql_real_escape_string() . Lihat bahagian konsep pada set aksara untuk mendapatkan maklumat lanjut.

    Parameter

    unescaped_string

    Rentetan yang hendak dilepaskan.

    Pengecam_pautan

    Sambungan MySQL. Jika pengecam pautan tidak dinyatakan, pautan terakhir dibuka oleh mysql_connect() diandaikan. Jika tiada pautan sedemikian ditemui, ia akan cuba menciptanya seolah-olah mysql_connect() telah dipanggil tanpa hujah. Jika tiada sambungan ditemui atau diwujudkan, an E_AMARAN ralat tahap dihasilkan.

    Kembalikan Nilai

    Mengembalikan rentetan yang terlepas, atau PALSU atas kesilapan.

    Ralat/Pengecualian

    Melaksanakan fungsi ini tanpa sambungan MySQL juga akan dipancarkan E_AMARAN ralat PHP peringkat. Hanya laksanakan fungsi ini dengan sambungan MySQL yang sah hadir.

    Contoh

    Contoh #1 Mudah mysql_real_escape_string() contoh

    //Sambung
    $link = mysql_connect("mysql_host" , "mysql_user" , "mysql_password" )
    ATAU mati(mysql_error());

    //Pertanyaan
    $query = sprintf ( "PILIH * DARI pengguna WHERE pengguna="%s" DAN kata laluan="%s"",
    mysql_real_escape_string($user),
    mysql_real_escape_string($password));
    ?>

    Contoh #2 mysql_real_escape_string() memerlukan contoh sambungan

    Contoh ini menunjukkan perkara yang berlaku jika sambungan MySQL tidak hadir semasa memanggil fungsi ini.

    Contoh di atas akan mengeluarkan sesuatu yang serupa dengan:

    Amaran: mysql_real_escape_string(): Tiada fail atau direktori sedemikian dalam /this/test/script.php pada baris 5 Amaran: mysql_real_escape_string(): Pautan ke pelayan tidak dapat diwujudkan dalam /this/test/script.php pada baris 5 rentetan bool(false)(41) "PILIH * DARI pelakon WHERE last_name = """

    Contoh #3 Contoh Serangan Suntikan SQL

    // Kami tidak menyemak $_POST["kata laluan"], ia boleh jadi apa sahaja yang pengguna inginkan!
    $_POST [ "nama pengguna" ] = "aidan" ;
    $_POST [ "kata laluan" ] = "" ATAU ""="" ;

    // Tanya pangkalan data untuk menyemak sama ada terdapat pengguna yang sepadan
    $query = ($_POST [ "nama pengguna" ]) " DAN kata laluan=" ( $_POST [ "kata laluan" ]) "" ;
    mysql_query($query);

    // Ini bermakna pertanyaan yang dihantar ke MySQL ialah:
    echo $query ;
    ?>

    Pertanyaan dihantar ke MySQL:

    Ini akan membolehkan sesiapa sahaja untuk log masuk tanpa kata laluan yang sah.

    Nota

    Sambungan MySQL diperlukan sebelum menggunakan mysql_real_escape_string() jika tidak ralat tahap E_AMARAN dihasilkan, dan PALSU dikembalikan. Jika link_identifier tidak ditakrifkan, sambungan MySQL terakhir digunakan.

    Nota: mysql_real_escape_string() tidak melarikan diri % dan _ . Ini adalah kad bebas dalam MySQL jika digabungkan dengan SUKA, GERAN, atau BATALKAN.

    8 tahun lepas

    Hanya sedikit fungsi yang meniru mysql_real_escape_string yang asal tetapi yang tidak memerlukan sambungan mysql aktif Boleh dilaksanakan sebagai fungsi statik dalam kelas pangkalan data.

    fungsi mysql_escape_mimic ($inp) (
    if(is_array($inp))
    return array_map (__KAEDAH__ , $inp );

    If(!empty($inp ) && is_string ($inp )) (
    return str_replace (array("\\" , "\0" , "\n" , "\r" , """ , """ , "\x1a" ), array("\\\\" , "\ \0" , "\\n" , "\\r" , "\\"" , "\\"" , "\\Z" ), $inp );
    }

    Kembalikan $inp ;
    }
    ?>

    13 tahun yang lalu

    Ambil perhatian bahawa mysql_real_escape_string tidak menambahkan garis miring ke belakang kepada \x00, \n, \r, dan \x1a seperti yang dinyatakan dalam dokumentasi, tetapi sebenarnya menggantikan watak dengan perwakilan MySQL yang boleh diterima untuk pertanyaan (cth. \n digantikan dengan "\ n" litteral). (\, ", dan " terlepas seperti yang didokumenkan) Ini tidak mengubah cara anda harus menggunakan fungsi ini, tetapi saya fikir ia bagus untuk mengetahuinya.

    6 tahun lepas

    Tiada perbincangan tentang melarikan diri selesai tanpa memberitahu semua orang bahawa anda pada dasarnya tidak boleh menggunakan input luaran untuk menjana kod yang ditafsirkan. Ini berlaku untuk pernyataan SQL, atau apa sahaja yang anda akan panggil apa-apa jenis fungsi "eval" pada.

    Jadi, daripada menggunakan fungsi yang rosak teruk ini, gunakan penyataan yang disediakan parametrik.

    Secara jujur, menggunakan data yang disediakan pengguna untuk mengarang pernyataan SQL harus dianggap sebagai kecuaian profesional dan anda harus bertanggungjawab oleh majikan atau pelanggan anda kerana tidak menggunakan penyata yang disediakan parametrik.

    Apakah maksudnya?

    Ini bermakna bukannya membina pernyataan SQL seperti ini:

    "MASUKKAN KE DALAM X (A) NILAI(".$_POST["a"].)"

    Anda harus menggunakan mysqli's prepare() function () untuk melaksanakan pernyataan yang kelihatan seperti ini:

    "MASUKKAN KE DALAM NILAI X (A)(?)"

    NB: Ini tidak bermakna anda tidak boleh sekali-kali menjana pernyataan SQL dinamik. Maksudnya ialah anda tidak boleh menggunakan data yang disediakan pengguna untuk menjana pernyataan tersebut. Sebarang data yang disediakan pengguna harus dihantar sebagai parameter kepada pernyataan selepas ia mempunyai telah disediakan.

    Jadi, sebagai contoh, jika anda membina rangka kerja kecil dan ingin melakukan sisipan pada jadual berdasarkan URI permintaan, adalah demi kepentingan anda untuk tidak mengambil nilai $_SERVER["REQUEST_URI"] (atau mana-mana sebahagian daripadanya) dan menggabungkannya secara langsung dengan pertanyaan anda, sebaliknya, anda harus menghuraikan bahagian nilai $_SERVER["REQUEST_URI"] yang anda inginkan, dan memetakannya melalui beberapa jenis fungsi atau tatasusunan bersekutu kepada bukan pengguna. nilai yang diberikan Jika pemetaan tidak menghasilkan nilai, anda tahu bahawa ada sesuatu yang tidak kena dengan data yang diberikan pengguna.

    Kegagalan untuk mengikuti ini telah menjadi punca kepada beberapa masalah suntikan SQL dalam rangka kerja Ruby On Rails, walaupun ia menggunakan pernyataan yang disediakan parametrik. Beginilah cara GitHub digodam pada satu ketika. Jadi, tiada bahasa yang terlepas daripada masalah ini. Itulah sebabnya ini adalah amalan terbaik umum dan bukan sesuatu yang khusus untuk PHP dan mengapa anda BENAR-BENAR harus mengamalkannya.

    Selain itu, anda masih perlu melakukan beberapa jenis pengesahan data yang disediakan oleh pengguna, walaupun semasa menggunakan pernyataan parametrik yang disediakan. Ini kerana data yang disediakan pengguna itu selalunya akan menjadi sebahagian daripada beberapa HTML yang dijana dan anda ingin memastikan bahawa data yang diberikan pengguna tidak akan menyebabkan masalah keselamatan dalam penyemak imbas.

    9 tahun lepas

    Terdapat kelainan yang menarik dalam contoh #2 tentang suntikan SQL: DAN mengambil keutamaan daripada OR, jadi pertanyaan yang disuntik sebenarnya dilaksanakan sebagai WHERE (pengguna="aidan" DAN kata laluan="") ATAU ""="", jadi sebaliknya mengembalikan rekod pangkalan data yang sepadan dengan nama pengguna sewenang-wenangnya (dalam kes ini "aidan"), ia sebenarnya akan mengembalikan SEMUA rekod pangkalan data Tanpa tertib tertentu Jadi penyerang mungkin boleh log masuk sebagai mana-mana akaun, tetapi tidak semestinya dengan mana-mana .

    Sudah tentu potensi serangan hanya boleh mengubah suai parameter mereka untuk menyasarkan pengguna tertentu yang diminati:

    //Cth. nilai penyerang
    $_POST [ "nama pengguna" ] = "" ;
    $_POST["kata laluan"] = "" ATAU pengguna = "pentadbir" DAN "" = "";

    // Pertanyaan cacat
    $query = "PILIH * DARI pengguna WHERE pengguna="$_POST [ nama pengguna ] " DAN kata laluan=" $_POST [ kata laluan ] "" ;

    echo $query ;

    // Pertanyaan yang dihantar ke MySQL akan berbunyi:
    // SELECT * FROM users WHERE user="" AND password="" OR user="administrator" AND ""="";
    // yang akan membolehkan sesiapa sahaja mendapat akses kepada akaun bernama "pentadbir"

    ?>

    1 tahun lepas

    @feedr
    Saya menghuraikan nota beliau seperti berikut:
    $string = "asda\0sd\x1aas\\\\\\\\dasd\"asdasd\na\"\"sdasdad";
    $array1 = array("\\\\\\\", "\0", "\n", "\r", """, """, "\x1a");
    $array2 = array("\\\\\\\\\\\\\\\\\", "\\\0", "\\\n", "\\\r", "\\\ " ", "\\\"", "\\\Z");
    echo($string);
    gema(PHP_EOL);
    untuk($i=0; $i jika ($i==0)
    $p = "/(?lain
    $p = "/(?echo($i);
    echo($p);
    echo($array2[$i]);
    $string = preg_replace($p, $array2[$i], $string);
    echo("\t");
    echo($string);
    gema(PHP_EOL);
    }
    gema(PHP_EOL);
    echo($string);

    2 tahun lepas

    Untuk Memetik Sam di Safari Numb

    [ "Tiada perbincangan tentang melarikan diri yang lengkap tanpa memberitahu semua orang bahawa anda pada dasarnya tidak boleh menggunakan input luaran untuk menjana kod yang ditafsirkan. Ini berlaku untuk pernyataan SQL, atau apa-apa sahaja yang anda akan panggil apa-apa jenis fungsi "eval".

    Jadi, daripada menggunakan fungsi yang rosak teruk ini, gunakan penyataan yang disediakan parametrik.

    Secara jujur, menggunakan data yang disediakan pengguna untuk mengarang penyata SQL harus dianggap sebagai kecuaian profesional dan anda harus bertanggungjawab oleh majikan atau pelanggan anda kerana tidak menggunakan penyata yang disediakan parametrik." ]

    Sam betul........

    Walau bagaimanapun, saya tidak fikir adalah wajar untuk menghentikan semua pembersihan dan hanya menyerahkan tugas kepada pernyataan yang disediakan parametrik.

    Pembangun tertentu yang bekerja dalam situasi tertentu akan sentiasa mengetahui lebih lanjut tentang input yang sah (khusus untuk konteks itu).

    Jika anda meminta pengguna untuk memasukkan nilai yang telah anda berikan kepada mereka dan anda tahu bahawa semua nilai tersebut bermula AB****** dan rentetan harus mempunyai panjang 7 atau 11 tetapi tidak pernah panjang yang lain maka anda mempunyai asas pra-sanitiser yang baik - panjang rentetan yang dibenarkan berbeza mungkin menunjukkan data lama.

    Saya tidak sekali-kali mahu hanya membuang sampah yang mungkin dihantar oleh pengguna berniat jahat melalui borang kepada penyata yang disediakan parametrik, saya sentiasa mahu melakukan pemeriksaan kewarasan saya sendiri terlebih dahulu dan dalam beberapa kes ini mungkin tersilap di sisi berhati-hati dan hanya pilih untuk membatalkan operasi Pangkalan Data sepenuhnya.

    Dengan cara itu DB saya tidak tersumbat dengan kenyataan tidak selamat yang dibuat selamat - ia tidak tersumbat yang mana lebih baik.

    Keselamatan dalam lapisan - sanitasi dan pengesahan masih perlu dipertimbangkan dalam setiap situasi SEBELUM menggunakan pernyataan yang disediakan.

    Di samping itu sejauh yang saya boleh membaca ke dalam dokumen rasmi
    ==============================================

    "Meloloskan diri dan suntikan SQL

    Pembolehubah terikat dihantar ke pelayan secara berasingan daripada pertanyaan dan dengan itu tidak boleh mengganggunya. Pelayan menggunakan nilai ini secara langsung pada titik pelaksanaan, selepas templat pernyataan dihuraikan. Parameter terikat tidak perlu dilepaskan kerana ia tidak pernah digantikan ke dalam rentetan pertanyaan secara langsung"

    Itu menunjukkan kepada saya bahawa bahaya dielakkan dalam dalaman dengan pengendalian alternatif bukan dengan pembatalan.

    Ini bermakna projek besar dengan penukaran yang tidak lengkap kepada penyata yang disediakan, kod warisan di bahagian berlainan organisasi atau pelayan yang bercakap antara satu sama lain semuanya boleh menyampaikan berita buruk daripada lokasi atau situasi kebal kepada yang tidak kebal.

    Selagi sanitasi dilakukan dengan cekap tanpa menanggung risiko tambahan maka secara peribadi saya akan tetap menggunakan lapisan sanitasi tertentu dan kemudian memanggil kenyataan yang disediakan.

    Jadi pada asasnya saya menggali jauh ke dalam bidang MySQL dan PHP... khususnya langkah keselamatan yang perlu saya ambil apabila berurusan dengan pangkalan data dan input borang. Setakat ini saya mendapati perkara berikut sangat disyorkan:

    1. Penyata Disediakan
    2. Menggunakan _real_escape_string()
    3. TIDAK menggunakan petikan ajaib kerana ia mengelirukan pangkalan data dan akhirnya memberi anda perkara seperti "Anda tidak memanggilnya...".

    Ini semua hebat dan saya memerhatikannya. Walau bagaimanapun, saya tertanya-tanya sama ada aksara seperti tanda dolar [$], tanda peratus [%] dan mungkin yang lain harus dilepaskan. Bolehkah pertanyaan itu mentafsirkan tanda dolar sebagai pembolehubah PHP? Bagaimana pula dengan sintaks LIKE yang saya dengar menggunakan simbol % atau pun kad bebas? Kenyataan yang disediakan secara teknikal harus menjaga semua ini, tetapi saya hanya mahu berada di pihak yang selamat dan memastikan saya menyelesaikan segala-galanya dengan betul. Dalam kes di mana saya terlupa menggunakan kenyataan yang disediakan atau hanya mengabaikannya, saya berharap barisan pertahanan kedua ini dapat memberitahu saya bahawa saya boleh menghilangkan rasa pening.

    Inilah yang saya gunakan pada masa ini untuk melarikan diri:

    Function escape($connection, $data)( $new_data = trim($data); $new_data = i_real_escape_string($connection, $new_data); $new_data = addcslashes($new_data, "%_$"); $new_data = htmlspecialchars ($data_baharu, ENT_NOQUOTES);

    Jadi adakah ini betul? Adakah saya melakukan sesuatu yang sangat salah? Sila ambil perhatian bahawa apabila mengembalikan data pangkalan data saya perlu mengalih keluar garis miring ke belakang sebelum aksara $,% dan _.

    Adakah saya melakukan sesuatu yang sangat salah?

    Pertama tentang penyelidikan anda.

    Kenyataan yang disediakan - satu-satunya perkara indah yang anda temui.

    Walaupun menggunakan mysqli_real_escape_string (dengan mengandaikan anda menggunakan pernyataan yang disediakan) adalah tidak berguna dan memudaratkan(mencipta hasil yang anda perhatikan sendiri: "Anda dipanggil tidak...").

    Dan Petikan Ajaib telah lama dialih keluar daripada bahasa - oleh itu tidak bernilai apa-apa.

    Jadi walaupun kebanyakan premis awal anda jelas salah.

    Sekarang kepada soalan anda.

    Bolehkah pertanyaan itu mentafsirkan tanda dolar sebagai pembolehubah PHP?

    Bagaimana pula dengan sintaks LIKE yang saya dengar menggunakan simbol % atau pun kad bebas?

    Ya, awak dengar betul. Tujuan sebenar pengendali LIKE adalah untuk melakukan carian corak. Melumpuhkan aksara ini dalam LIKE tidak akan masuk akal sedikit pun.

    Setiap kali anda akan menggunakan operator LIKE, anda mesti memutuskan watak khusus yang mana untuk digunakan dan yang tidak dibenarkan. Anda tidak boleh menggunakan penyelesaian sekali sahaja. Apatah lagi dalam semua interaksi mysql lain tanda % tidak mempunyai makna khusus.

    Kenyataan yang disediakan harus secara teknikal mengurus semua ini

    Pernyataan yang disediakan tidak ada kaitan dengan tanda $ atau %. Penyataan yang disediakan merujuk kepada suntikan SQL, tetapi tiada watak boleh menyebabkannya (anda tidak boleh memanggil "suntikan" sebagai pengendali LIKE yang digunakan dengan betul, boleh?).

    Akhirnya, ke bahagian yang paling teruk.

    Sekiranya anda terlupa menggunakan kenyataan yang disediakan atau hanya mengabaikan untuk mengikutinya,

    tiada apa yang akan menyelamatkan anda.

    Dan paling sedikit bantuan adalah daripada fungsi yang anda bangunkan.

    ringkaskan.

    1. Buang ciri ini.
    2. guna pengisi * untuk mewakili setiap pembolehubah individu dalam pertanyaan.
    3. Escape % dan _ aksara dalam input hanya jika ia akan digunakan dalam operator LIKE dan anda tidak mahu ia ditafsirkan.
    4. Gunakan htmlspecialchars() untuk output, bukan input mysql.

    *baca kenyataan yang disediakan jika istilah ini tidak anda kenali.

    Anda tidak perlu mengelak tanda dolar. MySQL tidak melihat watak ini secara khusus, dan PHP hanya mengenalinya dalam kod sumber, bukan dalam nilai rentetan (melainkan anda memanggil eval pada rentetan, tetapi itu adalah cacing cacing yang lain).

    Anda hanya perlu melepaskan % dan _ jika anda menggunakan input pengguna sebagai hujah LIKE dan anda tidak mahu pengguna boleh menggunakan kad bebas. Ini mungkin berlaku jika anda sedang memproses borang carian. Anda tidak perlu menggunakannya semasa menyimpan dalam pangkalan data.

    Anda tidak perlu menggunakan htmlspecialchars apabila mengakses pangkalan data. Ini hanya boleh digunakan apabila memaparkan data kepada pengguna pada halaman HTML untuk mengelakkan suntikan XSS.

    Bergantung pada data apa dan untuk apa ia digunakan.

    Jika anda mendapati bahawa penyataan luar kotak lalai PHP terlalu besar dan kompleks, saya cadangkan anda melihat beberapa kelas yang tersedia di github untuk memberi anda idea tentang pertanyaan yang dipermudahkan.

    Contoh memasukkan pertanyaan dengan kelas ini

    $data = Array ("log masuk" => "pentadbir", "aktif" => benar, "Nama pertama" => "John", "Nama Akhir" => "Doe", "kata laluan" => $db->func( "SHA1(?)",Array ("kata laluan rahsia+garam")), // kata laluan = SHA1("kata laluan rahsia+garam") "createdAt" => $db->now(), // createdAt = NOW() " tamat tempoh" => $db->now("+1Y") // tamat tempoh = NOW() + selang 1 tahun // Selang yang disokong [s]saat, [m]inute, [h]jam, [d]hari, [M]bulan, [Y]telinga); $id = $db->insert("pengguna", $data); if ($id) echo "pengguna telah dicipta. Id=" . $id; else echo "insert failed: " . $db->getLastError();