Pertanyaan Mengapa saya tidak menggunakan fungsi mysql_ * di PHP?


Apa alasan teknis mengapa orang tidak seharusnya menggunakan mysql_* fungsi? (misalnya. mysql_query(), mysql_connect() atau mysql_real_escape_string())?

Mengapa saya harus menggunakan yang lain bahkan jika mereka bekerja di situs saya?

Jika mereka tidak bekerja di situs saya, mengapa saya mendapatkan kesalahan seperti itu

Peringatan: mysql_connect (): Tidak ada file atau direktori semacam itu


2194
2017-10-12 13:18


asal


Jawaban:


Ekstensi MySQL:

  • Tidak dalam pengembangan aktif
  • Aku s secara resmi tidak lagi digunakan per PHP 5.5 (dirilis Juni 2013).
  • Telah dihapus sepenuhnya per PHP 7.0 (dirilis Desember 2015)
    • Ini berarti bahwa pada 31 Desember 2018 itu tidak akan ada dalam versi PHP yang didukung. Saat ini, hanya mendapat keamanan pembaruan.
  • Tidak memiliki antarmuka OO
  • Tidak mendukung:
    • Non-blocking, pertanyaan asynchronous
    • Pernyataan yang disiapkan atau kueri parameter
    • Prosedur yang tersimpan
    • Banyak Pernyataan
    • Transaksi
    • Metode otentikasi kata sandi "baru" (diaktifkan secara default di MySQL 5.6; diperlukan pada 5.7)
    • Semua fungsi di MySQL 5.1

Karena tidak lagi digunakan, menggunakannya membuat kode Anda kurang bukti di masa depan.

Kurangnya dukungan untuk pernyataan siap sangat penting karena mereka menyediakan lebih jelas, kurang kesalahan rawan metode melarikan diri dan mengutip data eksternal daripada secara manual melarikan diri dengan panggilan fungsi terpisah.

Lihat perbandingan ekstensi SQL.


1808
2018-01-01 11:52



PHP menawarkan tiga API berbeda untuk terhubung ke MySQL. Ini adalah mysql(dihapus pada PHP 7), mysqli, dan PDO ekstensi.

Itu mysql_* fungsi yang digunakan sangat populer, tetapi penggunaannya tidak didorong lagi. Tim dokumentasi sedang mendiskusikan situasi keamanan database, dan mendidik pengguna untuk menjauh dari ekstensi ext / mysql yang umum digunakan adalah bagian dari ini (periksa php.internals: mencabut ext / mysql).

Dan tim pengembang PHP berikutnya telah mengambil keputusan untuk menghasilkan E_DEPRECATED kesalahan ketika pengguna terhubung ke MySQL, baik melalui mysql_connect(), mysql_pconnect() atau fungsionalitas koneksi implisit dibangun ke dalam ext/mysql.

ext/mysql adalah tidak berlaku lagi sejak PHP 5.5 dan telah dihapus pada PHP 7.

Lihat Kotak Merah?

Ketika Anda melakukan apapun mysql_* halaman manual fungsi, Anda melihat kotak merah, menjelaskannya tidak boleh digunakan lagi.

Mengapa


Pindah 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 dipertahankan. Tidak ada fitur yang tidak didukung oleh ext/mysql termasuk: (dari manual PHP).

Alasan untuk tidak digunakan mysql_* fungsi:

  • Tidak dalam pengembangan aktif
  • Dihapus pada PHP 7
  • Tidak memiliki antarmuka OO
  • Tidak mendukung kueri non-pemblokiran, asynchronous
  • Tidak mendukung pernyataan siap atau pertanyaan parameter
  • Tidak mendukung prosedur yang tersimpan
  • Tidak mendukung banyak pernyataan
  • Tidak mendukung transaksi
  • Tidak mendukung semua fungsi di MySQL 5.1

Di atas titik yang dikutip dari jawaban Quentin

Kurangnya dukungan untuk pernyataan siap sangat penting karena mereka menyediakan metode rawan kesalahan yang lebih jelas, kurang melarikan diri dan mengutip data eksternal daripada secara manual melarikan diri dengan panggilan fungsi terpisah.

Lihat perbandingan ekstensi SQL.


Menekan peringatan penghentian

Sementara kode sedang dikonversi ke MySQLi/PDO, E_DEPRECATED kesalahan dapat ditekan dengan pengaturan error_reporting di php.ini untuk dikecualikan E_DEPRECATED:

error_reporting = E_ALL ^ E_DEPRECATED

Perhatikan bahwa ini juga akan disembunyikan peringatan penghentian lainnya, yang, bagaimanapun, mungkin untuk hal-hal selain dari 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 saya sekarang menulis yang sederhana PDO tutorial.


Tutorial PDO yang simpel dan singkat


Q. Pertanyaan pertama dalam pikiran saya adalah: apakah `PDO`?

SEBUAH. "PDO - Objek Data PHP - adalah lapisan akses database yang menyediakan metode akses yang seragam ke beberapa basis data. ”

alt text


Menghubungkan ke MySQL

Dengan mysql_* berfungsi atau kita bisa mengatakannya dengan cara lama (tidak lagi digunakan di PHP 5.5 dan di atas)

$link = mysql_connect('localhost', 'user', 'pass');
mysql_select_db('testdb', $link);
mysql_set_charset('UTF-8', $link);

Dengan PDO: Yang perlu Anda lakukan hanyalah membuat yang baru PDO obyek. Konstruktor menerima parameter untuk menentukan sumber database PDOKonstruktor kebanyakan mengambil empat parameter yang DSN (nama sumber data) dan secara opsional username, password.

Di sini saya pikir Anda terbiasa dengan semua kecuali DSN; ini baru di PDO. SEBUAH DSN pada dasarnya adalah serangkaian opsi yang memberi tahu PDO driver mana yang digunakan, dan rincian koneksi. Untuk referensi lebih lanjut, periksa PDO MySQL DSN.

$db = new PDO('mysql:host=localhost;dbname=testdb;charset=utf8', 'username', 'password');

catatan: Anda juga bisa menggunakan charset=UTF-8, tapi terkadang itu menyebabkan kesalahan, jadi lebih baik digunakan utf8.

Jika ada kesalahan koneksi, itu akan melempar a PDOException objek yang dapat ditangkap untuk ditangani Exception lebih lanjut.

Bacaan yang bagus: Koneksi dan manajemen Koneksi ¶ 

Anda juga dapat mengirimkan beberapa opsi driver sebagai larik ke parameter keempat. Saya merekomendasikan melewatkan parameter yang menempatkan PDO ke mode pengecualian. Karena beberapa PDO driver tidak mendukung pernyataan asli disiapkan, jadi PDO melakukan emulasi persiapan. Ini juga memungkinkan Anda mengaktifkan emulasi ini secara manual. Untuk menggunakan pernyataan siap sisi server bawaan, Anda harus secara eksplisit mengaturnya false.

