Advertisement
  1. Code
  2. WordPress
  3. Plugin Development

Tabel Database kustom: Keselamatan pertama

Scroll to top
Read Time: 11 min
This post is part of a series called Custom Database Tables.
Custom Database Tables: Creating the Table
Custom Database Tables: Creating an API

() translation by (you can also view the original English article)

Ini adalah bagian kedua dari seri tentang tabel database kustom di WordPress. Pada bagian satu kita membahas alasan untuk, dan melawan, menggunakan tabel kustom. Kami melihat beberapa rincian yang perlu dipertimbangkan - kolom penamaan, jenis kolom - serta cara membuat tabel. Sebelum kita melangkah lebih jauh kita perlu mencakup bagaimana untuk berinteraksi dengan tabel baru ini dengan aman. Dalam artikel sebelumnya saya dibahas sanitisation Umum dan validasi-dalam tutorial ini kita akan melihat ini secara lebih rinci dalam konteks database.

Keselamatan ketika berinteraksi dengan tabel database yang terpenting-itulah sebabnya kita sedang menutupinya awal dalam seri. Jika tidak dilakukan dengan benar maka Anda dapat meninggalkan meja Anda terbuka untuk manipulasi melalui SQL injection. Itu bisa memungkinkan hacker untuk mengekstrak informasi, mengganti konten atau bahkan mengubah cara berperilaku situs Anda - dan kerusakan yang bisa mereka lakukan tidak dibatasi ke meja kustom Anda.

Mari kita bayangkan kita ingin memungkinkan administrator untuk menghapus catatan dari log aktivitas kami. Sebuah kesalahan umum yang pernah saya lihat adalah sebagai berikut:

1
     if ( !empty($_GET['action'])
2
          && 'delete-activity-log' == $_GET['action'] 
3
          && isset($_GET['log_id']) ) {
4
               
5
               global $wpdb;
6
               unsafe_delete_log($_GET['log_id']);
7
          
8
     }
9
 
10
     function unsafe_delete_log( $log_id ){
11
          global $wpdb;
12
          $sql = "DELETE FROM {$wpdb->wptuts_activity_log} WHERE log_id = $log_id";
13
          $deleted = $wpdb->query( $sql );
14
     }

Jadi apa salah di sini? Banyak: Mereka sudah tidak memeriksa perizinan, sehingga siapapun dapat menghapus log aktivitas. Atau apakah mereka memeriksa nonces, sehingga bahkan dengan izin pemeriksaan, pengguna admin bisa akan tertipu untuk menghapus log. Ini semua tertutup dalam tutorial ini. Tetapi mereka kesalahan ketiga senyawa dua yang pertama: fungsi unsafe_delete_log() menggunakan nilai yang berlalu dalam perintah SQL tanpa melarikan diri pertama. Ini meninggalkan itu terbuka lebar untuk manipulasi.

Mari kita bayangkan penggunaannya adalah

1
  www.unsafe-site.com?action=delete-activity-log&log_id=7

Bagaimana jika penyerang dikunjungi (atau admin tertipu untuk mengunjungi): www.unsafe-site.com?action=delete-activity-log&log_id=1;%20DROP%20TABLE%20wp_posts. Log_id berisi perintah SQL, yang kemudian disuntikkan ke $sql dan akan dijalankan sebagai:

1
    DELETE from wp_wptuts_activity_log WHERE log_id=1; DROP TABLE wp_posts

Hasil: tabel wp_posts seluruh dihapus. Aku pernah melihat kode seperti ini di forum- dan hasilnya adalah bahwa siapa pun yang mengunjungi situs mereka dapat memperbarui atau menghapus setiap tabel dalam database mereka.

Jika kesalahan dua dikoreksi, maka hal itu membuatnya lebih sulit untuk jenis serangan bekerja- tetapi tidak mustahil, dan itu tidak akan melindungi 'penyerang' yang memiliki izin untuk menghapus log aktivitas. Hal ini sangat penting untuk melindungi situs Anda terhadap SQL suntikan. Hal ini juga sangat sederhana: WordPress menyediakan metode mempersiapkan. Dalam contoh khusus ini:

