Apa alasan teknis mengapa seseorang tidak boleh menggunakan mysql_*
fungsi? (misalnya mysql_query()
, mysql_connect()
atau mysql_real_escape_string()
)?
Mengapa saya harus menggunakan sesuatu yang lain walaupun mereka bekerja di situs saya?
Jika mereka tidak berfungsi di situs saya, mengapa saya mendapatkan kesalahan seperti
Peringatan: mysql_connect (): Tidak ada file atau direktori tersebut
Jawaban:
Ekstensi MySQL:
Karena sudah tidak digunakan lagi, menggunakannya akan membuat kode Anda kurang menjadi bukti di masa mendatang.
Kurangnya dukungan untuk pernyataan yang disiapkan sangat penting karena memberikan metode yang lebih jelas, tidak rawan kesalahan untuk keluar dan mengutip data eksternal daripada secara manual menghindarinya dengan pemanggilan fungsi yang terpisah.
Lihat perbandingan ekstensi SQL .
sumber
PHP menawarkan tiga API berbeda untuk terhubung ke MySQL. Ini adalah
mysql
(dihapus pada PHP 7)mysqli
,, danPDO
ekstensi.The
mysql_*
fungsi yang digunakan untuk menjadi sangat populer, tetapi penggunaannya tidak dianjurkan lagi. Tim dokumentasi sedang membahas situasi keamanan basis data, dan mendidik pengguna untuk menjauh dari ekstensi ext / mysql yang umum digunakan adalah bagian dari ini (lihat php.internals: deprecating ext / mysql ).Dan tim pengembang PHP kemudian telah mengambil keputusan untuk menghasilkan
E_DEPRECATED
kesalahan ketika pengguna terhubung ke MySQL, apakah melaluimysql_connect()
,mysql_pconnect()
atau fungsi koneksi implisit yang dibangunext/mysql
.ext/mysql
secara resmi tidak digunakan lagi pada PHP 5.5 dan telah dihapus pada PHP 7 .Lihat Kotak Merah?
Ketika Anda melanjutkan
mysql_*
halaman manual fungsi, Anda melihat kotak merah, menjelaskannya tidak boleh digunakan lagi.Mengapa
Beranjak dari
ext/mysql
tidak hanya tentang keamanan, tetapi juga tentang memiliki akses ke semua fitur dari database MySQL.ext/mysql
dibangun untuk MySQL 3.23 dan hanya mendapat sedikit tambahan sejak saat itu sementara sebagian besar menjaga kompatibilitas dengan versi lama ini yang membuat kode sedikit lebih sulit untuk dipelihara. Fitur yang hilang yang tidak didukung olehext/mysql
termasuk: ( dari manual PHP ).Alasan untuk tidak menggunakan
mysql_*
fungsi :Poin di atas dikutip dari jawaban Quentin
Kurangnya dukungan untuk pernyataan yang disiapkan sangat penting karena memberikan metode yang lebih jelas, lebih tidak rentan untuk melarikan diri dan mengutip data eksternal daripada secara manual menghindarinya dengan pemanggilan fungsi yang terpisah.
Lihat perbandingan ekstensi SQL .
Menekan peringatan penghentian
Saat kode sedang dikonversi ke
MySQLi
/PDO
,E_DEPRECATED
kesalahan bisa ditekan dengan menetapkanerror_reporting
di php.ini untuk dikecualikanE_DEPRECATED:
Perhatikan bahwa ini juga akan menyembunyikan peringatan penghentian lainnya , yang, bagaimanapun, mungkin untuk hal-hal selain MySQL. ( dari manual PHP )
Artikel PDO vs MySQLi: Yang Harus Anda Gunakan? oleh Dejan Marjanovic akan membantu Anda memilih.
Dan cara yang lebih baik adalah
PDO
, dan sekarang saya sedang menulisPDO
tutorial sederhana .Tutorial PDO sederhana dan pendek
Q. Pertanyaan pertama yang ada di pikiran saya adalah: apa itu `PDO`?
A. " PDO - Objek Data PHP - adalah lapisan akses basis data yang menyediakan metode seragam untuk akses ke banyak basis data."
Menghubungkan ke MySQL
Dengan
mysql_*
fungsi atau kita dapat mengatakannya dengan cara lama (tidak digunakan lagi dalam PHP 5.5 dan lebih tinggi)Dengan
PDO
: Yang perlu Anda lakukan adalah membuatPDO
objek baru . Konstruktor menerima parameter untuk menentukanPDO
konstruktor sumber database kebanyakan mengambil empat parameter yaituDSN
(nama sumber data) dan secara opsionalusername
,password
.Di sini saya pikir Anda sudah terbiasa dengan semua kecuali
DSN
; ini baru dalamPDO
. ADSN
pada dasarnya adalah serangkaian opsi yang memberi tahuPDO
driver mana yang harus digunakan, dan detail koneksi. Untuk referensi lebih lanjut, periksa PDO MySQL DSN .Catatan: Anda juga dapat menggunakan
charset=UTF-8
, tetapi terkadang menyebabkan kesalahan, jadi lebih baik digunakanutf8
.Jika ada kesalahan koneksi, itu akan melempar
PDOException
objek yang bisa ditangkap untuk menanganiException
lebih lanjut.Bacaan baik : Koneksi dan Manajemen koneksi ¶
Anda juga dapat mengirimkan beberapa opsi driver sebagai larik ke parameter keempat. Saya sarankan meneruskan parameter yang menempatkan
PDO
ke mode pengecualian. Karena beberapaPDO
pengemudi tidak mendukung pernyataan persiapan asli, jadiPDO
lakukan persaingan persiapan. Ini juga memungkinkan Anda mengaktifkan emulasi ini secara manual. Untuk menggunakan pernyataan yang disiapkan di sisi server asli, Anda harus mengaturnya secara eksplisitfalse
.Yang lain adalah untuk menonaktifkan persiapan emulasi yang diaktifkan di
MySQL
driver secara default, tetapi mempersiapkan emulasi harus dimatikan untuk digunakanPDO
dengan aman.Saya nanti akan menjelaskan mengapa persiapan persaingan harus dimatikan. Untuk menemukan alasan silakan periksa posting ini .
Ini hanya dapat digunakan jika Anda menggunakan versi lama
MySQL
yang tidak saya rekomendasikan.Di bawah ini adalah contoh bagaimana Anda dapat melakukannya:
Bisakah kita menetapkan atribut setelah konstruksi PDO?
Ya , kami juga dapat menetapkan beberapa atribut setelah konstruksi PDO dengan
setAttribute
metode:Penanganan Kesalahan
Menangani kesalahan jauh lebih mudah
PDO
daripadamysql_*
.Praktik umum saat menggunakan
mysql_*
adalah:OR die()
bukan cara yang baik untuk menangani kesalahan karena kita tidak dapat menangani masalahnyadie
. Ini hanya akan mengakhiri skrip tiba-tiba dan kemudian mengulangi kesalahan ke layar yang biasanya Anda TIDAK ingin ditampilkan kepada pengguna akhir Anda, dan biarkan peretas berdarah menemukan skema Anda. Sebagai alternatif, nilai-nilaimysql_*
fungsi yang kembali sering dapat digunakan bersama dengan mysql_error () untuk menangani kesalahan.PDO
menawarkan solusi yang lebih baik: pengecualian. Apa pun yang kita lakukanPDO
harus dibungkus dengantry
-catch
blok. Kita dapat memaksaPDO
ke salah satu dari tiga mode kesalahan dengan mengatur atribut mode kesalahan. Tiga mode penanganan kesalahan ada di bawah ini.PDO::ERRMODE_SILENT
. Itu hanya mengatur kode kesalahan dan bertindak hampir sama dengan dimysql_*
mana Anda harus memeriksa setiap hasil dan kemudian melihat$db->errorInfo();
untuk mendapatkan detail kesalahan.PDO::ERRMODE_WARNING
AngkatE_WARNING
. (Peringatan run-time (kesalahan non-fatal). Eksekusi skrip tidak dihentikan.)PDO::ERRMODE_EXCEPTION
: Melempar pengecualian. Ini merupakan kesalahan yang diangkat oleh PDO. Anda tidak boleh melemparPDOException
dari kode Anda sendiri. Lihat Pengecualian untuk informasi lebih lanjut tentang pengecualian dalam PHP. Kerjanya sangat miripor die(mysql_error());
, ketika tidak tertangkap. Tapi tidak seperti ituor die()
,PDOException
dapat ditangkap dan ditangani dengan anggun jika Anda memilih untuk melakukannya.Bagus dibaca :
Suka:
Dan Anda dapat membungkusnya dalam
try
-catch
, seperti di bawah ini:Anda tidak harus menangani
try
-catch
sekarang. Anda dapat menangkapnya kapan saja sesuai, tetapi saya sangat menyarankan Anda untuk menggunakantry
-catch
. Juga mungkin lebih masuk akal untuk menangkapnya di luar fungsi yang memanggilPDO
barang:Juga, Anda dapat menangani
or die()
atau kita dapat mengatakan sukamysql_*
, tetapi itu akan sangat bervariasi. Anda dapat menyembunyikan pesan kesalahan berbahaya dalam produksi dengan memutardisplay_errors off
dan hanya membaca log kesalahan Anda.Sekarang, setelah membaca semua hal di atas, Anda mungkin berpikir: apa sih adalah bahwa ketika saya hanya ingin memulai bersandar sederhana
SELECT
,INSERT
,UPDATE
, atauDELETE
pernyataan? Jangan khawatir, ini dia:Memilih Data
Jadi yang Anda lakukan
mysql_*
adalah:Sekarang
PDO
, Anda dapat melakukan ini seperti:Atau
Catatan : Jika Anda menggunakan metode seperti di bawah ini (
query()
), metode ini mengembalikanPDOStatement
objek. Jadi jika Anda ingin mengambil hasilnya, gunakan seperti di atas.Dalam Data PDO, diperoleh melalui
->fetch()
metode penanganan pernyataan Anda. Sebelum menelepon mengambil, pendekatan terbaik adalah memberi tahu PDO bagaimana Anda ingin data diambil. Pada bagian di bawah ini saya menjelaskan ini.Ambil Mode
Perhatikan penggunaan
PDO::FETCH_ASSOC
dalamfetch()
danfetchAll()
kode di atas. Ini memberitahuPDO
untuk mengembalikan baris sebagai array asosiatif dengan nama bidang sebagai kunci. Ada banyak mode pengambilan lainnya yang akan saya jelaskan satu per satu.Pertama-tama, saya jelaskan bagaimana memilih mode pengambilan:
Di atas, saya telah menggunakan
fetch()
. Anda juga bisa menggunakan:PDOStatement::fetchAll()
- Mengembalikan array yang berisi semua baris set hasilPDOStatement::fetchColumn()
- Mengembalikan satu kolom dari baris berikutnya dari kumpulan hasilPDOStatement::fetchObject()
- Mengambil baris berikutnya dan mengembalikannya sebagai objek.PDOStatement::setFetchMode()
- Tetapkan mode pengambilan default untuk pernyataan iniSekarang saya datang untuk mengambil mode:
PDO::FETCH_ASSOC
: mengembalikan array yang diindeks oleh nama kolom seperti yang dikembalikan di set hasil AndaPDO::FETCH_BOTH
(default): mengembalikan array yang diindeks dengan nama kolom dan nomor kolom yang diindeks 0 seperti yang dikembalikan di set hasil AndaBahkan ada lebih banyak pilihan! Baca tentang semuanya di
PDOStatement
Ambil dokumentasi. .Mendapatkan jumlah baris :
Alih-alih menggunakan
mysql_num_rows
untuk mendapatkan jumlah baris yang dikembalikan, Anda bisa mendapatkanPDOStatement
dan melakukannyarowCount()
, seperti:Mendapatkan ID yang Terakhir Dimasukkan
Sisipkan dan Perbarui atau Hapus pernyataan
Apa yang kami lakukan dalam
mysql_*
fungsi adalah:Dan di pdo, hal yang sama dapat dilakukan oleh:
Dalam kueri di atas,
PDO::exec
jalankan pernyataan SQL dan kembalikan jumlah baris yang terpengaruh.Sisipkan dan hapus akan dibahas nanti.
Metode di atas hanya berguna ketika Anda tidak menggunakan variabel dalam kueri. Tetapi ketika Anda perlu menggunakan variabel dalam kueri, jangan pernah mencoba seperti di atas dan di sana untuk pernyataan disiapkan atau pernyataan parameter .
Pernyataan Disiapkan
Q. Apa pernyataan yang disiapkan dan mengapa saya membutuhkannya?
A. Pernyataan yang disiapkan adalah pernyataan SQL pra-kompilasi yang dapat dieksekusi beberapa kali dengan mengirimkan hanya data ke server.
Alur kerja khas menggunakan pernyataan yang disiapkan adalah sebagai berikut ( dikutip dari Wikipedia tiga poin 3 ):
Mempersiapkan : Templat pernyataan dibuat oleh aplikasi dan dikirim ke sistem manajemen basis data (DBMS). Nilai-nilai tertentu dibiarkan tidak ditentukan, disebut parameter, placeholder atau variabel terikat (berlabel di
?
bawah):INSERT INTO PRODUCT (name, price) VALUES (?, ?)
DBMS mem-parsing, mengkompilasi, dan melakukan optimisasi kueri pada template pernyataan, dan menyimpan hasilnya tanpa menjalankannya.
1.00
untuk parameter kedua.Anda bisa menggunakan pernyataan yang disiapkan dengan menyertakan placeholder dalam SQL Anda. Pada dasarnya ada tiga yang tanpa penampung (jangan coba ini dengan variabel di atas satu), satu dengan penampung yang tidak disebutkan namanya, dan satu dengan penampung yang disebutkan.
P. Jadi sekarang, apa yang disebut placeholder dan bagaimana cara menggunakannya?
A. Penampung yang ditunjuk. Gunakan nama deskriptif yang didahului dengan titik dua, bukan tanda tanya. Kami tidak peduli dengan posisi / urutan nilai pada pemegang nama tempat:
bindParam(parameter,variable,data_type,length,driver_options)
Anda juga dapat mengikat menggunakan array eksekusi:
Fitur bagus lain untuk
OOP
teman adalah placeholder yang diberi nama memiliki kemampuan untuk menyisipkan objek langsung ke database Anda, dengan asumsi properti cocok dengan bidang yang disebutkan. Sebagai contoh:Q. Jadi sekarang, apa saja placeholder tanpa nama dan bagaimana cara menggunakannya?
A. Mari kita punya contoh:
dan
Di atas, Anda dapat melihat itu
?
alih - alih nama seperti pada tempat tempat nama. Sekarang dalam contoh pertama, kami menetapkan variabel ke berbagai placeholder ($stmt->bindValue(1, $name, PDO::PARAM_STR);
). Kemudian, kami memberikan nilai kepada placeholder tersebut dan menjalankan pernyataan tersebut. Dalam contoh kedua, elemen array pertama pergi ke yang pertama?
dan yang kedua ke yang kedua?
.CATATAN : Di placeholder tanpa nama, kita harus menjaga urutan elemen yang tepat dalam array yang kita lewati ke
PDOStatement::execute()
metode.SELECT
,INSERT
,UPDATE
,DELETE
Disiapkan querySELECT
:INSERT
:DELETE
:UPDATE
:CATATAN:
Namun
PDO
dan / atauMySQLi
tidak sepenuhnya aman. Periksa jawabannya. Apakah pernyataan yang disiapkan PDO cukup untuk mencegah injeksi SQL? oleh ircmaxell . Juga, saya mengutip beberapa bagian dari jawabannya:sumber
IN (...) construct
.function throwEx() { throw new Exception("You did selected not existng db"); } mysql_select_db("nonexistdb") or throwEx();
Ini bekerja untuk melempar pengecualian.Doesn't support non-blocking, asynchronous queries
sebagai alasan untuk tidak menggunakan mysql_ - Anda juga harus mencantumkan itu sebagai alasan untuk tidak menggunakan PDO, karena PDO juga tidak mendukungnya. (tapi MySQLi mendukungnya)Pertama, mari kita mulai dengan komentar standar yang kami berikan kepada semua orang:
Mari kita selesaikan ini, kalimat demi kalimat, dan jelaskan:
Mereka tidak lagi dipertahankan, dan secara resmi ditinggalkan
Ini berarti bahwa komunitas PHP secara bertahap menjatuhkan dukungan untuk fungsi-fungsi yang sangat lama ini. Mereka sepertinya tidak ada dalam versi PHP yang akan datang (baru-baru ini)! Terus menggunakan fungsi-fungsi ini dapat merusak kode Anda di masa depan (tidak begitu).
BARU! - ext / mysql sekarang secara resmi tidak digunakan lagi pada PHP 5.5!
Baru! ext / mysql telah dihapus dalam PHP 7 .
Sebagai gantinya, Anda harus mempelajari pernyataan yang sudah disiapkan
mysql_*
ekstensi tidak mendukung pernyataan yang disiapkan , yang (antara lain) merupakan penanggulangan yang sangat efektif terhadap SQL Injection . Ini memperbaiki kerentanan yang sangat serius dalam aplikasi yang bergantung pada MySQL yang memungkinkan penyerang mendapatkan akses ke skrip Anda dan melakukan kueri yang mungkin pada database Anda.Untuk informasi lebih lanjut, lihat Bagaimana saya bisa mencegah injeksi SQL dalam PHP?
Lihat Kotak Merah?
Ketika Anda pergi ke
mysql
halaman manual fungsi, Anda melihat kotak merah, menjelaskan itu tidak boleh digunakan lagi.Gunakan PDO atau MySQLi
Ada alternatif yang lebih baik, lebih kuat dan dibangun dengan baik, PDO - PHP Database Object , yang menawarkan pendekatan OOP lengkap untuk interaksi basis data, dan MySQLi , yang merupakan peningkatan spesifik MySQL.
sumber
IN (...) construct
.Kemudahan penggunaan
Alasan analitik dan sintetik telah disebutkan. Untuk pendatang baru ada insentif yang lebih signifikan untuk berhenti menggunakan fungsi tanggal mysql_.
API basis data kontemporer lebih mudah digunakan.
Sebagian besar parameter terikat yang dapat menyederhanakan kode. Dan dengan tutorial yang sangat baik (seperti yang terlihat di atas) transisi ke PDO tidak terlalu sulit.
Menulis ulang basis kode yang lebih besar sekaligus namun membutuhkan waktu. Raison d'être untuk alternatif perantara ini:
Fungsi pdo_ * Setara menggantikan
mysql_ *Menggunakan < pdo_mysql.php > Anda dapat beralih dari fungsi mysql_ lama dengan upaya minimal . Ini menambahkan
pdo_
pembungkus fungsi yang menggantikanmysql_
rekan - rekan mereka .Cukup di setiap skrip doa yang harus berinteraksi dengan database.
include_once(
"pdo_mysql.php"
);
Hapus
awalan fungsi di mana - mana dan ganti denganmysql_
pdo_
.mysql_
connect()
menjadipdo_
connect()
mysql_
query()
menjadipdo_
query()
mysql_
num_rows()
menjadipdo_
num_rows()
mysql_
insert_id()
menjadipdo_
insert_id()
mysql_
fetch_array()
menjadipdo_
fetch_array()
mysql_
fetch_assoc()
menjadipdo_
fetch_assoc()
mysql_
real_escape_string()
menjadipdo_
real_escape_string()
Kode Anda akan berfungsi sama dan sebagian besar masih terlihat sama:
Dll.
Kode Anda menggunakan PDO.
Sekarang saatnya untuk benar-benar menggunakannya .
Parameter batas bisa mudah digunakan
Anda hanya perlu API yang kurang sulit.
pdo_query()
menambahkan dukungan yang sangat mudah untuk parameter terikat. Mengonversi kode lama sangatlah mudah:Pindahkan variabel Anda dari string SQL.
pdo_query()
.?
sebagai pengganti di mana variabel sebelumnya.'
tanda kutip tunggal yang sebelumnya berisi nilai / variabel string.Keuntungannya menjadi lebih jelas untuk kode yang lebih panjang.
Seringkali variabel string tidak hanya diinterpolasi ke dalam SQL, tetapi digabungkan dengan lolosnya panggilan di antaranya.
Dengan
?
placeholder yang diterapkan, Anda tidak perlu repot dengan itu:Ingat bahwa pdo_ * masih memungkinkan salah satu atau .
Hanya saja, jangan melarikan diri variabel dan mengikatnya dalam permintaan yang sama.
:named
daftar placeholder nanti.Lebih penting lagi, Anda dapat memberikan variabel $ _REQUEST [] dengan aman di balik kueri apa pun. Ketika
<form>
bidang yang dikirimkan cocok dengan struktur basis data persisnya itu bahkan lebih pendek:Begitu banyak kesederhanaan. Tetapi mari kita kembali ke beberapa saran penulisan ulang dan alasan teknis tentang mengapa Anda mungkin ingin menyingkirkannya
dan melarikan diri.mysql_
Perbaiki atau hapus oldschool
sanitize()
fungsiSetelah Anda mengkonversi semua
panggilanmysql_
pdo_query
dengan params terikat, hapus semua berlebihanpdo_real_escape_string
panggilan yang .Secara khusus Anda harus memperbaiki setiap
sanitize
atauclean
ataufilterThis
atauclean_data
fungsi seperti yang diiklankan oleh tutorial tanggal dalam satu bentuk atau yang lain:Bug paling mencolok di sini adalah kurangnya dokumentasi. Lebih penting lagi urutan penyaringan berada dalam urutan yang salah.
Urutan yang benar seharusnya: ditinggalkan
stripslashes
sebagai panggilan terdalam, kemudiantrim
, setelah itustrip_tags
,htmlentities
untuk konteks output, dan hanya yang terakhir_escape_string
karena aplikasinya harus secara langsung mendahului SQL intersparsing.Tapi sebagai langkah pertama singkirkan saja
_real_escape_string
panggilan itu.Anda mungkin harus menjaga sisa
sanitize()
fungsi Anda untuk saat ini jika basis data dan aliran aplikasi Anda mengharapkan string HTML-context-safe. Tambahkan komentar bahwa itu hanya berlaku HTML untuk selanjutnya.Penanganan string / nilai didelegasikan ke PDO dan pernyataan parameternya.
Jika ada disebutkan
stripslashes()
dalam fungsi sanitasi Anda, itu mungkin menunjukkan pengawasan tingkat yang lebih tinggi.Itu biasanya ada di sana untuk membatalkan kerusakan (melarikan diri ganda) dari usang
magic_quotes
. Namun yang terbaik tetap terpusat , bukan string demi string.Gunakan salah satu pendekatan pembalikan userland . Kemudian menghapus
stripslashes()
dalamsanitize
fungsi.Bagaimana perbedaan pernyataan yang dipersiapkan
Ketika Anda mengacak variabel string ke dalam query SQL, itu tidak hanya menjadi lebih rumit untuk Anda ikuti. Ini juga merupakan upaya tambahan bagi MySQL untuk memisahkan kode dan data lagi.
Suntikan SQL hanya ketika data berdarah ke dalam kode konteks . Server basis data tidak dapat menemukan tempat di mana PHP awalnya menempel variabel di antara klausa permintaan.
Dengan parameter terikat Anda memisahkan kode SQL dan nilai konteks SQL dalam kode PHP Anda. Tapi itu tidak terseret lagi di belakang layar (kecuali dengan PDO :: EMULATE_PREPARES). Basis data Anda menerima perintah SQL yang tidak divariasikan dan nilai variabel 1: 1.
Sementara jawaban ini menekankan bahwa Anda harus memperhatikan keuntungan keterbacaan dari menjatuhkan
. Kadang-kadang juga ada keuntungan kinerja (INSERT berulang dengan nilai yang hanya berbeda) karena pemisahan data / kode yang terlihat dan teknis ini.mysql_
Hati-hati bahwa pengikatan parameter masih bukan solusi satu atap yang ajaib terhadap semua injeksi SQL. Ini menangani penggunaan paling umum untuk data / nilai. Tetapi tidak dapat membuat daftar putih nama kolom / pengidentifikasi tabel, membantu konstruksi klausa dinamis, atau hanya daftar nilai array sederhana.
Penggunaan PDO hybrid
pdo_*
Fungsi wrapper ini membuat stop-gap API yang ramah coding. (Ini cukup banyak apa yangMYSQLI
bisa terjadi jika bukan karena pergeseran tanda tangan fungsi istimewa). Mereka juga mengekspos PDO nyata di sebagian besar waktu.Menulis ulang tidak harus berhenti menggunakan nama fungsi pdo_ baru. Anda dapat melakukan transisi satu per satu setiap pdo_query () menjadi panggilan $ pdo-> prep () -> execute ().
Namun yang terbaik adalah mulai menyederhanakan lagi. Misalnya hasil umum mengambil:
Dapat diganti hanya dengan iteach foreach:
Atau lebih baik lagi pencarian array langsung dan lengkap:
Anda akan mendapatkan lebih banyak peringatan yang bermanfaat daripada PDO atau mysql_ biasanya memberikan setelah permintaan gagal.
Pilihan lain
Jadi ini mudah-mudahan memvisualisasikan beberapa alasan praktis dan jalan yang baik untuk dijatuhkan
.mysql_
Hanya beralih ke pdotidak cukup memotongnya.
pdo_query()
juga hanya sebuah frontend ke atasnya.Kecuali jika Anda juga memperkenalkan pengikatan parameter atau dapat menggunakan sesuatu yang lain dari API yang lebih bagus, itu adalah pengalihan tanpa tujuan. Saya harap itu digambarkan cukup sederhana untuk tidak memajukan keputusasaan bagi pendatang baru. (Pendidikan biasanya bekerja lebih baik daripada larangan.)
Meskipun memenuhi syarat untuk kategori hal-hal-yang-bisa-mungkin-mungkin-paling sederhana, itu juga masih merupakan kode yang sangat eksperimental. Saya baru saja menulisnya selama akhir pekan. Namun ada banyak alternatif. Hanya google untuk abstraksi basis data PHP dan jelajahi sedikit. Selalu ada dan akan ada banyak perpustakaan yang sangat baik untuk tugas-tugas seperti itu.
Jika Anda ingin menyederhanakan interaksi basis data Anda lebih lanjut, pembuat peta seperti Paris / Idiorm patut dicoba. Sama seperti tidak ada yang menggunakan DOM hambar dalam JavaScript lagi, Anda tidak perlu mengasuh antarmuka basis data mentah saat ini.
sumber
pdo_query("INSERT INTO pages VALUES (?,?,?,?,?)", $_POST);
fungsi - yaitu:pdo_query("INSERT INTO users VALUES (?, ?, ?), $_POST); $_POST = array( 'username' => 'lawl', 'password' => '123', 'is_admin' => 'true');
pdo_real_escape_string()
<- Apakah ini bahkan fungsi nyata, saya tidak dapat menemukan dokumentasi untuk itu? Silakan kirim sumber untuk ini.The
mysql_
fungsi:sumber
mysqli_
mysql_*
fungsi adalah shell ke dalam fungsi mysqlnd untuk versi PHP yang lebih baru. Jadi, bahkan jika perpustakaan klien lama tidak dipelihara lagi, mysqlnd dipertahankan :)Berbicara tentang alasan teknis , hanya ada beberapa, sangat spesifik dan jarang digunakan. Kemungkinan besar Anda tidak akan pernah menggunakannya dalam hidup Anda.
Mungkin saya terlalu bodoh, tetapi saya tidak pernah memiliki kesempatan untuk menggunakannya seperti itu
Jika Anda membutuhkannya - ini tidak diragukan lagi alasan teknis untuk beralih dari ekstensi mysql ke arah sesuatu yang lebih gaya dan tampak modern.
Namun demikian, ada juga beberapa masalah non-teknis, yang dapat membuat pengalaman Anda sedikit lebih sulit
Masalah terakhir ini adalah masalah.
Tapi, menurut saya, solusi yang diusulkan juga tidak lebih baik.
Sepertinya saya terlalu idealis mimpi bahwa semua pengguna PHP akan belajar bagaimana menangani pertanyaan SQL dengan benar sekaligus. Kemungkinan besar mereka hanya akan mengubah mysql_ * menjadi mysqli_ * secara mekanis, meninggalkan pendekatan yang sama . Terutama karena mysqli membuat pernyataan siap pakai yang luar biasa menyakitkan dan merepotkan.
Belum lagi pernyataan asli yang disiapkan tidak cukup untuk melindungi dari suntikan SQL, dan baik mysqli maupun PDO tidak menawarkan solusi.
Jadi, daripada melawan perpanjangan yang jujur ini, saya lebih suka melawan praktik yang salah dan mendidik orang dengan cara yang benar.
Juga, ada beberapa alasan yang salah atau tidak signifikan, seperti
mysql_query("CALL my_proc");
selama berabad-abad)Yang terakhir adalah poin yang menarik. Meskipun mysql ext tidak mendukung pernyataan yang disiapkan asli , mereka tidak diperlukan untuk keselamatan. Kami dapat dengan mudah memalsukan pernyataan yang disiapkan menggunakan placeholder yang ditangani secara manual (seperti halnya PDO):
voila , semuanya parameter dan aman.
Tapi oke, jika Anda tidak suka kotak merah di manual, masalah pilihan muncul: mysqli atau PDO?
Jawabannya adalah sebagai berikut:
Jika, seperti sebagian besar orang PHP, Anda menggunakan panggilan API mentah langsung dalam kode aplikasi (yang pada dasarnya merupakan praktik yang salah) - PDO adalah satu-satunya pilihan , karena ekstensi ini berpura-pura bukan hanya API tetapi lebih semi-DAL, masih belum lengkap tetapi menawarkan banyak fitur penting, dengan dua di antaranya membuat PDO sangat berbeda dari mysqli:
Jadi, jika Anda adalah pengguna PHP biasa dan ingin menghemat banyak sakit kepala saat menggunakan pernyataan asli yang disiapkan, PDO - lagi - adalah satu-satunya pilihan.
Namun, PDO bukan peluru perak juga dan memiliki kesulitan.
Jadi, saya menulis solusi untuk semua perangkap umum dan kasus kompleks di wiki tag PDO
Namun demikian, semua orang yang berbicara tentang ekstensi selalu kehilangan 2 fakta penting tentang Mysqli dan PDO:
Pernyataan yang disiapkan bukan peluru perak . Ada pengidentifikasi dinamis yang tidak dapat diikat menggunakan pernyataan yang disiapkan. Ada kueri dinamis dengan sejumlah parameter yang tidak diketahui yang membuat kueri membangun tugas yang sulit.
Baik fungsi mysqli_ * maupun PDO seharusnya tidak muncul dalam kode aplikasi.
Seharusnya ada lapisan abstraksi antara mereka dan kode aplikasi, yang akan melakukan semua pekerjaan kotor yaitu mengikat, mengulang, menangani kesalahan, dll. Di dalam, membuat kode aplikasi KERING dan bersih. Khusus untuk kasus kompleks seperti bangunan kueri dinamis.
Jadi, hanya beralih ke PDO atau mysqli tidak cukup. Kita harus menggunakan ORM, atau pembuat kueri, atau kelas abstraksi basis data apa pun alih-alih memanggil fungsi API mentah dalam kode mereka.
Dan sebaliknya - jika Anda memiliki lapisan abstraksi antara kode aplikasi Anda dan API mysql - sebenarnya tidak masalah mesin mana yang digunakan. Anda dapat menggunakan mysql ext hingga tidak lagi digunakan lagi dan kemudian dengan mudah menulis ulang kelas abstraksi Anda ke mesin lain, setelah semua kode aplikasi tetap utuh.
Berikut adalah beberapa contoh berdasarkan kelas safemysql saya untuk menunjukkan bagaimana seharusnya kelas abstraksi seperti itu:
Bandingkan satu baris ini dengan jumlah kode yang Anda perlukan dengan PDO .
Kemudian bandingkan dengan jumlah kode gila yang Anda perlukan dengan pernyataan mentah Mysqli. Perhatikan bahwa penanganan kesalahan, pembuatan profil, pembuatan log permintaan sudah terpasang dan berjalan.
Bandingkan dengan sisipan PDO biasa, ketika setiap nama bidang diulangi enam hingga sepuluh kali - di semua placeholder, binding, dan definisi kueri yang banyak ini.
Contoh lain:
Anda tidak dapat menemukan contoh bagi PDO untuk menangani kasus praktis semacam itu.
Dan itu akan terlalu bertele-tele dan kemungkinan besar tidak aman.
Jadi, sekali lagi - ini bukan hanya driver mentah yang harus menjadi perhatian Anda tetapi kelas abstraksi, berguna tidak hanya untuk contoh-contoh konyol dari manual pemula tetapi untuk memecahkan masalah kehidupan nyata apa pun.
sumber
mysql_*
membuat kerentanan sangat mudah didapat. Karena PHP digunakan oleh banyak pengguna pemula,mysql_*
secara aktif berbahaya dalam praktiknya, meskipun secara teori dapat digunakan tanpa hambatan.everything is parameterized and safe
- itu mungkin parameter, tetapi fungsi Anda tidak menggunakan nyata pernyataan siap.Not under active development
hanya untuk '0,01%' yang dibuat-buat? Jika Anda membangun sesuatu dengan fungsi diam ini, perbarui versi mysql Anda dalam setahun dan berakhir dengan sistem yang tidak berfungsi, saya yakin ada banyak sekali orang yang tiba-tiba dalam '0,01%' itu. Saya akan mengatakan itudeprecated
dannot under active development
terkait erat. Anda dapat mengatakan bahwa "tidak ada alasan [layak]" untuk itu, tetapi kenyataannya adalah bahwa ketika ditawarkan pilihan antara opsi,no active development
hampir sama buruknya dengan yangdeprecated
saya katakan?Ada banyak alasan, tetapi mungkin yang paling penting adalah bahwa fungsi-fungsi tersebut mendorong praktik pemrograman yang tidak aman karena tidak mendukung pernyataan yang disiapkan. Pernyataan yang disiapkan membantu mencegah serangan injeksi SQL.
Saat menggunakan
mysql_*
fungsi, Anda harus ingat untuk menjalankan parameter yang disediakan penggunamysql_real_escape_string()
. Jika Anda lupa hanya di satu tempat atau jika Anda hanya melarikan diri dari sebagian input, basis data Anda mungkin akan diserang.Menggunakan pernyataan yang sudah disiapkan di
PDO
ataumysqli
akan membuatnya sehingga kesalahan pemrograman semacam ini lebih sulit untuk dibuat.sumber
Karena (di antara alasan lain) itu jauh lebih sulit untuk memastikan input data disanitasi. Jika Anda menggunakan kueri parametrized, seperti yang dilakukan dengan PDO atau mysqli, Anda sepenuhnya dapat menghindari risiko.
Sebagai contoh, seseorang dapat digunakan
"enhzflep); drop table users"
sebagai nama pengguna. Fungsi lama akan memungkinkan mengeksekusi beberapa pernyataan per kueri, jadi sesuatu seperti bugger jahat itu dapat menghapus seluruh tabel.Jika seseorang menggunakan PDO dari mysqli, nama pengguna akan berakhir
"enhzflep); drop table users"
.Lihat bobby-tables.com .
sumber
The old functions will allow executing of multiple statements per query
- tidak, mereka tidak akan. Injeksi semacam itu tidak dimungkinkan dengan ext / mysql - satu-satunya cara injeksi semacam ini dimungkinkan dengan PHP dan MySQL adalah ketika menggunakan MySQLi danmysqli_multi_query()
fungsinya. Jenis injeksi yang dimungkinkan dengan ext / mysql dan unescaped string adalah hal-hal seperti' OR '1' = '1
mengekstrak data dari database yang tidak dimaksudkan untuk diakses. Dalam situasi tertentu dimungkinkan untuk menyuntikkan sub kueri, namun masih tidak mungkin untuk memodifikasi database dengan cara ini.Jawaban ini ditulis untuk menunjukkan betapa sepelenya untuk mem-bypass kode validasi pengguna PHP yang ditulis dengan buruk, bagaimana (dan menggunakan apa) serangan ini bekerja dan bagaimana cara mengganti fungsi MySQL lama dengan pernyataan yang disiapkan dengan aman - dan pada dasarnya, mengapa pengguna StackOverflow (mungkin dengan banyak perwakilan) menggonggong pada pengguna baru yang mengajukan pertanyaan untuk meningkatkan kode mereka.
Pertama, jangan ragu untuk membuat tes ini database mysql (Saya telah memanggil persiapan tambang):
Setelah itu selesai, kita bisa pindah ke kode PHP kita.
Mari kita asumsikan skrip berikut adalah proses verifikasi untuk admin di situs web (disederhanakan tetapi berfungsi jika Anda menyalin dan menggunakannya untuk pengujian):
Tampak cukup sah pada pandangan pertama.
Pengguna harus memasukkan login dan kata sandi, bukan?
Cemerlang, tidak masuk sebagai berikut:
dan kirimkan.
Outputnya adalah sebagai berikut:
Super! Bekerja seperti yang diharapkan, sekarang mari kita coba nama pengguna dan kata sandi yang sebenarnya:
Luar biasa! Hai semuanya, kode dengan benar memverifikasi admin. Itu sempurna!
Yah, tidak juga. Katakanlah pengguna adalah orang kecil yang pintar. Katakanlah orang itu adalah saya.
Masukkan yang berikut ini:
Dan hasilnya adalah:
Selamat, Anda baru saja mengizinkan saya memasukkan bagian admin super-dilindungi Anda saja dengan saya memasukkan nama pengguna palsu dan kata sandi palsu. Serius, jika Anda tidak percaya, buat database dengan kode yang saya berikan, dan jalankan kode PHP ini - yang sekilas BENAR-BENAR tampaknya memverifikasi nama pengguna dan kata sandi dengan baik.
Jadi, sebagai jawaban, ITULAH MENGAPA ANDA DIAMBIL DI.
Jadi, mari kita lihat apa yang salah, dan mengapa saya baru saja masuk ke gua super-admin-satunya-kelelawar Anda. Saya menebak dan berasumsi bahwa Anda tidak berhati-hati dengan input Anda dan hanya meneruskannya ke database secara langsung. Saya membuat input dengan cara yang akan MENGUBAH kueri yang sebenarnya Anda jalankan. Jadi, apa yang seharusnya, dan apa akhirnya?
Itulah kueri, tetapi ketika kami mengganti variabel dengan input aktual yang kami gunakan, kami mendapatkan yang berikut:
Lihat bagaimana saya membuat "kata sandi" saya sehingga akan terlebih dahulu menutup kutipan tunggal di sekitar kata sandi, kemudian memperkenalkan perbandingan yang sama sekali baru? Kemudian hanya untuk keamanan, saya menambahkan "string" lain sehingga kutipan tunggal akan ditutup seperti yang diharapkan dalam kode yang awalnya kami miliki.
Namun, ini bukan tentang orang yang berteriak pada Anda sekarang, ini tentang menunjukkan kepada Anda bagaimana membuat kode Anda lebih aman.
Oke, jadi apa yang salah, dan bagaimana kita memperbaikinya?
Ini adalah serangan injeksi SQL klasik. Salah satu yang paling sederhana dalam hal ini. Pada skala vektor serangan, ini adalah balita yang menyerang tank - dan menang.
Jadi, bagaimana kami melindungi bagian admin suci Anda dan menjadikannya bagus dan aman? Hal pertama yang harus dilakukan adalah berhenti menggunakan
mysql_*
fungsi-fungsi yang benar-benar tua dan usang itu . Saya tahu, Anda mengikuti tutorial yang Anda temukan online dan berhasil, tetapi sudah tua, sudah usang dan dalam waktu beberapa menit, saya baru saja melewatinya tanpa banyak berkeringat.Sekarang, Anda memiliki opsi yang lebih baik untuk menggunakan mysqli_ atau PDO . Saya pribadi penggemar berat PDO, jadi saya akan menggunakan PDO di sisa jawaban ini. Ada pro dan kontra, tetapi secara pribadi saya menemukan bahwa pro jauh melebihi penipu. Ini portabel di beberapa mesin basis data - apakah Anda menggunakan MySQL atau Oracle atau hanya tentang apa saja - hanya dengan mengubah string koneksi, ia memiliki semua fitur mewah yang ingin Anda gunakan dan itu bagus dan bersih. Saya suka bersih.
Sekarang, mari kita lihat kode itu lagi, kali ini ditulis menggunakan objek PDO:
Perbedaan utama adalah bahwa tidak ada lagi
mysql_*
fungsi. Semuanya dilakukan melalui objek PDO, kedua, menggunakan pernyataan yang disiapkan. Sekarang, apa pernyataan yang sudah Anda minta sebelumnya? Ini adalah cara untuk memberi tahu basis data sebelum menjalankan kueri, apa kueri yang akan kita jalankan. Dalam hal ini, kami memberi tahu database: "Hai, saya akan menjalankan pernyataan pilih yang menginginkan id, userid dan pass dari tabel pengguna di mana userid adalah variabel dan pass juga merupakan variabel.".Kemudian, dalam pernyataan eksekusi, kami melewatkan basis data array dengan semua variabel yang sekarang diharapkan.
Hasilnya luar biasa. Mari kita coba kombinasi nama pengguna dan kata sandi itu sebelumnya:
Pengguna tidak diverifikasi. Luar biasa.
Bagaimana tentang:
Oh, saya hanya sedikit bersemangat, itu berhasil: Cek lulus. Kami memiliki admin yang diverifikasi!
Sekarang, mari kita coba data yang akan dimasukkan oleh orang pintar untuk mencoba melewati sistem verifikasi kecil kami:
Kali ini, kami mendapatkan yang berikut:
Inilah sebabnya mengapa Anda diteriaki ketika memposting pertanyaan - itu karena orang dapat melihat bahwa kode Anda dapat dilewati tanpa mencoba. Tolong, gunakan pertanyaan dan jawaban ini untuk meningkatkan kode Anda, untuk membuatnya lebih aman dan untuk menggunakan fungsi yang saat ini.
Terakhir, ini bukan untuk mengatakan bahwa ini adalah kode SEMPURNA. Ada banyak hal lain yang dapat Anda lakukan untuk memperbaikinya, menggunakan kata sandi hash misalnya, memastikan bahwa ketika Anda menyimpan informasi sensitif dalam database, Anda tidak menyimpannya dalam teks biasa, memiliki beberapa tingkat verifikasi - tetapi sungguh, jika Anda hanya mengubah kode rawan injeksi lama Anda ke ini, Anda akan BAIK sepanjang jalan untuk menulis kode yang baik - dan fakta bahwa Anda sudah sejauh ini dan masih membaca memberi saya rasa harapan bahwa Anda tidak hanya akan menerapkan jenis ini kode saat menulis situs web dan aplikasi Anda, tetapi Anda mungkin pergi dan meneliti hal-hal lain yang baru saja saya sebutkan - dan banyak lagi. Tulis kode terbaik yang Anda bisa, bukan kode paling dasar yang nyaris tidak berfungsi.
sumber
mysql_*
itu sendiri tidak aman, tetapi mempromosikan kode tidak aman melalui tutorial yang buruk dan kurangnya pernyataan yang tepat untuk menyiapkan API.Ekstensi MySQL adalah yang tertua dari ketiganya dan merupakan cara asli yang digunakan pengembang untuk berkomunikasi dengan MySQL. Ekstensi ini sekarang tidak digunakan lagi karena dua alternatif lain karena perbaikan yang dibuat dalam rilis baru PHP dan MySQL.
MySQLi adalah ekstensi 'ditingkatkan' untuk bekerja dengan database MySQL. Ini mengambil keuntungan dari fitur-fitur yang tersedia dalam versi yang lebih baru dari server MySQL, memperlihatkan antarmuka berorientasi fungsi dan berorientasi objek ke pengembang dan melakukan beberapa hal bagus lainnya.
PDO menawarkan API yang menggabungkan sebagian besar fungsi yang sebelumnya tersebar di ekstensi akses basis data utama, yaitu MySQL, PostgreSQL, SQLite, MSSQL, dll. Antarmuka mengekspos objek tingkat tinggi bagi programmer untuk bekerja dengan koneksi basis data, kueri dan set hasil, dan driver tingkat rendah melakukan komunikasi dan penanganan sumber daya dengan server database. Banyak diskusi dan pekerjaan masuk ke PDO dan itu dianggap metode yang tepat untuk bekerja dengan database dalam kode profesional dan modern.
sumber
Saya menemukan jawaban di atas sangat panjang, jadi untuk meringkas:
Sumber: ikhtisar MySQLi
Seperti dijelaskan dalam jawaban di atas, alternatif untuk mysql adalah mysqli dan PDO (Objek Data PHP).
Baik MySQLi dan PDO diperkenalkan dalam PHP 5.0, sedangkan MySQL diperkenalkan sebelum PHP 3.0. Poin yang perlu diperhatikan adalah bahwa MySQL sudah termasuk dalam PHP5.x meskipun sudah tidak digunakan lagi dalam versi yang lebih baru.
sumber
Mungkin untuk mendefinisikan hampir semua
mysql_*
fungsi menggunakan mysqli atau PDO. Cukup sertakan di atas aplikasi PHP lama Anda, dan itu akan berfungsi pada PHP7. Solusi saya di sini .sumber
Fungsi-fungsi yang mirip dengan ini
mysql_connect()
,mysql_query()
ketik adalah versi PHP sebelumnya yaitu (PHP 4) fungsi dan sekarang tidak digunakan.Ini digantikan oleh
mysqli_connect()
,mysqli_query()
sama di PHP5 terbaru.Inilah alasan di balik kesalahan tersebut.
sumber
MySQL tidak lagi digunakan dalam PHP 5.5.0, dan dihapus dalam PHP 7.0.0. Untuk aplikasi besar dan lama, ini sulit untuk mencari dan mengganti setiap fungsi.
Kita dapat menggunakan fungsi MySQL dengan membuat fungsi wrapper untuk masing-masing di bawah ini menjalankan kode. Klik disini
sumber
fungsi mysql_ * sudah usang (pada PHP 5.5 ) mengingat fakta bahwa fungsi dan struktur kode yang lebih baik dikembangkan. Fakta bahwa fungsi tersebut sudah usang berarti bahwa tidak ada lagi upaya yang akan dilakukan untuk meningkatkannya dalam hal kinerja dan keamanan, yang berarti lebih sedikit bukti di masa depan. .
Jika Anda membutuhkan lebih banyak alasan:
sumber