Sejauh ini, Anda telah memahami dasar-dasar pengembangan plugin dan tema untuk WordPress. Sekarang adalah waktu yang tepat untuk mempertimbangkan implikasi keamanan dari penulisan kode.
Dalam pelajaran ini, Anda akan mempelajari cara mengembangkan dengan mempertimbangkan keamanan.
Anda akan mempelajari manfaat memastikan kode Anda aman, langkah-langkah yang harus diikuti untuk mengamankan kode, dan tempat menemukan informasi lebih lanjut tentang pengembangan dengan mempertimbangkan keamanan.
Penafian
Pelajaran ini dibuat sebagai pengantar untuk mengembangkan plugin dan tema dengan kesadaran terhadap keamanan. Kode yang digunakan dalam pelajaran ini sangat disederhanakan. Anda tidak boleh menggunakan kode apa pun yang digunakan dalam tutorial ini di plugin atau tema Anda.
Pastikan untuk membaca dokumentasi lengkap tentang Keamanan di buku panduan Pengembang WordPress di https://developer.wordpress.org/apis/security/ untuk memastikan Anda mengikuti metode dan prosedur yang benar.
Apa yang dimaksud dengan mengembangkan dengan mempertimbangkan keamanan?
Mengembangkan dengan mempertimbangkan keamanan adalah proses memastikan kode Anda tidak hanya berfungsi, tetapi juga tidak menimbulkan kerentanan keamanan.
Jika kode plugin atau tema Anda memiliki kerentanan keamanan, hal itu dapat membuat situs WordPress mana pun yang memasang produk Anda terbuka terhadap serangan potensial dan dapat menyebabkan situs tersebut diretas.
Saat menulis kode, penting untuk mengembangkan pola pikir yang berorientasi pada keamanan dan memikirkan bagaimana kode Anda dapat digunakan secara jahat.
- Jangan mempercayai data apa pun, baik itu input pengguna, data API pihak ketiga, maupun data di basis data Anda. Selalu periksa untuk memastikan data tersebut valid dan aman digunakan.
- WordPress memiliki sejumlah API yang dapat membantu Anda melakukan tugas-tugas umum, seperti membersihkan input pengguna, memvalidasi data, dan melakukan escape pada output. Andalkan API ini untuk membantu memvalidasi dan membersihkan data Anda, alih-alih menulis fungsi sendiri.
- Ikuti perkembangan kerentanan umum dan selalu perbarui kode Anda untuk mencegahnya.
Di mana keamanan ditempatkan dalam proses pengembangan?
Keamanan harus menjadi pertimbangan di setiap tahap proses pengembangan.
Secara umum, jumlah kerentanan keamanan terbesar yang ditemukan terjadi pada lapisan PHP, yang dijalankan di server.
Oleh karena itu, pelajaran ini akan berfokus pada langkah-langkah pencegahan yang paling umum dalam kode PHP Anda.
Membersihkan input
Salah satu langkah pertama yang harus dilakukan saat mengembangkan adalah memastikan bahwa setiap input pengguna telah dibersihkan. Ini berarti setiap data yang diteruskan dari pengguna, seperti pengiriman formulir atau parameter URL, diperiksa untuk memastikan data tersebut aman digunakan.
Dalam contoh kode ini, kolom nama dan email dikirim dari formulir yang dibuat oleh sebuah plugin, lalu disimpan dalam tabel basis data khusus bernama form_submissions;
$name = $_POST['name'];
$email = $_POST['email'];
global $wpdb;
$table_name = $wpdb->prefix . 'form_submissions';
$sql = "INSERT INTO $table_name (name, email) VALUES ('$name', '$email')";
$result = $wpdb->query($sql);
Seperti yang dapat Anda lihat, data disimpan langsung ke basis data tanpa proses pembersihan.
Ini berarti jika pengguna mengirimkan nama John'; DROP TABLE form_submissions;--, kueri SQL INSERT akan dijalankan, diikuti oleh kueri DROP, dan tabel tersebut akan dihapus dari basis data!
WordPress memiliki API pembersihan yang dapat digunakan untuk membersihkan data masuk. Anda dapat menggunakan fungsi sanitize_text_field dan sanitize_email untuk membersihkan kolom nama dan email sebelum digunakan dalam kueri.
$name = sanitize_text_field( $_POST['name'] );
$email = sanitize_email( $_POST['email'] );
global $wpdb;
$table_name = $wpdb->prefix . 'form_submissions';
$sql = "INSERT INTO $table_name (name, email) VALUES ('$name', '$email')";
$result = $wpdb->query($sql);
Perhatikan bahwa kode ini mengikuti prinsip utama pembersihan data, yaitu melakukannya sedini mungkin.
Untuk membaca lebih lanjut tentang fungsi pembersihan yang tersedia bagi pengembang WordPress, kunjungi halaman Membersihkan Data di Dokumentasi Pengembang WordPress.
Memvalidasi Data (4:06)
Memvalidasi data adalah proses mengujinya terhadap pola yang telah ditentukan sebelumnya (atau beberapa pola) dengan hasil yang pasti, yaitu valid atau tidak valid.
Data yang tidak tepercaya dapat berasal dari banyak sumber: pengguna, data API pihak ketiga, bahkan data basis data Anda dapat dianggap tidak tepercaya, terutama jika telah diubah oleh pihak lain. Bahkan admin situs dapat melakukan kesalahan dan memasukkan data yang salah atau tidak aman, jadi penting untuk selalu memeriksa data Anda.
Dalam contoh ini, fungsi penghapusan mengharuskan ID numerik dikirim ke callback admin-ajax:
add_action( 'wp_ajax_delete_form_submission', 'wp_learn_delete_form_submission' );
function wp_learn_delete_form_submission() {
if ( ! isset( $_POST['id'] ) ) {
wp_send_json_error( 'Invalid ID' );
}
$id = $_POST['id'];
global $wpdb;
$table_name = $wpdb->prefix . 'form_submissions';
$sql = "DELETE FROM $table_name WHERE id = $id";
$result = $wpdb->get_results( $sql );
return wp_send_json( array( 'result' => $result ) );
}
Di sini, ID digunakan langsung dalam kueri SQL tanpa validasi. Sekali lagi, ini berarti jika pengguna mengirimkan ID 1; DROP TABLE form_submissions;, kueri SQL DROP yang sama akan dijalankan setelah penghapusan, dan tabel tersebut akan dihapus.
Untuk mencegahnya, Anda dapat menggunakan fungsionalitas type-casting PHP untuk memastikan bahwa nilai $id selalu berupa bilangan bulat. Hal ini dapat dilakukan dengan menambahkan (int) sebelum nama variabel, seperti berikut:
$id = (int) $_POST['id'];
Perhatikan bahwa ini hanya akan berfungsi jika karakter pertama dalam string yang diteruskan melalui array $_POST dapat diubah menjadi bilangan bulat; jika tidak, nilai $id akan menjadi 0. Dalam hal ini, sebaiknya perbarui kode untuk menangani kondisi tersebut.
Kode ini juga mengikuti prinsip utama validasi data, yaitu melakukannya sedini mungkin.
if ($id === 0){
// return early with an error
return wp_send_json( array( 'result' => 'Invalid ID passed' ) );
}
Untuk membaca lebih lanjut tentang berbagai cara memvalidasi data, lihat bagian Memvalidasi Data di Dokumentasi Pengembang WordPress.
Melakukan escape pada output
Aspek keamanan lainnya adalah memastikan bahwa setiap informasi yang Anda tampilkan di browser aman, termasuk teks, HTML atau kode JavaScript, maupun data dari basis data.
Meskipun kode Anda tidak bertanggung jawab atas sumber data yang ditampilkan, kode tersebut tetap bertanggung jawab untuk menampilkannya dengan aman.
Dalam contoh ini, kode mengambil kiriman formulir dari basis data, lalu melakukan perulangan pada kiriman tersebut dan menampilkan data kiriman dalam layar admin di dasbor WordPress:
$submissions = wp_learn_get_form_submissions();
?>
<div class="wrap" id="wp_learn_admin">
<h1>Admin</h1>
<table>
<thead>
<tr>
<th>Name</th>
<th>Email</th>
</tr>
</thead>
<?php foreach ($submissions as $submission){ ?>
<tr>
<td><?php echo $submission->name; ?></td>
<td><?php echo $submission->email; ?></td>
<td><a class="delete-submission" data-id="<?php echo $submission->id?>" style="cursor:pointer;">Delete</a></td>
</tr>
<?php } ?>
</table>
</div>
<?php
Di sini terdapat tiga bagian data yang perlu dilakukan escape, yaitu kolom $submission->name dan $submission->email, serta $submission->id.
<td><a class="delete-submission" data-id="<?php echo (int) $submission->id?>" style="cursor:pointer;">Delete</a></td>
Untuk kolom nama dan email, kita dapat menggunakan fungsi escape bawaan WordPress esc_html(). ID dapat dilakukan escape dengan mengubahnya menjadi bilangan bulat, seperti yang mungkin Anda lakukan untuk validasi data.
<td><?php echo esc_html( $submission->name ); ?></td>
<td><?php echo esc_html( $submission->email ); ?></td>
<td><a class="delete-submission" data-id="<?php echo (int) $submission->id?>" style="cursor:pointer;">Delete</a></td>
Perhatikan bahwa kode ini mengikuti prinsip utama escape data, yaitu melakukan escape pada data selambat mungkin.
Untuk membaca lebih lanjut tentang berbagai cara melakukan escape pada output, lihat bagian Melakukan Escape pada Data di Dokumentasi Pengembang WordPress.
Mencegah permintaan yang tidak valid
Setiap kali permintaan dibuat, penting untuk memeriksa bahwa permintaan tersebut valid. Ini berarti memeriksa bahwa permintaan berasal dari sumber tepercaya.
Sebagai contoh, Anda mungkin memiliki shortcode yang menampilkan formulir tempat pengguna dapat mengirimkan informasi mereka.
Fungsi untuk menampilkan formulir mungkin terlihat seperti berikut.
add_shortcode( 'wp_learn_form_shortcode', 'wp_learn_form_shortcode' );
function wp_learn_form_shortcode() {
ob_start();
?>
<form method="post">
<input type="hidden" name="wp_learn_form" value="submit">
<div>
<label for="email">Name</label>
<input type="text" id="name" name="name" placeholder="Name">
</div>
<div>
<label for="email">Email address</label>
<input type="text" id="email" name="email" placeholder="Email address">
</div>
<div>
<input type="submit" id="submit" name="submit" value="Submit">
</div>
</form>
<?php
$form = ob_get_clean();
return $form;
}
?>
Saat pengguna mengirimkan formulir, data dikirim ke server. Data tersebut kemudian diproses, dibersihkan, dan disimpan di basis data.
add_action( 'wp', 'wp_learn_maybe_process_form' );
function wp_learn_maybe_process_form() {
if (!isset($_POST['wp_learn_form'])){
return;
}
$name = sanitize_text_field( $_POST['name'] );
$email = sanitize_email( $_POST['email'] );
global $wpdb;
$table_name = $wpdb->prefix . 'form_submissions';
$sql = "INSERT INTO $table_name (name, email) VALUES ('$name', '$email')";
$result = $wpdb->query($sql);
if ( 0 < $result ) {
wp_redirect( WPLEARN_SUCCESS_PAGE_SLUG );
die();
}
wp_redirect( WPLEARN_ERROR_PAGE_SLUG );
die();
}
Karena formulir dapat muncul di halaman mana pun tempat shortcode digunakan, pengguna jahat dapat mencoba mengirimkan permintaan POST ke formulir, baik untuk mencari kerentanan dalam plugin maupun dengan mengirimkan banyak permintaan ke formulir.
Untuk mencegahnya, Anda dapat memeriksa bahwa permintaan berasal dari sumber tepercaya. Untuk melakukannya, Anda dapat menerapkan sesuatu yang disebut nonce, atau angka yang digunakan sekali.
Pertama, di dalam formulir itu sendiri, Anda dapat menambahkan kolom nonce menggunakan fungsi wp_nonce_field, dengan meneruskan tindakan nonce dan nama nonce ke fungsi tersebut:
wp_nonce_field( 'wp_learn_form_nonce_action', 'wp_learn_form_nonce_field' );
Saat formulir ditampilkan di bagian depan situs, sebuah kolom tersembunyi ditambahkan ke formulir menggunakan nama nonce sebagai atribut id dan name pada kolom tersembunyi tersebut, serta nonce yang dihasilkan sebagai nilai kolom. Data ini dikirim saat formulir dikirimkan.
Kemudian, dalam fungsi yang memproses data formulir, Anda dapat memverifikasi bahwa nonce valid menggunakan fungsi wp_verify_nonce, dengan meneruskan kolom nonce yang dikirim melalui POST dan tindakan nonce ke fungsi tersebut. Jika hasil verifikasi ini salah, Anda dapat keluar lebih awal sehingga mencegah eksekusi kode lebih lanjut.
if ( ! wp_verify_nonce( $_POST['wp_learn_form_nonce_field'], 'wp_learn_form_nonce_action' ) {
wp_redirect( WPLEARN_ERROR_PAGE_SLUG );
die();
}
Setiap kali kode Anda membuat permintaan web, baik melalui pengalihan ke URL baru, pengiriman data POST ke formulir, maupun pembuatan permintaan AJAX, Anda harus memeriksa bahwa permintaan tersebut valid.
Untuk membaca lebih lanjut tentang cara menggunakan nonce dalam plugin Anda, lihat bagian Nonce di Dokumentasi Pengembang WordPress.
Mencegah pengguna yang tidak terautentikasi
Bergantung pada fungsionalitas kode Anda, sebaiknya batasi fitur tertentu hanya untuk pengguna dengan tingkat izin tertentu. Misalnya, Anda mungkin memiliki fungsi yang menghapus data dari basis data.
function wp_learn_delete_form_submission() {
if ( ! isset( $_POST['id'] ) ) {
wp_send_json_error( 'Invalid ID' );
}
$id = (int) $_POST['id'];
global $wpdb;
$table_name = $wpdb->prefix . 'form_submissions';
$sql = "DELETE FROM $table_name WHERE id = $id";
$result = $wpdb->get_results( $sql );
return wp_send_json( array( 'result' => $result ) );
}
Meskipun Anda mungkin telah berusaha sebaik mungkin untuk mencegah siapa pun yang tidak memiliki izin menjalankan fungsi ini, tetap merupakan langkah yang baik untuk menyertakan pemeriksaan terhadap situasi tersebut.
WordPress menyertakan sistem Peran dan Kapabilitas Pengguna yang tangguh, yang memungkinkan Anda menggunakan peran dan kapabilitas pengguna bawaan atau membuatnya sendiri.
Dalam kasus ini, Anda cukup mengizinkan pengguna yang memiliki kapabilitas untuk mengelola opsi situs, yaitu kapabilitas standar yang disertakan dalam peran administrator.
function wp_learn_delete_form_submission() {
if ( ! current_user_can( 'manage_options' ) ) {
return wp_send_json( array( 'result' => 'Authentication error' ) );
}
// rest of function code
}
Dokumentasi Pengembang WordPress memiliki bagian terperinci tentang Peran dan Kapabilitas Pengguna, yang mencakup daftar kapabilitas bawaan serta cara membuat kapabilitas khusus.
Bacaan lebih lanjut
Untuk mempersiapkan diri agar dapat mengembangkan dengan mempertimbangkan keamanan, pastikan Anda membaca entri tentang Keamanan di Dokumentasi Pengembang WordPress, karena entri tersebut mencakup semua contoh dalam pelajaran ini, serta informasi tambahan tentang praktik terbaik keamanan, kerentanan umum, dan lebih banyak contoh kode.