1
     function safe_delete_log( $log_id ){
2
         global $wpdb;
3
         $sql = $wpdb->prepare("DELETE from {$wpdb->wptuts_activity_log} WHERE log_id = %d", $log_id);
4
         $deleted = $wpdb->query( $sql )
5
     }

Perintah SQL akan sekarang menjalankan sebagai

1
    DELETE from wp_wptuts_activity_log WHERE log_id=1;

Pembersihan query Database

Kebanyakan sanitisation dapat dilakukan dengan hanya menggunakan $wpdb global-terutama melalui yang mempersiapkan metode. Ini juga menyediakan metode untuk memasukkan dan memperbarui data ke dalam tabel dengan aman. Ini biasanya bekerja dengan menggantikan input tidak diketahui, atau masukan, bergaul dengan pengganti format. Format ini memberitahu WordPress data apa yang diharapkan:

  • %s menandakan string
  • %d menandakan integer
  • %f menunjukkan pelampung

Kita mulai dengan melihat tiga metode yang tidak hanya membersihkan pertanyaan - tetapi membangun mereka untuk Anda juga.

Memasukkan Data

WordPress menyediakan metode $wpdb-> insert(). Sebuah bungkus untuk memasukkan data ke dalam database dan menangani sanitisation. Dibutuhkan tiga parameter:

  • Tabel Nama-nama tabel
  • Data-array data untuk memasukkan sebagai kolom-> pasangan nilai
  • Format-macam format yang sesuai dengan nilai dalam array data (misalnya %s %d, %f)

Perhatikan bahwa tombol data harus kolom: jika ada kunci yang tidak cocok kolom kesalahan dapat dibuang.

Dalam contoh yang mengikuti kami telah secara eksplisit menetapkan data - tapi tentu saja, secara umum, data ini akan datang dari user input - sehingga bisa apa-apa. Seperti yang dibahas dalam artikel ini data harus telah divalidasi pertama, untuk mengembalikan kesalahan ke pengguna - tapi kita masih perlu untuk membersihkan data sebelum menambahkannya ke meja kami. Kita akan melihat validasi dalam artikel berikutnya dari seri ini.

1
global $wpdb;
2
// 

3
$user_id = 1;
4
$activity = 1;
5
$object_id = 1479;
6
$activity_date = date_i18n('Y-m-d H:i:s', false, true);
7
$inserted = $wpdb->insert(
8
     $wpdb->wptuts_activity_log,
9
     array(
10
        'user_id'=>$user_id,
11
        'activity'=>$activity,
12
        'object_id'=>$object_id,
13
        'activity_date'=> $activity_date,
14
      ),
15
     array (
16
        '%d',
17
        '%s',
18
        '%d',
19
        '%s',
20
     )
21
 );
22
 if( $inserted ){
23
    $insert_id = $wpdb->insert_id;
24
 }else{
25
    //Insert failed

26
 }

Memperbarui Data

Untuk memperbarui data dalam database kami memiliki $wpdb-> update() akan membuat. Metode ini menerima lima argumen:

  • Tabel Nama-nama tabel
  • Data-array data untuk memperbarui sebagai kolom-> pasangan nilai
  • Mana – array data untuk mencocokkan sebagai kolom-> pasangan nilai
  • Format data-macam format untuk 'data' nilai yang sesuai
  • Mana Format-macam format untuk nilai yang sesuai 'mana'

Ini update setiap baris yang cocok di mana array dengan nilai-nilai dari data array. Sekali lagi, seperti yang dengan $wpdb->insert() kunci array data harus cocok kolom. Ia mengembalikan false pada kesalahan, atau jumlah baris yang diperbarui.

Dalam contoh berikut kami memperbarui catatan dengan login ID '14' (yang harus paling satu record, karena ini adalah kunci utama kami). Ini update user ID untuk 2 dan kegiatan untuk 'diedit'.

1
global $wpdb;
2
$user_id=2;
3
$activity='edited';
4
$log_id = 14;
5
$updated = $wpdb->update(
6
     $wpdb->wptuts_activity_log,
7
     array(
8
        'user_id'=>$user_id, 
9
        'activity'=>$activity,
10
     ),
11
     array('log_id'=>$log_id,),
12
     array( '%d', '%s'),
13
     array( '%d'),
14
 );