Yang lainnya adalah untuk mematikan mempersiapkan emulasi yang diaktifkan di MySQLdriver secara default, tetapi persiapkan emulasi harus dimatikan untuk digunakan PDO dengan aman.

Saya akan menjelaskan mengapa mempersiapkan emulasi harus dimatikan. Untuk menemukan alasannya 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:

$db = new PDO('mysql:host=localhost;dbname=testdb;charset=UTF-8', 
              'username', 
              'password',
              array(PDO::ATTR_EMULATE_PREPARES => false,
              PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION));

Bisakah kita menetapkan atribut setelah konstruksi PDO?

iya nih, kita juga bisa mengatur beberapa atribut setelah PDO dibangun dengan setAttribute metode:

$db = new PDO('mysql:host=localhost;dbname=testdb;charset=UTF-8', 
              'username', 
              'password');
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$db->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);

Penanganan Kesalahan


Penanganan kesalahan jauh lebih mudah di PDO dari mysql_*.

Praktik yang umum saat menggunakan mysql_* aku s:

//Connected to MySQL
$result = mysql_query("SELECT * FROM table", $link) or die(mysql_error($link));

OR die() bukan cara yang baik untuk menangani kesalahan karena kami tidak dapat menangani hal tersebut die. Ini hanya akan mengakhiri skrip secara tiba-tiba dan kemudian menggemakan kesalahan ke layar yang biasanya Anda TIDAK ingin menunjukkan kepada pengguna akhir Anda, dan biarkan peretas berdarah menemukan skema Anda. Bergantian, nilai kembali dari mysql_* fungsi sering dapat digunakan bersama mysql_error () untuk menangani kesalahan.

PDO menawarkan solusi yang lebih baik: pengecualian. Apapun yang kita lakukan PDO harus dibungkus dalam try-catch blok. Kami bisa memaksa PDO ke salah satu dari tiga mode kesalahan dengan mengatur atribut mode kesalahan. Tiga mode penanganan kesalahan di bawah.

  • PDO::ERRMODE_SILENT. Ini hanya mengatur kode kesalahan dan bertindak hampir sama dengan mysql_* di mana Anda harus memeriksa setiap hasil dan kemudian melihat $db->errorInfo(); untuk mendapatkan detail kesalahan.
  • PDO::ERRMODE_WARNING Menaikkan E_WARNING. (Peringatan run-time (kesalahan non-fatal). Eksekusi skrip tidak dihentikan.)
  • PDO::ERRMODE_EXCEPTION: Lempar pengecualian. Ini merupakan kesalahan yang ditimbulkan oleh PDO. Anda seharusnya tidak melempar a PDOException dari kode Anda sendiri. Lihat Pengecualian untuk informasi lebih lanjut tentang pengecualian dalam PHP. Itu bertindak sangat mirip or die(mysql_error());, ketika tidak tertangkap. Tapi tidak seperti itu or die(), yang PDOException dapat ditangkap dan ditangani dengan anggun jika Anda memilih untuk melakukannya.

Bacaan yang bagus:

Seperti:

$stmt->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT );
$stmt->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING );
$stmt->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION );

Dan Anda bisa membungkusnya try-catch, seperti di bawah ini:

try {
    //Connect as appropriate as above
    $db->query('hi'); //Invalid query!
} 
catch (PDOException $ex) {
    echo "An Error occured!"; //User friendly message/message you want to show to user
    some_logging_function($ex->getMessage());
}

Anda tidak harus menangani try-catch sekarang juga. Anda dapat menangkapnya kapan saja, tetapi saya sangat menyarankan Anda untuk menggunakannya try-catch. Juga mungkin lebih masuk akal untuk menangkapnya di luar fungsi yang memanggil PDO barang:

function data_fun($db) {
    $stmt = $db->query("SELECT * FROM table");
    return $stmt->fetchAll(PDO::FETCH_ASSOC);
}

//Then later
try {
    data_fun($db);
}
catch(PDOException $ex) {
    //Here you can handle error and show message/perform action you want.
}

Juga, Anda bisa mengatasinya dengan or die() atau kita bisa bilang suka mysql_*, tapi itu akan sangat bervariasi. Anda dapat menyembunyikan pesan kesalahan berbahaya dalam produksi dengan memutar display_errors off dan hanya membaca log kesalahan Anda.

Sekarang, setelah membaca semua hal di atas, Anda mungkin berpikir: apa sih itu ketika saya hanya ingin mulai bersandar sederhana SELECT, INSERT, UPDATE, atau DELETE pernyataan? Jangan khawatir, ini dia:


Memilih Data

PDO select image

Jadi apa yang Anda lakukan mysql_* aku s:

<?php
$result = mysql_query('SELECT * from table') or die(mysql_error());

$num_rows = mysql_num_rows($result);

while($row = mysql_fetch_assoc($result)) {
    echo $row['field1'];
}

Sekarang di PDO, Anda dapat melakukan ini seperti:

<?php
$stmt = $db->query('SELECT * FROM table');

while($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
    echo $row['field1'];
}

Atau

<?php
$stmt = $db->query('SELECT * FROM table');
$results = $stmt->fetchAll(PDO::FETCH_ASSOC);

//Use $results

Catatan: Jika Anda menggunakan metode seperti di bawah ini (query()), metode ini mengembalikan a PDOStatement obyek. Jadi jika Anda ingin mengambil hasilnya, gunakan seperti di atas.

<?php
foreach($db->query('SELECT * FROM table') as $row) {
    echo $row['field1'];
}

Dalam PDO Data, diperoleh melalui ->fetch(), metode penanganan pernyataan Anda. Sebelum menelepon mengambil, pendekatan terbaik adalah memberi tahu PDO bagaimana Anda ingin data diambil. Di bagian bawah saya menjelaskan ini.

Ambil Mode

Perhatikan penggunaan PDO::FETCH_ASSOC dalam fetch() dan fetchAll() kode di atas. Ini memberitahu PDO untuk mengembalikan baris sebagai array asosiatif dengan nama field sebagai kunci. Ada banyak mode pengambilan lain yang juga akan saya jelaskan satu per satu.

Pertama-tama, saya menjelaskan cara memilih mode pengambilan:

 $stmt->fetch(PDO::FETCH_ASSOC)

Di atas, saya telah menggunakan fetch(). Anda juga bisa menggunakan:

Sekarang saya datang untuk mengambil mode:

  • PDO::FETCH_ASSOC: mengembalikan array yang diindeks oleh nama kolom sebagai dikembalikan dalam kumpulan hasil Anda
  • PDO::FETCH_BOTH (default): mengembalikan array yang diindeks oleh kedua nama kolom dan nomor kolom yang diindeks 0 sebagai dikembalikan dalam kumpulan hasil Anda