15
 if( $updated ){
16
    //Number of rows updated = $updated

17
 }

Menghapus

Sejak 3.4 WordPress juga telah memberikan metode $wpdb->delete() untuk dengan mudah (dan aman) menghapus row(s). Metode ini mengambil tiga parameter:

  • Tabel Nama-nama tabel
  • Mana – array data untuk mencocokkan sebagai kolom-> pasangan nilai
  • Format-macam format untuk tipe nilai yang sesuai (misalnya %s %d, %f)

Jika Anda ingin kode agar kompatibel dengan WordPress pra-3.4, maka Anda akan perlu menggunakan $wpdb-> mempersiapkan metode untuk sanitise pernyataan SQL yang sesuai. Contoh ini telah diberikan di atas. $wpdb-> Hapus metode mengembalikan jumlah menghapus baris, atau palsu sebaliknya-sehingga Anda dapat menentukan jika penghapusan berhasil.

1
global $wpdb;
2
$deleted = $wpdb->delete(
3
     $wpdb->wptuts_activity_log,
4
     array('log_id'=>14,),
5
     array( '%d'),
6
 );
7
 if( $deleted ){
8
    //Number of rows deleted = $deleted

9
 }

esc_sql

Mengingat metode di atas, dan metode $wpdb->prepare() lebih umum yang dibahas selanjutnya, fungsi ini sedikit berlebihan. Yang disediakan sebagai pembungkus berguna untuk $wpdb->escape() metode, itu sendiri addslashes dimuliakan. Sejak yang biasanya lebih tepat, dan dianjurkan untuk menggunakan tiga metode di atas, atau $wpdb-> prepare(), Anda mungkin akan menemukan bahwa Anda jarang perlu menggunakan esc_sql().

Sebagai contoh sederhana:

1
$activity = 'commented';
2
$sql = "DELETE FROM {$wpdb->wptuts_activity_log} WHERE activity='".esc_sql($activity)."';";

Pertanyaan Umum

Untuk perintah SQL umum yang mana (yaitu mereka tidak memasukkan, menghapus atau update baris) yang kita harus menggunakan metode $wpdb-> prepare(). It menerima sejumlah variabel argumen. Yang pertama adalah query SQL yang kita ingin mengeksekusi dengan semua data 'tidak diketahui' digantikan oleh pengganti format sesuai mereka. Nilai-nilai ini melewati sebagai argumen tambahan, dalam urutan bahwa mereka muncul.

Misalnya daripada:

1
$sql = "SELECT* FROM {$wpdb->wptuts_activity_log} 

2
         WHERE user_id = $user_id 

3
         AND object_id = $object_id 

4
         AND activity = $activity 

5
         ORDER BY activity_date $order";
6
$logs = $wpdb->get_results($sql);

Kami punya

1
$sql = $wpdb->prepare("SELECT* FROM {$wpdb->wptuts_activity_log} 

2
                WHERE user_id = %d 

3
                AND object_id = %d 

4
                AND activity = %s 

5
                ORDER BY activity_date %s", 
6
               $user_id,$object_id,$activity, $order  );
7
$logs = $wpdb->get_results($sql);

Metode mempersiapkan melakukan dua hal.

  1. Itu berlaku mysql_real_escape_string() (atau addslashes()) nilai-nilai yang dimasukkan. Secara khusus ini akan mencegah nilai-nilai yang berisi tanda kutip dari melompat keluar dari query.
  2. Itu berlaku vsprintf() saat menambahkan nilai ke query untuk memastikan mereka yang diformat dengan tepat (begitu bilangan bulat bilangan bulat, mengapung yang mengapung dll). Ini sebabnya kami contoh di awal artikel dilucuti keluar segala sesuatu tetapi '1'.

Pertanyaan yang lebih rumit