Bahkan ada lebih banyak pilihan! Baca tentang semuanya PDOStatement Ambil dokumentasi..

Mendapatkan jumlah baris:

Alih-alih menggunakan mysql_num_rows untuk mendapatkan jumlah baris yang dikembalikan, Anda bisa mendapatkan PDOStatement dan lakukan rowCount(), seperti:

<?php
$stmt = $db->query('SELECT * FROM table');
$row_count = $stmt->rowCount();
echo $row_count.' rows selected';

Mendapatkan ID Terakhir Disisipkan

<?php
$result = $db->exec("INSERT INTO table(firstname, lastname) VAULES('John', 'Doe')");
$insertId = $db->lastInsertId();

Masukkan dan Perbarui atau Hapus pernyataan

Insert and update PDO image

Apa yang kami lakukan di mysql_* fungsi adalah:

<?php
$results = mysql_query("UPDATE table SET field='value'") or die(mysql_error());
echo mysql_affected_rows($result);

Dan dalam pdo, hal yang sama ini dapat dilakukan dengan:

<?php
$affected_rows = $db->exec("UPDATE table SET field='value'");
echo $affected_rows;

Di 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 query. Tetapi ketika Anda perlu menggunakan variabel dalam kueri, jangan pernah mencoba seperti di atas dan di sana untuk pernyataan siap atau pernyataan parameter aku s.


Laporan yang disiapkan

Q. Apa yang dimaksud dengan pernyataan siap dan mengapa saya membutuhkannya?
SEBUAH. Pernyataan yang disiapkan adalah pernyataan SQL yang dikompilasi sebelumnya yang dapat dijalankan beberapa kali dengan mengirimkan hanya data ke server.

Alur kerja khas menggunakan pernyataan yang disiapkan adalah sebagai berikut (dikutip dari Wikipedia tiga 3 poin):

  1. Mempersiapkan: Template pernyataan dibuat oleh aplikasi dan dikirim ke sistem manajemen basis data (DBMS). Nilai-nilai tertentu tidak ditentukan, disebut parameter, placeholder atau variabel bind (berlabel ? di bawah):

    INSERT INTO PRODUCT (name, price) VALUES (?, ?)

  2. The DBMS mem-parsing, mengkompilasi, dan melakukan optimasi query pada template pernyataan, dan menyimpan hasilnya tanpa mengeksekusinya.

  3. Menjalankan: Di lain waktu, aplikasi menyediakan (atau mengikat) nilai untuk parameter, dan DBMS mengeksekusi pernyataan (kemungkinan mengembalikan hasil). Aplikasi dapat mengeksekusi pernyataan sebanyak yang diinginkan dengan nilai yang berbeda. Dalam contoh ini, mungkin menyediakan 'Roti' untuk parameter pertama dan 1.00untuk parameter kedua.

Anda dapat menggunakan pernyataan siap dengan menyertakan placeholder di SQL Anda. Pada dasarnya ada tiga yang tanpa placeholder (jangan coba ini dengan variabel di atas satu), satu dengan placeholder yang tidak disebutkan namanya, dan satu dengan placeholder bernama.

Q. Jadi sekarang, apa yang disebut placeholder dan bagaimana cara menggunakannya?
SEBUAH. Placeholder bernama. Gunakan nama deskriptif yang diawali dengan tanda titik dua, alih-alih tanda tanya. Kami tidak peduli tentang posisi / urutan nilai di tempat pemegang nama:

 $stmt->bindParam(':bla', $bla);

bindParam(parameter,variable,data_type,length,driver_options)

Anda juga dapat mengikat menggunakan larik eksekusi juga:

<?php
$stmt = $db->prepare("SELECT * FROM table WHERE id=:id AND name=:name");
$stmt->execute(array(':name' => $name, ':id' => $id));
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);

Fitur bagus lainnya untuk OOP teman adalah tempat penampung bernama memiliki kemampuan untuk memasukkan objek langsung ke database Anda, dengan asumsi properti cocok dengan bidang bernama. Sebagai contoh:

class person {
    public $name;
    public $add;
    function __construct($a,$b) {
        $this->name = $a;
        $this->add = $b;
    }

}
$demo = new person('john','29 bla district');
$stmt = $db->prepare("INSERT INTO table (name, add) value (:name, :add)");
$stmt->execute((array)$demo);

Q. Jadi sekarang, apa yang disebut placeholder tidak bernama dan bagaimana cara menggunakannya?
SEBUAH. Mari kita ambil contoh:

<?php
$stmt = $db->prepare("INSERT INTO folks (name, add) values (?, ?)");
$stmt->bindValue(1, $name, PDO::PARAM_STR);
$stmt->bindValue(2, $add, PDO::PARAM_STR);
$stmt->execute();

dan

$stmt = $db->prepare("INSERT INTO folks (name, add) values (?, ?)");
$stmt->execute(array('john', '29 bla district'));

Di atas, Anda dapat melihatnya ? bukannya nama seperti di tempat pemegang nama. Sekarang di contoh pertama, kami menetapkan variabel ke berbagai placeholder ($stmt->bindValue(1, $name, PDO::PARAM_STR);). Kemudian, kami menetapkan nilai ke placeholder dan menjalankan pernyataan. Pada contoh kedua, elemen array pertama mengarah ke yang pertama ? dan yang kedua sampai yang kedua ?.

CATATAN: Di placeholder tidak bernama kita harus memperhatikan urutan elemen yang tepat dalam larik yang akan kita lewati PDOStatement::execute() metode.


SELECT, INSERT, UPDATE, DELETE pertanyaan yang disiapkan

  1. SELECT:

    $stmt = $db->prepare("SELECT * FROM table WHERE id=:id AND name=:name");
    $stmt->execute(array(':name' => $name, ':id' => $id));
    $rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
    
  2. INSERT:

    $stmt = $db->prepare("INSERT INTO table(field1,field2) VALUES(:field1,:field2)");
    $stmt->execute(array(':field1' => $field1, ':field2' => $field2));
    $affected_rows = $stmt->rowCount();
    
  3. DELETE:

    $stmt = $db->prepare("DELETE FROM table WHERE id=:id");
    $stmt->bindValue(':id', $id, PDO::PARAM_STR);
    $stmt->execute();
    $affected_rows = $stmt->rowCount();
    
  4. UPDATE:

    $stmt = $db->prepare("UPDATE table SET name=? WHERE id=?");
    $stmt->execute(array($name, $id));
    $affected_rows = $stmt->rowCount();
    

CATATAN:

Namun PDO dan / atau MySQLi tidak sepenuhnya aman. Periksa jawabannya Apakah PDO menyiapkan pernyataan yang cukup untuk mencegah injeksi SQL? oleh ircmaxell. Juga, saya mengutip beberapa bagian dari jawabannya:

$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
$pdo->query('SET NAMES GBK');
$stmt = $pdo->prepare("SELECT * FROM test WHERE name = ? LIMIT 1");
$stmt->execute(array(chr(0xbf) . chr(0x27) . " OR 1=1 /*"));

1160
2017-10-12 13:28



Pertama, mari kita mulai dengan komentar standar yang kami berikan kepada semua orang:

Tolong, jangan gunakan mysql_* berfungsi dalam kode baru. Mereka tidak lagi dipertahankan dan secara resmi tidak digunakan lagi. Lihat kotak merah? Belajar tentang pernyataan siap sebagai gantinya, dan gunakan PDO atau MySQLi - artikel ini akan membantu Anda memutuskan yang mana. Jika Anda memilih PDO, di sini adalah tutorial yang bagus.

Mari kita bahas ini, kalimat demi kalimat, dan jelaskan:

  • Mereka tidak lagi dipertahankan, dan secara resmi tidak lagi digunakan

    Ini berarti bahwa komunitas PHP secara bertahap kehilangan dukungan untuk fungsi-fungsi yang sangat lama ini. Mereka mungkin tidak ada di masa depan (versi terbaru) dari PHP! Terus menggunakan fungsi-fungsi ini dapat merusak kode Anda di masa depan (tidak demikian) jauh.

    BARU! - ext / mysql sekarang tidak berlaku lagi sejak PHP 5.5!

    Lebih baru! ext / mysql telah dihapus di PHP 7.

  • Sebaliknya, Anda harus belajar tentang pernyataan yang disiapkan

    mysql_* ekstensi tidak mendukung pernyataan siap, yang (antara lain) merupakan tindakan balasan yang sangat efektif Injeksi SQL. Ini memperbaiki kerentanan yang sangat serius dalam aplikasi tergantung MySQL yang memungkinkan penyerang untuk mendapatkan akses ke skrip Anda dan melakukan setiap pertanyaan yang mungkin di database Anda.

    Untuk informasi lebih lanjut, lihat Bagaimana saya bisa mencegah injeksi SQL di PHP?

  • Lihat Kotak Merah?

    Ketika Anda pergi ke manapun mysql halaman manual fungsi, Anda melihat kotak merah, menjelaskannya tidak boleh digunakan lagi.

  • Gunakan PDO atau MySQLi

    Ada alternatif yang lebih baik, lebih kuat dan dibangun dengan baik, PDO - Objek Database PHP, yang menawarkan pendekatan OOP lengkap untuk interaksi database, dan MySQLi, yang merupakan perbaikan khusus MySQL.


279
2017-12-24 23:30



Kemudahan penggunaan

Alasan analitik dan sintetis sudah disebutkan. Untuk pendatang baru ada insentif yang lebih signifikan untuk berhenti menggunakan fungsi mysql_ tertanggal.

API database kontemporer hanya lebih mudah menggunakan.

Ini sebagian besar parameter terikat yang dapat menyederhanakan kode. Dan dengan tutorial yang bagus (seperti yang terlihat di atas) transisi ke PDO tidak terlalu sulit.

Menulis ulang basis kode yang lebih besar sekaligus membutuhkan waktu. Raison d'être untuk alternatif menengah ini:

Fungsi pdo_ * setara sebagai pengganti mysql_ *

Menggunakan <pdo_mysql.php> Anda dapat beralih dari fungsi mysql_ lama dengan sedikit usaha. Ia menambahkan pdo_ pembungkus fungsi yang menggantikannya mysql_ rekan-rekan.

  1. Secara sederhana include_once("pdo_mysql.php"); di setiap skrip permintaan yang harus berinteraksi dengan database.

  2. Hapus mysql_ awalan fungsi dimana mana dan menggantinya dengan pdo_.

    • mysql_connect() menjadi pdo_connect()
    • mysql_query() menjadi pdo_query()
    • mysql_num_rows() menjadi pdo_num_rows()
    • mysql_insert_id() menjadi pdo_insert_id()
    • mysql_fetch_array() menjadi pdo_fetch_array()
    • mysql_fetch_assoc() menjadi pdo_fetch_assoc()
    • mysql_real_escape_string() menjadi pdo_real_escape_string()
    • dan seterusnya... 

       
  3. Kode Anda akan bekerja sama dan sebagian besar masih terlihat sama:

    include_once("pdo_mysql.php"); 
    
    pdo_connect("localhost", "usrABC", "pw1234567");
    pdo_select_db("test");
    
    $result = pdo_query("SELECT title, html FROM pages");  
    
    while ($row = pdo_fetch_assoc($result)) {
        print "$row[title] - $row[html]";
    }
    

Et voila.
Kode Anda menggunakan PDO.
Sekarang saatnya untuk benar-benar memanfaatkan saya t.

Parameter terikat dapat mudah digunakan

Anda hanya membutuhkan API yang kurang berat.

pdo_query() menambahkan dukungan yang sangat lancar untuk parameter yang terikat. Mengubah kode lama sangat mudah:

Pindahkan variabel Anda dari string SQL.

  • Tambahkan sebagai parameter fungsi dipisahkan koma ke pdo_query().
  • Tempatkan tanda tanya ? sebagai placeholder di mana variabel sebelumnya.
  • Menyingkirkan ' tanda kutip tunggal yang sebelumnya menyertakan nilai / variabel string.

Keuntungan menjadi lebih jelas untuk kode yang lebih panjang.

Seringkali variabel string tidak hanya diinterpolasi ke dalam SQL, tetapi digabungkan dengan panggilan keluar di antaranya.

pdo_query("SELECT id, links, html, title, user, date FROM articles
   WHERE title='" . pdo_real_escape_string($title) . "' OR id='".
   pdo_real_escape_string($title) . "' AND user <> '" .
   pdo_real_escape_string($root) . "' ORDER BY date")

Dengan ? placeholder diterapkan Anda tidak perlu repot-repot dengan itu:

pdo_query("SELECT id, links, html, title, user, date FROM articles
   WHERE title=? OR id=? AND user<>? ORDER BY date", $title, $id, $root)

Ingat bahwa pdo_ * masih memungkinkan baik atau.
Hanya saja, jangan melarikan diri dari suatu variabel dan ikat di kueri yang sama.

  • Fitur placeholder disediakan oleh PDO asli di belakangnya.
  • Demikian juga diizinkan :named daftar placeholder nanti.

Lebih penting lagi, Anda dapat mengirimkan variabel $ _REQUEST [] dengan aman di balik kueri apa pun. Saat diserahkan <form> bidang cocok dengan struktur basis data sebenarnya lebih pendek:

pdo_query("INSERT INTO pages VALUES (?,?,?,?,?)", $_POST);

Begitu banyak kesederhanaan. Tapi mari kembali ke beberapa saran penulisan ulang dan alasan teknis tentang mengapa Anda mungkin ingin menyingkirkannya mysql_dan melarikan diri.

Perbaiki atau hapus semua oldschool sanitize() fungsi

Setelah Anda mengkonversi semua mysql_ panggilan ke pdo_query dengan params yang terikat, hapus semua yang berlebihan pdo_real_escape_string panggilan.

Khususnya Anda harus memperbaikinya sanitize atau clean atau filterThis atau clean_data berfungsi seperti yang diiklankan oleh tutorial tanggal dalam satu bentuk atau yang lainnya:

function sanitize($str) {
   return trim(strip_tags(htmlentities(pdo_real_escape_string($str))));
}

Kebanyakan bug yang mencolok di sini adalah kurangnya dokumentasi. Lebih signifikan urutan penyaringan adalah urutan yang salah.

  • Urutan yang benar akan: tidak berlaku lagi stripslashes sebagai panggilan terdalam, lalu trim, sesudahnya strip_tags, htmlentities untuk konteks keluaran, dan hanya yang terakhir _escape_string sebagai aplikasinya harus langsung mendahului SQL intersparsing.

  • Tetapi sebagai langkah pertama saja singkirkan _real_escape_string panggilan.

  • Anda mungkin harus menyimpan sisanya sanitize() berfungsi untuk saat ini jika aliran database dan aplikasi Anda mengharapkan string HTML-konteks-aman. Tambahkan komentar yang hanya berlaku pelarian HTML untuk selanjutnya.

  • Penanganan string / nilai didelegasikan ke PDO dan pernyataan parameternya.

  • Jika ada penyebutan stripslashes() dalam fungsi sanitasi Anda, ini mungkin menunjukkan tingkat pengawasan yang lebih tinggi.

    • Itu biasanya ada untuk membatalkan kerusakan (melarikan diri ganda) dari yang ditinggalkan magic_quotes. Namun yang mana terbaik tetap terpusat, bukan string demi string.

    • Gunakan salah satu dari pembalikan userland pendekatan. Kemudian hapus stripslashes() dalam sanitize fungsi.

    Catatan bersejarah tentang magic_quotes. Fitur itu sudah tidak digunakan lagi. Ini sering salah digambarkan sebagai gagal keamanan fitur Namun. Tapi magic_quotes adalah fitur keamanan yang gagal karena bola tenis gagal sebagai sumber nutrisi. Itu bukan tujuan mereka.

    Implementasi asli di PHP2 / FI memperkenalkannya secara eksplisit hanya dengan "kutipan akan secara otomatis lolos sehingga lebih mudah untuk mengirim data formulir secara langsung ke kueri msql". Khususnya aman untuk digunakan dengan aman mSQL, karena hanya mendukung ASCII.
      Kemudian PHP3 / Zend memperkenalkan kembali magic_quotes untuk MySQL dan salah mendokumentasikannya. Tapi awalnya itu hanya a fitur kenyamanan, tidak berniat untuk keamanan.

Bagaimana pernyataan yang disiapkan berbeda

Ketika Anda mengacak variabel string ke dalam query SQL, itu tidak hanya menjadi lebih rumit untuk Anda ikuti. Ini juga upaya asing untuk MySQL untuk memisahkan kode dan data lagi.

Suntikan SQL hanyalah saat data memancar ke kode konteks. Sebuah server database tidak dapat kemudian menemukan di mana PHP awalnya menempelkan variabel di antara klausa permintaan.

Dengan parameter terikat Anda memisahkan kode SQL dan nilai-nilai konteks SQL dalam kode PHP Anda. Tetapi tidak teracak lagi di belakang layar (kecuali dengan PDO :: EMULATE_PREPARES). Database Anda menerima perintah SQL unvaried dan nilai variabel 1: 1.

Sementara jawaban ini menekankan bahwa Anda harus peduli tentang manfaat yang dapat dibaca dari menjatuhkan mysql_. Terkadang ada juga keunggulan kinerja (INSERT berulang dengan nilai yang berbeda) karena pemisahan data / kode yang terlihat dan teknis ini.

Berhati-hatilah bahwa pengikatan parameter masih bukan solusi one-stop yang ajaib semua Suntikan SQL. Ini menangani penggunaan paling umum untuk data / nilai. Tetapi tidak dapat memasukkan nama kolom / pengidentifikasi tabel daftar putih, membantu dengan konstruksi klausa dinamis, atau hanya daftar nilai array biasa.

Penggunaan PDO Hybrid

Ini pdo_* fungsi wrapper membuat API stop-gap coding-friendly. (Ini cukup banyak apa MYSQLI bisa saja kalau bukan karena perubahan fungsi signature idiosinkratik). Mereka juga mengekspos PDO yang sebenarnya paling banyak.
Menulis ulang tidak harus berhenti menggunakan nama fungsi pdo_ yang baru. Anda bisa satu per satu transisi setiap pdo_query () ke plain $ pdo-> prepare () -> execute () call.

Namun, yang terbaik adalah mulai menyederhanakan lagi. Misalnya hasil umum yang diambil:

$result = pdo_query("SELECT * FROM tbl");
while ($row = pdo_fetch_assoc($result)) {

Dapat diganti hanya dengan iterasi foreach:

foreach ($result as $row) {

Atau lebih baik lagi pengambilan array langsung dan lengkap:

$result->fetchAll();

Anda akan mendapatkan lebih banyak peringatan bermanfaat dalam banyak kasus daripada PDO atau mysql_ biasanya menyediakan setelah kueri gagal.

Pilihan lain

Jadi ini mudah-mudahan divisualisasikan beberapa praktis alasan dan jalur yang bermanfaat untuk dijatuhkan mysql_.

Hanya beralih ke  tidak cukup memotongnya. pdo_query() juga hanya sebuah frontend ke atasnya.

Kecuali Anda juga memperkenalkan pengikatan parameter atau dapat memanfaatkan sesuatu yang lain dari API yang lebih baik, itu adalah saklar yang tidak berguna. Saya harap itu digambarkan cukup sederhana untuk tidak lebih jauh mengecilkan hati para pendatang baru. (Pendidikan biasanya bekerja lebih baik daripada pelarangan.)

Meskipun memenuhi syarat untuk kategori yang paling sederhana-yang-bisa-mungkin-bekerja, itu juga masih merupakan kode eksperimen. Saya baru saja menulisnya selama akhir pekan. Ada sejumlah besar alternatif. Hanya google untuk Abstraksi database PHP dan telusuri sedikit. Selalu ada banyak perpustakaan yang bagus untuk tugas-tugas semacam itu.

Jika Anda ingin menyederhanakan interaksi database Anda lebih lanjut, seperti mappers Paris / Idiorm patut dicoba. Sama seperti tidak ada yang menggunakan DOM hambar di JavaScript lagi, Anda tidak perlu mengasuh antarmuka database mentah saat ini.


200
2017-10-12 13:22



Itu mysql_ fungsi:

  1. sudah ketinggalan zaman - mereka tidak dipertahankan lagi
  2. jangan biarkan Anda berpindah dengan mudah ke backend basis data lain
  3. jangan mendukung pernyataan siap, karenanya
  4. mendorong pemrogram untuk menggunakan penggabungan untuk membuat kueri, yang mengarah ke kerentanan injeksi SQL

134
2018-01-01 17:42



Berbicara tentang teknis alasannya, 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 menggunakan mereka seperti itu

  • non-blocking, pertanyaan asynchronous
  • prosedur tersimpan mengembalikan beberapa hasil
  • Enkripsi (SSL)
  • Kompresi

Jika Anda membutuhkannya - ini tidak diragukan lagi alasan teknis untuk beralih dari ekstensi mysql ke arah sesuatu yang lebih bergaya dan modern.

Namun demikian, ada juga beberapa masalah non-teknis, yang dapat membuat pengalaman Anda sedikit lebih sulit

  • penggunaan lebih lanjut dari fungsi-fungsi ini dengan versi PHP modern akan meningkatkan pemberitahuan yang tidak berlaku lagi. Mereka hanya bisa dimatikan.
  • di masa depan yang jauh, mereka mungkin dapat dihapus dari pembangunan PHP default. Bukan masalah besar juga, karena mydsql ext akan dipindahkan ke PECL dan setiap penghosting akan senang mengkompilasi PHP dengannya, karena mereka tidak ingin kehilangan klien yang situsnya bekerja selama beberapa dekade.
  • resistensi yang kuat dari komunitas Stackoverflow. Еsetiap kali Anda menyebutkan fungsi-fungsi yang jujur ​​ini, Anda diberi tahu bahwa mereka di bawah tabu yang ketat.
  • menjadi pengguna PHP rata-rata, kemungkinan besar ide Anda menggunakan fungsi-fungsi ini rawan kesalahan dan salah. Hanya karena semua banyak tutorial dan manual yang mengajarkan Anda dengan cara yang salah. Bukan fungsi itu sendiri - saya harus menekankannya - tetapi cara mereka digunakan.

Masalah terakhir ini adalah masalah.
Tapi, menurut saya, solusi yang diusulkan juga tidak lebih baik.
Sepertinya saya terlalu idealis sebuah mimpi bahwa semua pengguna PHP akan belajar bagaimana menangani query SQL dengan benar sekaligus. Kemungkinan besar mereka hanya akan mengubah mysql_ * ke mysqli_ * secara mekanis, meninggalkan pendekatan yang sama. Terutama karena mysqli membuat pernyataan siap penggunaan yang luar biasa menyakitkan dan menyusahkan.
Belum lagi itu asli pernyataan siap tidak cukup untuk melindungi dari injeksi SQL, dan baik mysqli maupun PDO menawarkan solusi.

Jadi, daripada melawan ekstensi 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

  • Tidak mendukung Stored Procedures (kami menggunakan mysql_query("CALL my_proc"); lama sekali)
  • Tidak mendukung Transaksi (sama seperti di atas)
  • Tidak mendukung Pernyataan Berganda (siapa yang membutuhkannya?)
  • Tidak dalam pengembangan aktif (jadi apa? Apakah itu mempengaruhi kamu dengan cara praktis apa pun?)
  • Tidak memiliki antarmuka OO (untuk membuatnya adalah masalah beberapa jam)
  • Tidak mendukung Pernyataan Siap Pakai atau Kueri Yang Ditentukan

Yang terakhir adalah poin yang menarik. Meskipun mysql ext tidak mendukung asli pernyataan siap, mereka tidak diperlukan untuk keamanan. Kita dapat dengan mudah memalsukan pernyataan yang disiapkan menggunakan placeholder yang ditangani secara manual (seperti halnya PDO):

function paraQuery()
{
    $args  = func_get_args();
    $query = array_shift($args);
    $query = str_replace("%s","'%s'",$query); 

    foreach ($args as $key => $val)
    {
        $args[$key] = mysql_real_escape_string($val);
    }

    $query  = vsprintf($query, $args);
    $result = mysql_query($query);
    if (!$result)
    {
        throw new Exception(mysql_error()." [$query]");
    }
    return $result;
}

$query  = "SELECT * FROM table where a=%s AND b LIKE %s LIMIT %d";
$result = paraQuery($query, $a, "%$b%", $limit);

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 Anda memahami perlunya menggunakan lapisan abstraksi basis data dan mencari API untuk membuatnya, mysqli adalah pilihan yang sangat bagus, karena memang mendukung banyak fitur khusus mysql.
  • Jika, seperti sebagian besar orang PHP, Anda menggunakan API baku panggilan langsung di kode aplikasi (yang pada dasarnya praktik salah) - PDO adalah satu-satunya pilihan, karena ekstensi ini berpura-pura bukan hanya API melainkan semi-DAL, masih belum lengkap tetapi menawarkan banyak fitur penting, dengan dua di antaranya membuat PDO sangat berbeda dari mysqli:

    • tidak seperti mysqli, PDO dapat mengikat placeholder berdasarkan nilai, yang membuat kueri yang dibuat secara dinamis layak tanpa beberapa layar kode yang cukup berantakan.
    • tidak seperti mysqli, PDO selalu dapat mengembalikan hasil query dalam array biasa yang sederhana, sementara mysqli dapat melakukannya hanya pada instalasi mysqlnd.

Jadi, jika Anda adalah pengguna PHP rata-rata dan ingin menghemat banyak sakit kepala saat menggunakan pernyataan asli 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-kasus kompleks di Tag wiki PDO

Namun demikian, semua orang berbicara tentang ekstensi selalu hilang 2 fakta penting tentang Mysqli dan PDO:

  1. Pernyataan siap bukan peluru perak. Ada pengidentifikasi dinamis yang tidak dapat terikat menggunakan pernyataan siap. Ada kueri dinamis dengan sejumlah parameter yang tidak diketahui yang membuat kueri membuat tugas yang sulit.

  2. Baik mysqli_ * maupun fungsi PDO seharusnya muncul dalam kode aplikasi.
    Seharusnya ada sebuah lapisan abstraksi antara mereka dan kode aplikasi, yang akan melakukan semua pekerjaan kotor dari pengikatan, perulangan, penanganan kesalahan, dll. di dalam, membuat kode aplikasi KERING dan bersih. Khusus untuk kasus-kasus kompleks seperti pembuatan kueri dinamis.

Jadi, beralih ke PDO atau mysqli saja tidak cukup. Seseorang harus menggunakan ORM, atau pembangun 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 - tidak terlalu penting mesin mana yang digunakan. Anda dapat menggunakan mysql ext hingga tidak lagi digunakan dan dengan mudah menulis ulang kelas abstraksi ke mesin lain, memiliki semua kode aplikasi utuh.

Berikut beberapa contoh berdasarkan pada saya kelas safemysql untuk menunjukkan bagaimana kelas abstraksi seperti itu seharusnya:

$city_ids = array(1,2,3);
$cities   = $db->getCol("SELECT name FROM cities WHERE is IN(?a)", $city_ids);

Bandingkan satu baris ini dengan jumlah kode yang Anda perlukan dengan PDO.
Kemudian bandingkan dengan jumlah kode yang gila Anda akan perlu dengan pernyataan disiapkan Mysqli mentah. Perhatikan bahwa penanganan kesalahan, pembuatan profil, pembuatan log permintaan sudah dibuat dan berjalan.

$insert = array('name' => 'John', 'surname' => "O'Hara");
$db->query("INSERT INTO users SET ?u", $insert);

Bandingkan dengan sisipan PDO biasa, ketika setiap nama kolom tunggal diulang enam hingga sepuluh kali - di semua banyak penampung bernama, bindings, dan definisi query ini.

Contoh lain:

$data = $db->getAll("SELECT * FROM goods ORDER BY ?n", $_GET['order']);

Anda hampir tidak dapat menemukan contoh untuk PDO untuk menangani kasus praktis seperti itu.
Dan itu akan terlalu bertele-tele dan kemungkinan besar tidak aman.

Jadi, sekali lagi - bukan hanya driver mentah yang harus menjadi perhatian Anda, tetapi kelas abstraksi, tidak hanya berguna untuk contoh-contoh konyol dari manual pemula tetapi untuk menyelesaikan masalah kehidupan nyata apa pun.


98
2017-10-12 13:23



Ada banyak alasan, tetapi mungkin yang paling penting adalah bahwa fungsi-fungsi tersebut mendorong praktik pemrograman tidak aman karena tidak mendukung pernyataan siap. Pernyataan siap membantu mencegah serangan injeksi SQL.

Ketika menggunakan mysql_* fungsi, Anda harus ingat untuk menjalankan parameter yang diberikan pengguna melalui mysql_real_escape_string(). Jika Anda lupa hanya di satu tempat atau jika Anda kebetulan melarikan diri hanya sebagian dari input, database Anda mungkin akan diserang.

Menggunakan pernyataan yang disiapkan di PDO atau mysqli akan membuatnya sehingga kesalahan pemrograman semacam ini lebih sulit dibuat.


87
2017-10-12 13:24



Karena (di antara alasan lain) jauh lebih sulit untuk memastikan data input disterilkan. Jika Anda menggunakan pertanyaan parametrized, seperti yang dilakukan dengan PDO atau mysqli Anda dapat sepenuhnya menghindari risiko.

Sebagai contoh, seseorang bisa menggunakannya "enhzflep); drop table users" sebagai nama pengguna. Fungsi-fungsi lama akan memungkinkan mengeksekusi beberapa pernyataan per query, sehingga sesuatu seperti bugger jahat itu dapat menghapus seluruh tabel.

Jika seseorang menggunakan PDO mysqli, nama pengguna akan berakhir "enhzflep); drop table users".

Lihat bobby-tables.com.


70
2017-09-18 12:28



Jawaban ini ditulis untuk menunjukkan betapa sepele itu untuk memotong kode validasi pengguna PHP yang ditulis dengan buruk, bagaimana (dan menggunakan apa) serangan ini bekerja dan bagaimana mengganti fungsi-fungsi lama MySQL dengan pernyataan siap 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 database uji coba mysql ini (saya telah menyebut persiapan ranjau):

mysql> create table users(
    -> id int(2) primary key auto_increment,
    -> userid tinytext,
    -> pass tinytext);
Query OK, 0 rows affected (0.05 sec)

mysql> insert into users values(null, 'Fluffeh', 'mypass');
Query OK, 1 row affected (0.04 sec)

mysql> create user 'prepared'@'localhost' identified by 'example';
Query OK, 0 rows affected (0.01 sec)

mysql> grant all privileges on prep.* to 'prepared'@'localhost' with grant option;
Query OK, 0 rows affected (0.00 sec)

Dengan 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):

<?php 

    if(!empty($_POST['user']))
    {
        $user=$_POST['user'];
    }   
    else
    {
        $user='bob';
    }
    if(!empty($_POST['pass']))
    {
        $pass=$_POST['pass'];
    }
    else
    {
        $pass='bob';
    }

    $database='prep';
    $link=mysql_connect('localhost', 'prepared', 'example');
    mysql_select_db($database) or die( "Unable to select database");

    $sql="select id, userid, pass from users where userid='$user' and pass='$pass'";
    //echo $sql."<br><br>";
    $result=mysql_query($sql);
    $isAdmin=false;
    while ($row = mysql_fetch_assoc($result)) {
        echo "My id is ".$row['id']." and my username is ".$row['userid']." and lastly, my password is ".$row['pass']."<br>";
        $isAdmin=true;
        // We have correctly matched the Username and Password
        // Lets give this person full access
    }
    if($isAdmin)
    {
        echo "The check passed. We have a verified admin!<br>";
    }
    else
    {
        echo "You could not be verified. Please try again...<br>";
    }
    mysql_close($link);

?>

<form name="exploited" method='post'>
    User: <input type='text' name='user'><br>
    Pass: <input type='text' name='pass'><br>
    <input type='submit'>
</form>

Terlihat cukup resmi pada pandangan pertama.

Pengguna harus memasukkan login dan kata sandi, bukan?

Brilian, jangan masukkan yang berikut ini:

user: bob
pass: somePass

dan serahkan.

Outputnya adalah sebagai berikut:

You could not be verified. Please try again...

Super! Bekerja seperti yang diharapkan, sekarang mari coba nama pengguna dan kata sandi yang sebenarnya:

user: Fluffeh
pass: mypass

Luar biasa! Hi-tos semua bulat, kode benar diverifikasi admin. Itu sempurna!

Yah, tidak juga. Katakanlah pengguna adalah orang kecil yang pintar. Katakanlah orang itu adalah aku.

Masukkan yang berikut ini:

user: bob
pass: n' or 1=1 or 'm=m

Dan hasilnya adalah:

The check passed. We have a verified admin!

Selamat, Anda hanya mengizinkan saya untuk memasukkan bagian yang hanya dilindungi oleh admin super dengan saya memasukkan nama pengguna palsu dan kata sandi palsu. Serius, jika Anda tidak mempercayai saya, 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 jawabannya, ITULAH MENGAPA ANDA DIKUNJUNGI.

Jadi, mari kita lihat apa yang salah, dan mengapa saya baru saja masuk ke go-admin-only-bat-cave Anda. Saya menebak dan berasumsi bahwa Anda tidak berhati-hati dengan masukan Anda dan langsung meneruskannya ke basis data secara langsung. Saya membangun input dengan cara yang akan MENGUBAH query yang sebenarnya Anda jalankan. Jadi, apa yang seharusnya terjadi, dan apa akhirnya?

select id, userid, pass from users where userid='$user' and pass='$pass'

Itu adalah kueri, tetapi ketika kami mengganti variabel dengan input aktual yang kami gunakan, kami mendapatkan yang berikut:

select id, userid, pass from users where userid='bob' and pass='n' or 1=1 or 'm=m'

Lihat bagaimana saya membangun "kata sandi" saya sehingga pertama-tama menutup satu kutipan di sekitar kata sandi, lalu memperkenalkan perbandingan yang benar-benar baru? Kemudian hanya untuk keamanan, saya menambahkan "string" lain sehingga satu kutipan akan ditutup seperti yang diharapkan dalam kode yang kami miliki sebelumnya.

Namun, ini bukan tentang orang-orang yang berteriak pada Anda sekarang, ini tentang menunjukkan kepada Anda bagaimana membuat kode Anda lebih aman.

Oke, jadi apa yang salah, dan bagaimana cara 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 kita melindungi bagian admin suci Anda dan membuatnya bagus dan aman? Hal pertama yang harus dilakukan adalah berhenti menggunakan yang benar-benar tua dan usang mysql_* fungsi. 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 penggunaan yang lebih baik mysqli_ atau PDO. Saya pribadi penggemar berat PDO, jadi saya akan menggunakan PDO di sisa jawaban ini. Ada pro dan kontra, tapi secara pribadi saya menemukan bahwa pro jauh lebih besar daripada kontra. Ini portabel di beberapa mesin database - apakah Anda menggunakan MySQL atau Oracle atau hanya tentang apa pun berdarah - hanya dengan mengubah string koneksi, ia memiliki semua fitur mewah yang ingin kita gunakan dan itu bagus dan bersih. Saya suka bersih.

Sekarang, mari kita lihat kode itu lagi, kali ini ditulis menggunakan objek PDO:

<?php 

    if(!empty($_POST['user']))
    {
        $user=$_POST['user'];
    }   
    else
    {
        $user='bob';
    }
    if(!empty($_POST['pass']))
    {
        $pass=$_POST['pass'];
    }
    else
    {
        $pass='bob';
    }
    $isAdmin=false;

    $database='prep';
    $pdo=new PDO ('mysql:host=localhost;dbname=prep', 'prepared', 'example');
    $sql="select id, userid, pass from users where userid=:user and pass=:password";
    $myPDO = $pdo->prepare($sql, array(PDO::ATTR_CURSOR => PDO::CURSOR_FWDONLY));
    if($myPDO->execute(array(':user' => $user, ':password' => $pass)))
    {
        while($row=$myPDO->fetch(PDO::FETCH_ASSOC))
        {
            echo "My id is ".$row['id']." and my username is ".$row['userid']." and lastly, my password is ".$row['pass']."<br>";
            $isAdmin=true;
            // We have correctly matched the Username and Password
            // Lets give this person full access
        }
    }

    if($isAdmin)
    {
        echo "The check passed. We have a verified admin!<br>";
    }
    else
    {
        echo "You could not be verified. Please try again...<br>";
    }

?>

<form name="exploited" method='post'>
    User: <input type='text' name='user'><br>
    Pass: <input type='text' name='pass'><br>
    <input type='submit'>
</form>

Perbedaan utama adalah tidak ada lagi mysql_* fungsi. Itu semua dilakukan melalui objek PDO, kedua, menggunakan pernyataan yang disiapkan. Sekarang, apa pernyataan prepred yang Anda tanyakan? Ini adalah cara untuk memberitahu database 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 meneruskan dari pengguna tabel di mana userid adalah variabel dan pass juga merupakan variabel."

Kemudian, dalam pernyataan eksekusi, kami meneruskan database array dengan semua variabel yang sekarang harapkan.

Hasilnya fantastis. Mari coba kombinasi nama pengguna dan kata sandi itu dari sebelumnya:

user: bob
pass: somePass

Pengguna tidak diverifikasi. Luar biasa.

Bagaimana tentang:

user: Fluffeh
pass: mypass

Oh, saya baru saja sedikit bersemangat, itu berhasil: Cek berlalu. Kami memiliki admin terverifikasi!

Sekarang, mari coba data yang akan dimasukkan orang pintar untuk mencoba melewati sistem verifikasi kecil kami:

user: bob
pass: n' or 1=1 or 'm=m

Kali ini, kita mendapatkan yang berikut:

You could not be verified. Please try again...

Inilah sebabnya mengapa Anda dimarahi saat memposting pertanyaan - itu karena orang dapat melihat bahwa kode Anda dapat dilewati tanpa perlu mencoba. Silakan, gunakan pertanyaan dan jawaban ini untuk meningkatkan kode Anda, untuk membuatnya lebih aman dan menggunakan fungsi yang saat ini.

Terakhir, ini bukan untuk mengatakan bahwa ini adalah kode PERFECT. Ada banyak hal lagi yang dapat Anda lakukan untuk memperbaikinya, gunakan kata sandi hash misalnya, pastikan bahwa ketika Anda menyimpan informasi sensetif dalam basis data, Anda tidak menyimpannya dalam teks biasa, memiliki beberapa tingkat verifikasi - tetapi sebenarnya, 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 telah sampai sejauh ini dan masih membaca memberi saya rasa harapan bahwa Anda tidak akan hanya menerapkan jenis ini kode saat menulis situs web dan aplikasi Anda, tetapi Anda mungkin keluar 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.


62
2017-09-02 07:20



Ekstensi MySQL adalah yang tertua dari ketiganya dan merupakan cara asli yang digunakan pengembang untuk berkomunikasi dengan MySQL. Ekstensi ini sekarang sedang tidak lagi digunakan mendukung yang lain dua  alternatif karena perbaikan yang dibuat dalam rilis baru dari PHP dan MySQL.

  • MySQLi adalah ekstensi 'ditingkatkan' untuk bekerja dengan database MySQL. Ini mengambil keuntungan dari fitur-fitur yang tersedia di versi baru dari server MySQL, mengekspos kedua fungsi berorientasi dan antarmuka berorientasi objek ke pengembang dan melakukan beberapa hal bagus lainnya.

  • PDO menawarkan API yang mengkonsolidasikan sebagian besar fungsi yang sebelumnya tersebar di seluruh ekstensi akses basis data utama, yaitu MySQL, PostgreSQL, SQLite, MSSQL, dll. Antarmuka memaparkan objek tingkat tinggi bagi programmer untuk bekerja dengan koneksi basis data, kueri, dan hasil set, dan driver tingkat rendah melakukan komunikasi dan penanganan sumber daya dengan server database. Banyak diskusi dan pekerjaan akan masuk ke PDO dan itu dianggap metode yang tepat untuk bekerja dengan basis data dalam kode profesional modern.


30
2017-09-07 15:06