Anda harus menemukan bahwa $wpdb-> mempersiapkan, bersama dengan insert, update dan delete metode adalah yang Anda butuhkan. Kadang-kadang walaupun ada keadaan dimana pendekatan 'manual' lebih diinginkan - kadang-kadang hanya dari pembacaan sudut pandang. Misalnya, kita memiliki array yang tidak diketahui kegiatan yang kami ingin semua log. Kami * bisa * secara dinamis menambahkan pengganti %s ke SQL query, namun pendekatan yang lebih langsung tampaknya lebih mudah:

1
 //An unknown array that should contain strings being queried for

2
 $activities = array( ... ); 
3
4
 //Sanitize the contents of the array

5
 $activities = array_map('esc_sql',$activities);
6
 $activities = array_map('sanitize_title_for_query',$activities);
7
8
 //Create a string from the sanitised array forming the inner part of the IN( ... ) statement

9
 $in_sql = "'". implode( "','", $activities ) . "'";
10
11
 //Add this to the query

12
 $sql = "SELECT* FROM $wpdb->wptuts_activity_log WHERE activity IN({$in_sql});"
13
14
 //Perform the query

15
 $logs = $wpdb->get_results($sql);

Idenya adalah untuk menerapkan esc_sql dan sanitize_title_for_query untuk setiap elemen dalam array. Pertama menambahkan garis miring untuk menghindari istilah-mirip $wpdb->prepare() apa Apakah. Kedua hanya berlaku sanitize_title_with_dashes() – meskipun perilaku dapat dimodifikasi sepenuhnya melalui filter. Pernyataan SQL yang sebenarnya yang dibentuk oleh meledak sekarang orang minta-minga array ke string dipisahkan koma, yang ditambahkan ke bagian IN(...) query.

Jika array diharapkan mengandung bilangan bulat kemudian itu sudah cukup untuk menggunakan intval() atau absint() untuk membersihkan setiap elemen dalam array.

Membolehkan akses

Dalam kasus lain membolehkan akses mungkin tepat. Misalnya input tidak diketahui mungkin sebuah array dari kolom yang akan dikembalikan dalam permintaan. Karena kita tahu apa yang kolom database yang kita dapat hanya whitelist mereka-menghapus setiap bidang yang kami tidak menyadari. Namun, untuk membuat kode kita manusia ramah kita harus tidak bersifat case-sensitive. Untuk melakukan hal ini kita akan mengubah apa pun yang kita menerima huruf kecil-karena dalam bagian satu kami khusus menggunakan nama kolom huruf kecil.

1
 //An unknown array that should contain columns to be included in the query

2
 $fields = array( ... ); 
3
4
 //A whitelist of allowed fields

5
 $allowed_fields = array( ... );    
6
7
 //Convert fields to lowercase (as our column names are all lower case - see part 1)

8
 $fields = array_map('strtolower',$fields);
9
10
 //Sanitize by white listing

11
 $fields = array_intersect($fields, $allowed_fields);
12
13
 //Return only selected fields. Empty $fields is interpreted as all

14
 if( empty($fields) ){
15
     $sql = "SELECT* FROM {$wpdb->wptuts_activity_log}";
16
 }else{
17
     $sql = "SELECT ".implode(',',$fields)." FROM {$wpdb->wptuts_activity_log}";
18
 }
19
20
 //Perform the query   

21
 $logs = $wpdb->get_results($sql);

Membolehkan akses ini juga nyaman ketika menetapkan bagian ORDER BY pada query (jika ini diatur oleh input pengguna): data dapat dipesan sebagai DESC atau ASC hanya.

1
     //Unknown user input (expected to be asc or desc)

2
     $order = $_GET['order']; 
3
4
     //Allow input to be any, or mixed, case

5
     $order = strtoupper($order);
6
     
7
     //Sanitised order value

8
     $order = ( 'ASC' == $order ? 'ASC' : 'DEC' );

SEPERTI query

Pernyataan SQL seperti mendukung penggunaan wildcard seperti % (nol atau lebih karakter) dan _ (tepat satu karakter) ketika pencocokan nilai untuk query. Sebagai contoh nilai foobar akan cocok salah satu pertanyaan:

1
 SELECT * FROM $wpdb->wptuts_activity_log WHERE activity LIKE 'foo%'
2
 SELECT * FROM $wpdb->wptuts_activity_log WHERE activity LIKE '%bar'
3
 SELECT * FROM $wpdb->wptuts_activity_log WHERE activity LIKE '%oba%'
4
 SELECT * FROM $wpdb->wptuts_activity_log WHERE activity LIKE 'fo_bar%'

Namun, karakter khusus ini sebenarnya bisa hadir dalam istilah yang dicari untuk- dan begitu untuk mencegah mereka dari menjadi ditafsirkan sebagai wildcard-kita perlu untuk melarikan diri mereka. Untuk WordPress ini menyediakan fungsi like_escape(). Catatan bahwa ini tidak mencegah SQL Injeksi- tetapi hanya lolos % dan _ karakter: Anda masih perlu untuk menggunakan esc_sql() atau $wpdb-> prepare().

1
 //Collect term

2
 $term = $_GET['activity'];
3
4
 //Escape any wildcards

5
 $term = like_escape($term);
6
7
 $sql = $wpdb->prepare("SELECT* FROM $wpdb->wptuts_activity_log WHERE activity LIKE %s", '%'.$term.'%');
8
9
 $logs = $wpdb->get_results($sql);

Query Wrapper fungsi

Dalam contoh kita telah melihat kami telah menggunakan dua metode lain untuk $wpdb:

  • $wpdb-> query ($sql)-ini melakukan permintaan apapun yang diberikan dan mengembalikan jumlah baris yang terkena.
  • $wpdb-> get_results ($sql, $ouput)-ini melakukan query yang diberikan dan mengembalikan hasil pencocokan set (yaitu baris pencocokan). $output menetapkan format hasil yang dikembalikan:
    • ARRAY_A-numerik serangkaian baris, dimana setiap baris adalah array asosiatif, mengetik oleh kolom.
    • ARRAY_N-numerik serangkaian baris, dimana setiap baris adalah array numerik.
    • OBJEK-numerik serangkaian baris, dimana setiap baris adalah objek baris. Default.
    • OBJECT_K-array asosiatif baris (mengetik dengan nilai kolom pertama), dimana setiap baris adalah array asosiatif.

Ada orang lain yang belum disebutkan terlalu:

  • $wpdb-> get_row ($sql, $ouput, $row)-ini melakukan query dan kembali satu baris. set $row yang baris adalah dikembalikan, secara default ini adalah 0, baris pertama yang cocok. $output menetapkan format baris:
    • ARRAY_A-baris adalah kolom => nilai pair.
    • ARRAY_N-baris adalah array nilai numerik.
    • OBJEK – baris dikembalikan sebagai objek. Default.
  • $wpdb-> get_col ($sql, $column)-ini melakukan query dan mengembalikan array numerik nilai dari kolom tertentu. $column menentukan kolom yang kembali integer. Secara default, ini adalah 0, kolom pertama.
  • $wpdb-> get_var ($sql, $column, $row)-ini melakukan query dan mengembalikan nilai tertentu. $row dan $column adalah seperti di atas, dan menentukan nilai yang kembali. Sebagai contoh,
    1
    $activities_by_user_1 = $wpdb->get_var("SELECT COUNT(*) FROM {$wpdb->wptuts_activity_log} WHERE user_id = 1");
    

Hal ini penting untuk dicatat bahwa metode ini hanya pembungkus untuk melakukan query SQL dan format hasil. Mereka tidak membersihkan query-sehingga Anda tidak harus menggunakan mereka sendiri ketika permintaan berisi beberapa data 'tidak diketahui'.


Ringkasan

Kita telah membahas cukup banyak dalam tutorial ini - dan data sanitisation adalah topik yang penting untuk memahami. Dalam artikel berikutnya kita akan menjadi menerapkannya kami plug-in. Kita akan melihat mengembangkan satu set fungsi pembungkus (mirip dengan fungsi seperti wp_insert_post(), wp_delete_post() dll) yang akan menambah lapisan abstraksi antara kami plug-in dan database.

Advertisement
Did you find this post useful?
Want a weekly email summary?
Subscribe below and we’ll send you a weekly email summary of all new Code tutorials. Never miss out on learning about the next big thing.
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.