Struktur Aplikasi Web Modern

Sebelum Anda dapat mengevaluasi aplikasi web secara efektif untuk tujuan rekonstruksi, sebaiknya pahami dulu teknologi umum yang digunakan banyak aplikasi web sebagai dependensi. Dependensi ini mencakup mulai dari pustaka bantuan JavaScript dan modul CSS yang telah ditentukan sebelumnya, hingga server web dan bahkan sistem operasi. Dengan memahami peran dependensi ini dan implementasinya yang umum dalam tumpukan aplikasi, akan jauh mudah untuk mengidentifikasinya dengan cepat dan mencari kesalahan konfigurasi.

Aplikasi Web Modern Versus Lama

Aplikasi web saat ini sering kali dibangun di atas teknologi yang belum ada 10 tahun lalu. Peralatan yang tersedia untuk membangun aplikasi web telah berkembang pesat pada masa itu, sehingga terkadang tampak seperti spesialisasi yang sama sekali berbeda saat ini.

Satu dekade yang lalu, sebagian besar aplikasi web dibangun menggunakan kerangka kerja sisi server yang merender halaman HTML/JS/CSS yang kemudian akan dikirim ke klien. Ketika membutuhkan pembaruan, klien cukup meminta halaman lain dari server untuk dirender dan disalurkan melalui HTTP.

Tak lama setelah itu, aplikasi web mulai lebih sering menggunakan HTTP seiring dengan munculnya Ajax (JavaScript dan XML asinkron), yang memungkinkan permintaan jaringan dibuat dari dalam sesi halaman melalui JavaScript.

Saat ini, banyak aplikasi sebenarnya lebih tepat direpresentasikan sebagai dua atau lebih aplikasi yang berkomunikasi melalui protokol jaringan, dibandingkan dengan satu aplikasi monolitik. Ini merupakan salah satu perbedaan arsitektur utama antara aplikasi web saat ini dan aplikasi web satu dekade lalu.

Sering kali, aplikasi web saat ini terdiri dari beberapa aplikasi yang terhubung dengan Representational State Transfer (REST) ​​API. API ini bersifat stateless dan hanya ada untuk memenuhi permintaan dari satu aplikasi ke aplikasi lainnya. Ini berarti mereka tidak benar-benar menyimpan informasi apa pun tentang peminta.

Banyak aplikasi klien (UI) saat ini yang berjalan di peramban lebih mirip dengan aplikasi desktop tradisional. Aplikasi klien ini mengelola siklus hidupnya sendiri, meminta datanya sendiri, dan tidak memerlukan pemuatan ulang halaman setelah bootstrap awal selesai.

Bukan hal yang aneh bagi aplikasi mandiri yang diterapkan ke peramban web untuk berkomunikasi dengan banyak server.

Pertimbangkan aplikasi hosting gambar yang memungkinkan login pengguna—kemungkinan besar aplikasi tersebut akan memiliki server hosting/distribusi khusus yang terletak di satu URL, dan URL terpisah untuk mengelola basis data dan login.

Dapat dikatakan bahwa aplikasi saat ini sering kali sebenarnya merupakan kombinasi dari banyak aplikasi terpisah namun simbiosis yang bekerja bersama secara serempak. Hal ini dapat dikaitkan dengan pengembangan protokol jaringan yang lebih terdefinisi dengan jelas dan pola arsitektur API.

Rata-rata aplikasi web modern mungkin menggunakan beberapa teknologi berikut:

  • REST API

  • JSON atau XML

  • JavaScript

  • Kerangka kerja SPA (React, Vue, EmberJS, AngularJS)

  • Sistem autentikasi dan otorisasi

  • Satu atau lebih server web (biasanya pada server Linux)

  • Satu atau lebih paket perangkat lunak server web (ExpressJS, Apache, NginX)

  • Satu atau lebih basis data (MySQL, MongoDB, dll.)

  • Penyimpanan data lokal pada klien (cookie, penyimpanan web, IndexDB)

Catatan

Daftar ini tidak lengkap, dan mengingat saat ini terdapat miliaran situs web individual di internet, mustahil untuk membahas semua teknologi aplikasi web dalam buku ini.

Anda sebaiknya menggunakan buku dan situs web pemrograman lain, seperti Stack Overflow, jika Anda perlu mempelajari teknologi tertentu yang tidak tercantum dalam bab ini.

Beberapa teknologi ini sudah ada satu dekade lalu, tetapi tidak adil untuk mengatakan bahwa teknologi tersebut tidak berubah dalam kurun waktu tersebut. Basis data telah ada selama beberapa dekade, tetapi basis data NoSQL dan basis data sisi klien jelas merupakan perkembangan yang lebih baru. Pengembangan aplikasi JavaScript tumpukan penuh juga tidak mungkin dilakukan hingga NodeJS dan npm mulai diadopsi dengan cepat. Lanskap aplikasi web telah berubah begitu cepat dalam satu dekade terakhir atau lebih sehingga banyak dari teknologi ini telah beralih dari tidak dikenal menjadi hampir ada di mana-mana.

Bahkan ada lebih banyak teknologi di masa depan: misalnya, API Cache untuk menyimpan permintaan secara lokal, dan Web Sockets sebagai protokol jaringan alternatif untuk komunikasi klien-ke-server (atau bahkan klien-ke-klien). Pada akhirnya, peramban bermaksud untuk sepenuhnya mendukung variasi kode rakitan yang dikenal sebagai web assembly, yang akan memungkinkan bahasa non-JavaScript digunakan untuk menulis kode sisi klien di peramban.

Setiap teknologi baru dan yang akan datang ini membawa celah keamanan baru yang dapat ditemukan dan dieksploitasi, baik untuk kebaikan maupun kejahatan. Saat ini merupakan masa yang menarik untuk berkecimpung dalam bisnis mengeksploitasi atau mengamankan aplikasi web.

Sayangnya, saya tidak dapat memberikan penjelasan mengenai setiap teknologi yang digunakan di web saat ini—itu akan membutuhkan buku tersendiri! Namun, sisa bab ini akan memberikan pengantar tentang teknologi-teknologi yang telah disebutkan sebelumnya. Silakan fokuskan pada teknologi yang belum Anda kenal secara mendalam.

REST APIs

REST singkatan dari Representational State Transfer, yang merupakan cara canggih untuk mendefinisikan API yang memiliki beberapa karakteristik unik:

  • Harus terpisah dari klien.

    REST API dirancang untuk membangun aplikasi web yang sangat skalabel, namun tetap sederhana. Memisahkan klien dari API tetapi mengikuti struktur API yang ketat memudahkan aplikasi klien untuk meminta sumber daya dari API tanpa dapat melakukan panggilan ke basis data atau menjalankan logika sisi server sendiri.

  • Harus stateless.

    Secara desain, REST API hanya menerima masukan dan menyediakan keluaran. API tidak boleh menyimpan status apa pun terkait koneksi klien. Namun, ini tidak berarti bahwa REST API tidak dapat melakukan autentikasi dan otorisasi—sebaliknya, otorisasi harus ditokenisasi dan dikirim pada setiap permintaan.

  • Harus mudah di-cache

    Untuk menskalakan aplikasi web yang dikirimkan melalui internet dengan tepat, REST API harus dapat dengan mudah menandai responsnya sebagai dapat di-cache atau tidak. Karena REST juga mencakup definisi yang sangat ketat tentang data apa yang akan disajikan dari titik akhir mana, hal ini sebenarnya sangat mudah dikonfigurasi pada REST API yang dirancang dengan tepat. Idealnya, cache harus dikelola secara terprogram agar tidak secara tidak sengaja membocorkan informasi istimewa kepada pengguna lain.

  • Setiap titik akhir harus mendefinisikan objek atau metode tertentu.

    Biasanya, ini didefinisikan secara hierarkis; misalnya, /moderators/joe/logs/12_21_2018. Dengan demikian, REST API dapat dengan mudah menggunakan kata kerja HTTP seperti GET, POST, PUT, dan DELETE. Akibatnya, satu titik akhir dengan beberapa kata kerja HTTP menjadi terdokumentasi sendiri.

Ingin mengubah akun moderator "joe"? Gunakan PUT /moderators/joe. Ingin menghapus log 12_21_2018? Cukup dengan deduksi sederhana: HAPUS /moderators/joe/logs/12_21_2018.

Karena REST API mengikuti pola arsitektur yang terdefinisi dengan baik, alat seperti Swagger dapat dengan mudah diintegrasikan ke dalam aplikasi dan mendokumentasikan titik akhir sehingga pengembang lain lebih mudah memahami maksud titik akhir (lihat Gambar 3-1).

../_images/03-01-apiblog2.png

Di masa lalu, sebagian besar aplikasi web menggunakan API berstruktur Simple Object Access Protocol (SOAP). REST memiliki beberapa keunggulan dibandingkan SOAP:

  • Meminta data target, bukan fungsi

  • Caching permintaan yang mudah

  • Sangat skalabel

Selain itu, meskipun API SOAP harus menggunakan XML sebagai format data in-transit, API REST dapat menerima format data apa pun, tetapi biasanya yang digunakan adalah JSON. JSON jauh lebih ringan (kurang bertele-tele) dan lebih mudah dibaca manusia daripada XML, yang juga memberikan REST keunggulan dibandingkan pesaingnya.

Berikut adalah contoh payload yang ditulis dalam XML:

<user>
  <username>joe</username>
  <password>correcthorsebatterystaple</password>
  <email>joe@website.com</email>
  <joined>12/21/2005</joined>
  <client-data>
    <timezone>UTF</timezone>
    <operating-system>Windows 10</operating-system>
    <licenses>
      <videoEditor>abc123-2005</videoEditor>
      <imageEditor>123-456-789</imageEditor>
    </licenses>
  </client-data>
</user>

Dan serupa, muatan yang sama ditulis dalam JSON:

{
  "username": "joe",
  "password": "correcthorsebatterystaple",
  "email": "joe@website.com",
  "joined": "12/21/2005",
  "client_data": {
    "timezone": "UTF",
    "operating_system": "Windows 10",
    "licenses": {
      "videoEditor": "abc123-2005",
      "imageEditor": "123-456-789"
    }
  }
}

Sebagian besar aplikasi web modern yang akan Anda temui menggunakan API RESTful, atau API mirip REST yang menyajikan JSON. Semakin jarang menemukan API SOAP dan XML di luar aplikasi perusahaan tertentu yang mempertahankan desain kaku seperti itu untuk kompatibilitas lama.

Memahami struktur API REST penting saat Anda mencoba merekayasa balik lapisan API aplikasi web. Menguasai dasar-dasar API REST akan memberi Anda keuntungan, karena Anda akan menemukan bahwa banyak API yang ingin Anda selidiki mengikuti arsitektur REST—tetapi selain itu, banyak alat yang mungkin ingin Anda gunakan atau integrasikan dengan alur kerja Anda akan diekspos melalui API REST.

Notasi Objek JavaScript

REST adalah spesifikasi arsitektur yang mendefinisikan bagaimana verba HTTP harus dipetakan ke sumber daya (titik akhir API dan fungsionalitas) di server. Sebagian besar API REST saat ini menggunakan JSON sebagai format data in-transit-nya.

Pertimbangkan hal ini: server API aplikasi harus berkomunikasi dengan kliennya (biasanya berupa kode di peramban atau aplikasi seluler). Tanpa hubungan klien/server, kita tidak dapat menyimpan status di seluruh perangkat, dan mempertahankan status tersebut di antara akun. Semua status harus disimpan secara lokal.

Karena aplikasi web modern membutuhkan banyak komunikasi klien/server (untuk pertukaran data hilir, dan permintaan hulu dalam bentuk verba HTTP), tidaklah layak untuk mengirim data dalam format ad hoc. Format in-transit data harus distandarisasi.

JSON adalah salah satu solusi potensial untuk masalah ini. JSON adalah format berkas standar terbuka (bukan hak milik) yang memenuhi sejumlah persyaratan menarik:

  • Sangat ringan (mengurangi bandwidth jaringan).

  • Membutuhkan penguraian yang sangat sedikit (mengurangi beban perangkat keras server/klien).

  • Mudah dibaca manusia.

  • Bersifat hierarkis (dapat merepresentasikan hubungan kompleks antardata).

  • Objek JSON direpresentasikan sangat mirip dengan objek JavaScript, sehingga memudahkan penggunaan JSON dan membuat objek JSON baru di peramban.

Semua peramban utama saat ini mendukung penguraian JSON secara native (dan cepat!), yang, selain poin-poin sebelumnya, menjadikan JSON format yang hebat untuk mentransmisikan data antara server stateless dan peramban web.

JSON berikut:

{
  "first": "Sam",
  "last": "Adams",
  "email": "sam.adams@company.com",
  "role": "Engineering Manager",
  "company": "TechCo.",
  "location": {
    "country": "USA",
    "state": "california",
    "address": "123 main st.",
    "zip": 98404
  }
}

dapat diurai dengan mudah menjadi objek JavaScript di browser:

const jsonString = `{
  "first": "Sam",
  "last": "Adams",
  "email" "sam.adams@company.com",
  "role": "Engineering Manager",
  "company": "TechCo.",
  "location": {
    "country": "USA",
    "state": "california",
    "address": "123 main st.",
    "zip": 98404
  }
}`;

// convert the string sent by the server to an object
const jsonObject = JSON.parse(jsonString);

JSON fleksibel, ringan, dan mudah digunakan. Namun, JSON memiliki kekurangan, karena setiap format ringan memiliki kelebihan dan kekurangan dibandingkan dengan alternatif yang lebih berat. Hal ini akan dibahas nanti di buku ini ketika kita mengevaluasi perbedaan keamanan khusus antara JSON dan para pesaingnya, tetapi untuk saat ini, penting untuk dipahami bahwa sejumlah besar permintaan jaringan antara peramban dan server dikirim sebagai JSON saat ini.

Biasakan diri Anda membaca string JSON, dan pertimbangkan untuk memasang plugin di peramban atau editor kode Anda untuk memformat string JSON. Kemampuan untuk mengurai string ini dengan cepat dan menemukan kunci spesifik akan sangat berharga saat melakukan uji penetrasi berbagai macam API dalam waktu singkat.

JavaScript

Di sepanjang buku ini, kita akan terus membahas aplikasi klien dan server.

Server adalah komputer (biasanya yang canggih) yang berada di pusat data (terkadang disebut cloud), dan bertanggung jawab untuk menangani permintaan ke situs web. Terkadang, server-server ini sebenarnya merupakan kluster dari banyak server; di lain waktu, server mungkin hanya berupa satu server ringan yang digunakan untuk pengembangan atau pencatatan.

Di sisi lain, klien adalah perangkat apa pun yang dapat diakses pengguna yang mereka manipulasi untuk menggunakan aplikasi web. Klien bisa berupa ponsel, kios mal, atau layar sentuh di mobil listrik—tetapi untuk keperluan kita, biasanya hanya berupa peramban web.

Server dapat dikonfigurasi untuk menjalankan hampir semua perangkat lunak yang dapat Anda bayangkan, dalam bahasa apa pun yang dapat Anda bayangkan. Server web saat ini berjalan dengan Python, Java, JavaScript, C++, dll. Klien (khususnya, peramban) tidak memiliki kemewahan itu. JavaScript bukan hanya bahasa pemrograman, tetapi juga satu-satunya bahasa pemrograman untuk skrip sisi klien di peramban web. JavaScript adalah bahasa pemrograman dinamis yang awalnya dirancang untuk digunakan di peramban internet. JavaScript sekarang digunakan di banyak aplikasi, dari seluler hingga internet of things, atau IoT (lihat Gambar 3-2).

Banyak contoh kode di seluruh buku ini ditulis dalam JavaScript (lihat Gambar 3-2). Jika memungkinkan, contoh kode backend juga ditulis menggunakan sintaksis JavaScript sehingga tidak ada waktu yang terbuang dalam peralihan konteks. JavaScript kini digunakan di banyak aplikasi, mulai dari seluler hingga IoT.

../_images/03-02-JavaScript-example.png

Saya akan berusaha menjaga JavaScript tetap bersih dan sesederhana mungkin, tetapi saya mungkin menggunakan beberapa konstruksi yang didukung JavaScript yang tidak sepopuler (atau terkenal) dalam bahasa pemrograman lain.

JavaScript adalah bahasa pemrograman yang unik karena pengembangannya bergantung pada perkembangan peramban, dan mitranya, Model Objek Dokumen (DOM). Oleh karena itu, ada beberapa keanehan yang mungkin perlu Anda ketahui sebelum melanjutkan.

Variabel dan Cakupan

Dalam JavaScript ES6 (versi terbaru), ada empat cara untuk mendefinisikan variabel:

// global definition
age = 25;
// function scoped
var age = 25;
// block scoped
let age = 25;
// block scoped, without reassignment
const age = 25;

Semua ini mungkin tampak serupa, tetapi secara fungsional sangat berbeda.

age = 25

Tanpa menyertakan pengenal seperti var, let, atau const, variabel apa pun yang Anda definisikan akan dimasukkan ke dalam lingkup global. Ini berarti bahwa objek lain yang didefinisikan sebagai anak dari lingkup global akan dapat mengakses variabel tersebut. Secara umum, hal ini dianggap sebagai praktik yang buruk dan kita harus menghindarinya. (Hal ini juga dapat menjadi penyebab kerentanan keamanan yang signifikan atau bug fungsional.)

Perlu dicatat bahwa semua variabel yang tidak memiliki pengenal juga akan memiliki penunjuk yang ditambahkan ke objek jendela di peramban:

// define global integer
age = 25;
// direct call (returns 25)
console.log(age);
// call via pointer on window (returns 25)
console.log(window.age);

Hal ini, tentu saja, dapat menyebabkan konflik penamaan ruang pada window (sebuah objek yang diandalkan oleh DOM browser untuk mempertahankan status window), yang merupakan alasan bagus lainnya untuk menghindarinya.

var age = 25

Setiap variabel yang didefinisikan dengan pengidentifikasi var dicakup ke fungsi terdekat, atau secara global jika tidak ada blok fungsi luar yang didefinisikan (dalam kasus global,variabel tersebut muncul di jendela serupa dengan variabel tanpa pengidentifikasi seperti yang ditunjukkan sebelumnya).

Jenis variabel ini agak membingungkan, yang mungkin menjadi bagian alasan mengapa let akhirnya diperkenalkan.

const func = function() {
  if (true) {
    // define age inside of if block
    var age = 25;
  }

/*
 *
 * logging age will return 25
 * this happens because the var identifier binds to the nearest
 * function, rather than the nearest block.
 */
  console.log(age);
};

Pada contoh sebelumnya, sebuah variabel didefinisikan menggunakan pengidentifikasi var dengan nilai 25. Di sebagian besar bahasa pemrograman modern lainnya, usia akan menjadi tidak terdefinisi ketika mencoba mencatatnya. Sayangnya, var tidak mengikuti aturan umum ini dan membatasi dirinya pada fungsi, alih-alih blok. Hal ini dapat menyebabkan pengembang JavaScript baru terjebak dalam kebingungan dalam men-debug.

let age = 25

ECMAScript 6 (spesifikasi untuk JavaScript) memperkenalkan let dan const—dua cara menginstansiasi objek yang bertindak lebih mirip dengan yang ada dalam bahasa pemrograman modern lainnya.

Seperti yang Anda duga, let memiliki cakupan blok. Artinya:

const func = function() {
  if (true) {
    // define age inside of if block
    let age = 25;
  }

  /*
   * This time, console.log(age) will return `undefined`.
   *
   * This is because `let`, unlike `var` binds to the nearest block.
   * Binding scope to the nearest block, rather than the nearest function
   * is generally considered to be better for readability, and
   * results in a reduction of scope-related bugs.
   */
  console.log(age);
};

.

const age = 25

const, seperti let, juga memiliki cakupan blok, tetapi juga tidak dapat ditetapkan ulang. Hal ini membuatnya mirip dengan variabel final dalam bahasa seperti Java.

const func = function() {
  const age = 25;

  /*
   * This will result in: TypeError: invalid assignment to const `age`
   *
   * Much like `let`, `const` is block scoped.
   * The major difference is that `const` variables do not support
   * reassignment after they are instantiated.
   *
   * If an object is declared as a const, its properties can still be
   * changed. As a result, `const` ensures the pointer to `age` in memory
   * is not changed, but does not care if the value of `age` or a property
   * on `age` changes.
   */

   age = 35;
};

Secara umum, Anda harus selalu berusaha menggunakan let dan const dalam kode Anda untuk menghindari bug dan meningkatkan keterbacaan.

Fungsi

Dalam JavaScript, fungsi adalah objek. Artinya, fungsi dapat ditetapkan dan ditetapkan ulang menggunakan variabel dan pengidentifikasi dari bagian sebelumnya.

Ini semua adalah fungsi:

// anonymous function
function () {};
// globally declared named function
a = function() {};
// function scoped named function
var a = function() { };
// block scoped named function
let a = function () {};
// block scoped named function without re-assignment
const a = function () {};
// anonymous function inheriting parent context
() => {};
// immediately invoked function expression (IIFE)
(function() { })();

Fungsi pertama adalah fungsi anonim — artinya tidak dapat direferensikan setelah dibuat. Empat fungsi berikutnya hanyalah fungsi dengan cakupan yang ditentukan berdasarkan pengenal yang diberikan. Ini sangat mirip dengan cara kita sebelumnya membuat variabel untuk age. Fungsi keenam adalah fungsi singkat — fungsi ini berbagi konteks dengan induknya (lebih lanjut tentang itu nanti).

Fungsi terakhir adalah jenis fungsi khusus yang mungkin hanya akan Anda temukan di JavaScript, yang dikenal sebagai IIFE — ekspresi fungsi yang langsung dipanggil. Ini adalah fungsi yang langsung aktif ketika dimuat dan berjalan di dalam namespace-nya sendiri. Ini digunakan oleh pengembang JavaScript yang lebih mahir untuk mengenkapsulasi blok kode agar tidak dapat diakses di tempat lain.

Konteks

Jika Anda dapat menulis kode dalam bahasa lain (non-JavaScript), ada lima hal yang perlu Anda pelajari untuk menjadi pengembang JavaScript yang baik: cakupan, konteks, pewarisan prototipe, asinkroni, dan DOM peramban.

Setiap fungsi dalam JavaScript memiliki serangkaian properti dan data yang melekat padanya. Kami menyebutnya konteks fungsi. Konteks tidak bersifat tetap, dan dapat dimodifikasi selama runtime. Objek yang disimpan dalam konteks fungsi dapat direferensikan menggunakan kata kunci this:

const func = function() {
  this.age = 25;

  // will return 25
  console.log(this.age);
  };

// will return undefined
console.log(this.age);

Seperti yang bisa Anda bayangkan, banyak bug pemrograman yang mengganggu disebabkan oleh konteks yang sulit di-debug—terutama ketika konteks suatu objek harus diteruskan ke fungsi lain.

JavaScript memperkenalkan beberapa solusi untuk masalah ini guna membantu pengembang dalam berbagi konteks antarfungsi:

// create a new getAge() function clone with the context from ageData
// then call it with the param 'joe'
const getBoundAge = getAge.bind(ageData)('joe');

// call getAge() with ageData context and param joe
const boundAge = getAge.call(ageData,'joe');

// call getAge() with ageData context and param joe
const boundAge = getAge.apply(ageData, ['joe']);

Ketiga fungsi ini, bind, call, dan apply, memungkinkan pengembang untuk memindahkan konteks dari satu fungsi ke fungsi lainnya. Satu-satunya perbedaan antara call dan apply adalah call mengambil daftar argumen, sedangkan apply mengambil array argumen. Keduanya dapat dipertukarkan dengan mudah:

// destructure array into list
const boundAge = getAge.call(ageData, ...['joe']);

Tambahan baru lainnya untuk membantu programmer dalam mengelola konteks adalah fungsi panah, yang juga disebut fungsi singkat . Fungsi ini mewarisi konteks dari induknya, memungkinkan konteks dibagikan dari fungsi induk ke anaknya tanpa memerlukan pemanggilan/penerapan atau pengikatan eksplisit:

// global context
this.garlic = false;

// soup recipe
const soup = { garlic: true };

// standard function attached to soup object
soup.hasGarlic1 = function() { console.log(this.garlic); }
// true

// arrow function attached to global context
soup.hasGarlic2 = () => { console.log(this.garlic); } //
false

Menguasai cara-cara mengelola konteks ini akan membuat pengintaian melalui server atau klien berbasis JavaScript jauh lebih mudah dan cepat. Anda bahkan mungkin menemukan beberapa kerentanan khusus bahasa pemrograman yang muncul dari kompleksitas ini.

Pewarisan Prototipe

Prototypal Inheritance, tidak seperti banyak bahasa pemrograman sisi server tradisional yang menyarankan penggunaan model pewarisan berbasis kelas, JavaScript telah dirancang dengan sistem pewarisan prototipe yang sangat fleksibel. Sayangnya, karena hanya sedikit bahasa pemrograman yang menggunakan sistem pewarisan jenis ini, sistem ini sering diabaikan oleh pengembang, banyak di antaranya yang mencoba mengubahnya menjadi sistem berbasis kelas.

Dalam sistem berbasis classes, kelas beroperasi seperti cetak biru yang mendefinisikan objek. Dalam sistem semacam itu, kelas dapat mewarisi dari kelas lain dan menciptakan hubungan hierarkis dengan cara ini. Dalam bahasa seperti Java, subkelas dihasilkan dengan kata kunci extends, atau diinstansikan dengan kata kunci new.

JavaScript tidak sepenuhnya mendukung jenis classes ini, tetapi karena fleksibilitas pewarisan prototipe, dimungkinkan untuk meniru fungsionalitas persis kelas dengan beberapa abstraksi di atas sistem prototipe JavaScript. Dalam sistem pewarisan prototipe, seperti dalam JavaScript, setiap objek yang dibuat memiliki properti yang disebut prototipe. Properti prototipe dilengkapi dengan properti konstruktor yang mengarah kembali ke fungsi yang memiliki prototipe tersebut. Ini berarti bahwa objek apa pun dapat digunakan untuk menginstansiasi objek baru, karena konstruktor mengarah ke objek yang berisi prototipe yang memuat konstruktor tersebut. Ini mungkin membingungkan, tetapi berikut adalah contohnya:

/*
 *
 * A vehicle pseudoclass written in JavaScript.
 * This is simple on purpose, in order to more clearly demonstrate
 * prototypal inheritance fundamentals.
 */
const Vehicle = function(make, model) {
  this.make = make;
  this.model = model;

  this.print = function() {
    return `${this.make}: ${this.model}`;
  };
};
const prius = new Vehicle('Toyota','Prius');
console.log(prius.print());

Ketika objek baru dibuat di JavaScript, sebuah objek terpisah juga dibuat, yang disebut __proto__. Objek ini menunjuk ke prototipe yang konstruktornya dipanggil saat pembuatan objek tersebut. Hal ini memungkinkan perbandingan antar objek, misalnya:

const prius = new Vehicle('Toyota', 'Prius');
const charger = new Vehicle('Dodge' 'Charger');

/*
 * As we can see, the "Prius" and "Charger" objects were both
 * created based off of "Vehicle".
 */

prius.__proto__ === charger.__proto__;

Seringkali, prototipe pada suatu objek dimodifikasi oleh pengembang, yang menyebabkan perubahan yang membingungkan pada fungsionalitas aplikasi web. Terutama, karena semua objek dalam JavaScript dapat diubah secara default, perubahan pada properti prototipe dapat terjadi kapan saja selama runtime.

Menariknya, ini berarti bahwa tidak seperti model pewarisan yang dirancang lebih kaku, pohon pewarisan JavaScript dapat berubah saat runtime. Akibatnya, objek dapat berubah bentuk saat runtime:

const prius = new Vehicle('Toyota', 'Prius');
const charger = new Vehicle('Dodge', 'Charger');

/*
 *
 * This will fail, because the Vehicle object
 * does not have a "getMaxSpeed" function.
 * Hence, objects inheriting from Vehicle do not have such a function
 * either.
 */
console.log(prius.getMaxSpeed()); // Error: getMaxSpeed is not a function

/*
 * Now we will assign a getMaxSpeed() function to the prototype of Vehicle,
 * all objects inheriting from Vehicle will be updated in real time as
 * prototypes propagate from the Vehicle object to its children.
 */
Vehicle.prototype.getMaxSpeed = function() {
  return 100; // mph
};

/*
 * Because the Vehicle's prototype has been updated, the
 * getMaxSpeed function will now function on all child objects.
 */
prius.getMaxSpeed(); // 100
charger.getMaxSpeed(); // 100

Prototipe membutuhkan waktu untuk terbiasa, tetapi pada akhirnya kekuatan dan fleksibilitasnya mengalahkan kesulitan apa pun yang ada dalam kurva pembelajaran. Prototipe sangat penting untuk dipahami ketika mempelajari keamanan JavaScript, karena hanya sedikit pengembang yang sepenuhnya memahaminya.

Selain itu, karena prototipe menyebar ke turunan ketika dimodifikasi, jenis serangan khusus ditemukan dalam sistem berbasis JavaScript yang disebut Prototype Pollution. Serangan ini melibatkan modifikasi pada objek JavaScript induk, yang secara tidak sengaja mengubah fungsionalitas objek turunan.

Asynchrony

Asinkroni adalah salah satu konsep "sulit dipahami, mudah diingat" yang tampaknya sering muncul dalam pemrograman jaringan. Karena peramban harus berkomunikasi dengan server secara teratur, dan waktu antara permintaan dan respons tidak standar (dengan mempertimbangkan ukuran muatan, latensi, dan waktu pemrosesan server), asinkroni sering digunakan di web untuk menangani variasi tersebut.

Dalam model pemrograman sinkron, operasi dilakukan sesuai urutan kemunculannya. Misalnya:

console.log('a');
console.log('b');
console.log('c');
// a
// b
// c

Dalam kasus di atas, operasi terjadi secara berurutan, dengan andal mengeja "abc" setiap kali ketiga fungsi ini dipanggil dalam urutan yang sama.

Dalam model pemrograman asinkron, ketiga fungsi dapat dibaca dalam urutan yang sama oleh interpreter setiap kali, tetapi mungkin tidak diselesaikan dalam urutan yang sama. Perhatikan contoh ini, yang bergantung pada fungsi pencatatan asinkron:

// --- Attempt #1 ---
async.log('a');
async.log('b');
async.log('c');
// a
// b
// c

// --- Attempt #2 ---
async.log('a');
async.log('b');
async.log('c');
// a
// c
// b

// --- Attempt #3 ---
async.log('a');
async.log('b');
async.log('c');
// a
// b
// c

Saat fungsi logging dipanggil untuk kedua kalinya, fungsi tersebut tidak diselesaikan secara berurutan. Mengapa?

Saat menangani pemrograman jaringan, permintaan sering kali membutuhkan waktu yang bervariasi, waktu habis, dan beroperasi secara tidak terduga. Dalam aplikasi web berbasis JavaScript, hal ini sering ditangani melalui model pemrograman asinkron, alih-alih hanya menunggu satu permintaan selesai sebelum memulai permintaan lain. Manfaatnya adalah peningkatan kinerja yang sangat besar yang bisa puluhan kali lebih cepat daripada alternatif sinkron. Alih-alih memaksa permintaan untuk diselesaikan satu demi satu, kami memulai semuanya secara bersamaan dan kemudian memprogram apa yang seharusnya dilakukan setelah penyelesaian—sebelum penyelesaian terjadi.

Dalam versi JavaScript yang lebih lama, hal ini biasanya dilakukan dengan sistem yang disebut callbacks:

const config = {
  privacy: public,
  acceptRequests: true
};

/*
 * First request a user object from the server.
 * Once that has completed, request a user profile from the server.
 * Once that has completed, set the user profile config.
 * Once that has completed, console.log "success!"
 */
getUser(function(user) {
  getUserProfile(user, function(profile) {
    setUserProfileConfig(profile, config, function(result) {
      console.log('success!');
    });
  });
});

Meskipun callback sangat cepat dan efisien, dibandingkan dengan model sinkron, callback sangat sulit dibaca dan di-debug.

Filosofi pemrograman selanjutnya menyarankan pembuatan objek yang dapat digunakan kembali yang akan memanggil fungsi berikutnya setelah fungsi tertentu selesai. Objek ini disebut promise, dan digunakan dalam banyak bahasa pemrograman saat ini:

const config = {
  privacy: public,
  acceptRequests: true
};

/*
 * First request a user object from the server.
 * Once that has completed, request a user profile from the server.
 * Once that has completed, set the user profile config.
 * Once that has completed, console.log "success!"
 */
const promise = new Promise((resolve, reject) => {
  getUser(function(user) {
    if (user) { return resolve(user); }
    return reject();
  });
}).then((user) => {
  getUserProfile(user, function(profile) {
    if (profile) { return resolve(profile); }
    return reject();
  });
}).then((profile) => {
  setUserProfile(profile, config, function(result) {
    if (result) { return resolve(result); }
    return reject();
  });
}).catch((err) => {
  console.log('an error occured!');
});

Kedua contoh sebelumnya menggunakan logika aplikasi yang sama persis. Perbedaannya terletak pada keterbacaan dan pengorganisasiannya. Pendekatan berbasis promise dapat dipecah lebih lanjut, berkembang secara vertikal, alih-alih horizontal, dan membuat penanganan kesalahan jauh lebih mudah. ​​Promise dan callback dapat dioperasikan bersama dan dapat digunakan bersama, tergantung pada preferensi programmer.

Metode terbaru untuk menangani asinkroni adalah fungsi async. Tidak seperti objek fungsi normal, fungsi-fungsi ini dirancang untuk mempermudah penanganan asinkroni.

Pertimbangkan fungsi async berikut:

const config = {
  privacy: public,
  acceptRequests: true
};

/*
 * First request a user object from the server.
 * Once that has completed, request a user profile from the server.
 * Once that has completed, set the user profile config.
 * Once that has completed, console.log "success!"
 */
const setUserProfile = async function() {
  let user = await getUser();
  let userProfile = await getUserProfile(user);
  let setProfile = await setUserProfile(userProfile, config);
};

setUserProfile();

Anda mungkin menyadari ini jauh lebih mudah dibaca—bagus, itulah intinya!

Fungsi asinkron mengubah fungsi menjadi promise. Setiap pemanggilan metode di dalam promise dengan await sebelumnya akan menghentikan eksekusi lebih lanjut di dalam fungsi tersebut hingga pemanggilan metode tersebut diselesaikan.

Kode di luar fungsi asinkron masih dapat beroperasi secara normal. Pada dasarnya, fungsi asinkron mengubah fungsi normal menjadi promise. Anda akan semakin sering melihat hal ini dalam kode sisi klien, dan kode sisi server berbasis JavaScript seiring berjalannya waktu.

Browser DOM

You should now have sufficient understanding of asynchronous programming—the model that is dominant on the web and in client/server applications. With that information in your head, the final JavaScript-related concept you should be aware of is the browser DOM.

The DOM is the hierarchical representation data used to manage state in modern web browsers. Figure 3-3 shows the window object, one of the topmost standard objects defined by the DOM specification.

../_images/03-03-DOM-window-object.png

JavaScript adalah bahasa pemrograman, dan seperti bahasa pemrograman lainnya, bahasa pemrograman yang baik bergantung pada pustaka standar yang kuat. Pustaka ini, tidak seperti pustaka standar dalam bahasa lain, dikenal sebagai DOM.

DOM menyediakan fungsionalitas rutin yang teruji dengan baik dan berkinerja tinggi, serta diimplementasikan di semua peramban utama, sehingga kode Anda akan berfungsi identik atau hampir identik, apa pun peramban yang menjalankannya.

Tidak seperti pustaka standar lainnya, DOM bukan untuk menambal kekurangan fungsionalitas dalam bahasa atau menyediakan fungsionalitas umum (yang merupakan fungsi sekunder dari DOM), tetapi terutama untuk menyediakan antarmuka umum untuk mendefinisikan pohon hierarki simpul yang merepresentasikan halaman web. Anda mungkin tidak sengaja memanggil fungsi DOM dan menganggapnya sebagai fungsi JS. Contohnya adalah document.querySelector() atau document.implementation.

Objek utama yang membentuk DOM adalah window dan document, yang masing-masing didefinisikan secara cermat dalam spesifikasi yang dikelola oleh sebuah organisasi bernama WhatWG.

Terlepas dari apakah Anda seorang pengembang JavaScript, penguji pena aplikasi web, atau insinyur keamanan, mengembangkan pemahaman mendalam tentang DOM peramban dan perannya dalam aplikasi web sangat penting untuk menemukan kerentanan yang terlihat jelas pada lapisan presentasi dalam suatu aplikasi. Anggaplah DOM sebagai kerangka kerja tempat aplikasi berbasis JavaScript diterapkan kepada pengguna akhir, dan perlu diingat bahwa tidak semua celah keamanan terkait skrip merupakan akibat dari JavaScript yang tidak tepat, tetapi terkadang dapat disebabkan oleh implementasi DOM peramban yang tidak tepat.

SPA Frameworks

Situs web lama biasanya dibangun dengan kombinasi skrip ad hoc untuk memanipulasi DOM, dan banyak kode templat HTML yang digunakan kembali. Ini bukanlah model yang skalabel, dan meskipun berfungsi untuk mengirimkan konten statis kepada pengguna akhir, model ini tidak berfungsi untuk mengirimkan aplikasi yang kompleks dan kaya logika.

Perangkat lunak aplikasi desktop pada saat itu memiliki fungsionalitas yang tangguh, memungkinkan pengguna untuk menyimpan dan memelihara status aplikasi. Situs web di masa lalu tidak menyediakan jenis fungsionalitas ini, meskipun banyak perusahaan lebih suka mengirimkan aplikasi kompleks mereka melalui web karena memberikan banyak manfaat, mulai dari kemudahan penggunaan hingga pencegahan pembajakan.

Kerangka kerja aplikasi satu halaman (SPA) dirancang untuk menjembatani kesenjangan fungsionalitas antara situs web dan aplikasi desktop. Kerangka kerja SPA memungkinkan pengembangan aplikasi berbasis JavaScript kompleks yang menyimpan status internalnya sendiri, dan terdiri dari komponen UI yang dapat digunakan kembali, yang masing-masing memiliki siklus hidup yang terpelihara sendiri, mulai dari rendering hingga eksekusi logika.

Kerangka kerja SPA marak di web saat ini, mendukung aplikasi terbesar dan paling kompleks (seperti Facebook, Twitter, dan YouTube) di mana fungsionalitas menjadi kunci dan menghadirkan pengalaman aplikasi yang hampir seperti desktop.

Beberapa kerangka kerja SPA sumber terbuka terbesar saat ini adalah ReactJS, EmberJS, VueJS, dan AngularJS (Gambar 3-4). Semuanya dibangun di atas JavaScript dan DOM, tetapi membawa kompleksitas tambahan baik dari perspektif keamanan maupun fungsionalitas.

../_images/03-04-vueJS.png

Sistem Autentikasi dan Otorisasi

Di dunia di mana sebagian besar aplikasi terdiri dari klien (peramban/ponsel) dan server, dan server menyimpan data yang awalnya dikirim dari klien, sistem harus tersedia untuk memastikan bahwa akses data yang disimpan di masa mendatang berasal dari pengguna yang tepat.

Kami menggunakan istilah authenticattion untuk menggambarkan alur yang memungkinkan sistem identify pengguna. Dengan kata lain, sistem autentikasi memberi tahu kami bahwa "joe123" sebenarnya adalah "joe123" dan bukan "susan1988".

Istilah authorization digunakan untuk menggambarkan alur di dalam sistem untuk menentukan sumber daya apa yang dapat diakses oleh "joe123", berbeda dengan "susan1988". Misalnya, "joe123" seharusnya dapat mengakses foto pribadi yang diunggahnya sendiri, dan "susan1988" seharusnya dapat mengakses foto miliknya, tetapi mereka seharusnya tidak dapat mengakses foto satu sama lain.

Kedua proses ini sangat penting bagi fungsionalitas aplikasi web, dan keduanya merupakan fungsi dalam aplikasi web yang memerlukan kontrol keamanan yang tepat.

Autentikasi

Sistem autentikasi awal pada dasarnya sederhana. Misalnya, autentikasi dasar HTTP melakukan autentikasi dengan melampirkan header Otorisasi pada setiap permintaan. Header tersebut terdiri dari string yang berisi Basic: <base64-encoded username:password>. Server menerima kombinasi nama pengguna:sandi dan, pada setiap permintaan, memeriksanya dengan basis data. Tentu saja, skema autentikasi jenis ini memiliki beberapa kelemahan—misalnya, kredensial sangat mudah bocor dalam berbagai cara, mulai dari WiFi yang disusupi melalui HTTP hingga serangan XSS sederhana.

Perkembangan autentikasi selanjutnya mencakup autentikasi digest, yang menggunakan hash kriptografi, alih-alih pengodean base64. Setelah autentikasi digest, banyak teknik dan arsitektur baru bermunculan untuk autentikasi, termasuk yang tidak melibatkan kata sandi atau memerlukan perangkat eksternal.

Saat ini, sebagian besar aplikasi web memilih dari serangkaian arsitektur autentikasi, tergantung pada sifat bisnisnya. Misalnya, protokol OAuth sangat cocok untuk situs web yang ingin berintegrasi dengan situs web yang lebih besar. OAuth memungkinkan situs web besar (seperti Facebook, Google, dll.) untuk memberikan token yang memverifikasi identitas pengguna ke situs web mitra. OAuth dapat bermanfaat bagi pengguna karena data pengguna hanya perlu diperbarui di satu situs, alih-alih di beberapa situs —tetapi OAuth bisa berbahaya karena satu situs web yang disusupi dapat mengakibatkan beberapa profil yang disusupi.

Otentikasi dasar HTTP dan otentikasi digest masih digunakan secara luas saat ini, dengan digest menjadi lebih populer karena memiliki pertahanan yang lebih baik terhadap serangan intersepsi dan replay. Seringkali ini digabungkan dengan alat seperti 2FA untuk memastikan bahwa token autentikasi tidak disusupi, dan bahwa identitas pengguna yang masuk tidak berubah.

Authorization

Otorisasi adalah langkah selanjutnya setelah autentikasi. Sistem otorisasi lebih sulit dikategorikan, karena otorisasi sangat bergantung pada logika bisnis di dalam aplikasi web.

Secara umum, aplikasi yang dirancang dengan baik memiliki kelas otorisasi terpusat yang bertanggung jawab untuk menentukan apakah pengguna memiliki akses ke sumber daya atau fungsionalitas tertentu.

Jika API ditulis dengan buruk, API tersebut akan menerapkan pemeriksaan per API, yang secara manual mereproduksi fungsionalitas otorisasi. Sering kali, jika Anda dapat mengetahui bahwa suatu aplikasi mengimplementasikan ulang pemeriksaan otorisasi di setiap API, aplikasi tersebut kemungkinan akan memiliki beberapa API yang pemeriksaannya tidak memadai hanya karena kesalahan manusia.

Beberapa sumber daya umum yang harus selalu menjalani pemeriksaan otorisasi meliputi pembaruan pengaturan/profil, pengaturan ulang kata sandi, pembacaan/penulisan pesan pribadi, fungsi berbayar apa pun, dan fungsi pengguna yang ditingkatkan (seperti fungsi moderasi).

Web Server

Aplikasi web klien-server modern bergantung pada sejumlah teknologi yang dibangun di atas satu sama lain agar komponen sisi server dan komponen sisi klien berfungsi sebagaimana mestinya

Dalam kasus server, logika aplikasi berjalan di atas paket server web berbasis perangkat lunak sehingga pengembang aplikasi tidak perlu khawatir tentang penanganan permintaan dan pengelolaan proses. Perangkat lunak server web, tentu saja, berjalan di atas sistem operasi (biasanya beberapa distro Linux seperti Ubuntu, CentOS, atau RedHat), yang berjalan di atas perangkat keras fisik di suatu pusat data.

Namun, dalam hal perangkat lunak server web, ada beberapa pemain besar di dunia aplikasi web modern. Apache masih melayani hampir separuh situs web di dunia, sehingga kita dapat mengasumsikan Apache juga melayani sebagian besar aplikasi web. Apache bersifat sumber terbuka, telah dikembangkan selama sekitar 25 tahun, dan berjalan di hampir semua distro Linux, serta beberapa server Windows (lihat Gambar 3-5).

../_images/03-05-apache-http-server.png

Apache hebat bukan hanya karena komunitas kontributornya yang besar dan sifatnya yang open source, tetapi juga karena kemudahannya dalam konfigurasi dan plug-in. Apache adalah server web yang fleksibel dan kemungkinan besar akan Anda lihat dalam jangka panjang. Pesaing terbesar Apache adalah Nginx (diucapkan "Engine X"). Nginx menjalankan sekitar 30% server web dan berkembang pesat.

Meskipun Nginx dapat digunakan secara gratis, perusahaan induknya (saat ini F5 Networks) menggunakan model berbayar di mana dukungan dan fungsionalitas tambahan dikenakan biaya. Nginx digunakan untuk aplikasi bervolume tinggi dengan banyak koneksi unik, dibandingkan dengan aplikasi dengan sedikit koneksi yang membutuhkan banyak data. Aplikasi web yang melayani banyak pengguna secara bersamaan dapat mengalami peningkatan kinerja yang signifikan ketika beralih dari Apache ke Nginx, karena arsitektur Nginx memiliki overhead per koneksi yang jauh lebih rendah.

Di balik Nginx terdapat Microsoft IIS, meskipun popularitas server berbasis Windows telah menurun karena lisensi yang mahal dan kurangnya kompatibilitas dengan paket perangkat lunak sumber terbuka (OSS) berbasis Unix. IIS merupakan pilihan server web yang tepat ketika berhadapan dengan banyak teknologi khusus Microsoft, tetapi mungkin menjadi beban bagi perusahaan yang mencoba membangun di atas sumber terbuka.

Ada banyak server web yang lebih kecil di luar sana, dan masing-masing memiliki manfaat dan kekurangan keamanannya sendiri. Mengenal tiga server besar ini akan bermanfaat saat Anda mempelajari buku ini dan mempelajari cara menemukan kerentanan yang berasal dari konfigurasi yang tidak tepat, alih-alih hanya kerentanan yang terdapat dalam logika aplikasi.

Server-Side Databases

Setelah klien mengirimkan data untuk diproses ke server, server sering kali harus menyimpan data ini agar dapat diambil di sesi mendatang. Menyimpan data dalam memori tidak dapat diandalkan dalam jangka panjang, karena restart dan crash dapat menyebabkan hilangnya data. Selain itu, memori akses acak cukup mahal dibandingkan dengan disk.

Saat menyimpan data di disk, tindakan pencegahan yang tepat perlu diambil untuk memastikan bahwa data dapat diambil, disimpan, dan dikueri dengan andal dan cepat. Hampir semua aplikasi web saat ini menyimpan data yang dikirimkan pengguna dalam beberapa jenis basis data—sering kali basis data yang digunakan bervariasi tergantung pada logika bisnis dan kasus penggunaan tertentu.

Basis data SQL masih merupakan basis data tujuan umum yang paling populer di pasaran. Bahasa kueri SQL ketat, tetapi cepat dan mudah dipelajari. SQL dapat digunakan untuk apa saja mulai dari penyimpanan kredensial pengguna hingga pengelolaan objek JSON atau blob gambar kecil. Yang terbesar di antaranya adalah PostgreSQL, Microsoft SQL Server, MySQL, dan SQLite.

Ketika penyimpanan yang lebih fleksibel dibutuhkan, basis data NoSQL tanpa skema dapat digunakan. Basis data seperti MongoDB, DocumentDB, dan CouchDB menyimpan informasi sebagai "dokumen" yang terstruktur longgar, fleksibel, dan dapat dimodifikasi kapan saja, tetapi tidak semudah atau seefisien dalam hal kueri atau agregasi

Dalam lanskap aplikasi web saat ini, juga terdapat basis data yang lebih canggih dan khusus. Mesin pencari sering kali menggunakan basis data mereka sendiri yang sangat terspesialisasi yang harus disinkronkan dengan basis data utama secara berkala. Contohnya adalah Elasticsearch yang sangat populer.

Setiap jenis basis data memiliki tantangan dan risiko yang unik. Injeksi SQL adalah arketipe kerentanan yang terkenal dan efektif terhadap basis data SQL utama ketika kueri tidak dibentuk dengan benar. Namun, serangan bergaya injeksi dapat terjadi terhadap hampir semua basis data jika seorang peretas bersedia mempelajari model kueri basis data tersebut.

Perlu dipertimbangkan bahwa banyak aplikasi web modern dapat menggunakan beberapa basis data secara bersamaan, dan seringkali memang demikian. Aplikasi dengan pembangkitan kueri SQL yang cukup aman mungkin tidak memiliki kueri dan izin MongoDB atau Elasticsearch yang cukup aman.

Client-Side Data Stores

Secara tradisional, data minimal disimpan di klien karena keterbatasan teknis dan masalah kompatibilitas lintas-peramban. Hal ini berubah dengan cepat. Banyak aplikasi sekarang menyimpan status aplikasi yang signifikan di klien, seringkali dalam bentuk data konfigurasi atau skrip besar yang akan menyebabkan kemacetan jaringan jika harus diunduh setiap kali diakses.

Dalam kebanyakan kasus, kontainer penyimpanan yang dikelola peramban yang disebut penyimpanan lokal digunakan untuk menyimpan dan mengakses data kunci/nilai dari klien. Penyimpanan lokal mengikuti Same Origin Policy (SOP) yang diberlakukan peramban, yang mencegah domain lain (situs web) mengakses data yang disimpan secara lokal satu sama lain. Aplikasi web dapat mempertahankan status bahkan ketika browser atau tab ditutup (lihat Gambar 3-6).

../_images/03-06-local-storage-modern-browsers.png

Subset penyimpanan lokal yang disebut penyimpanan sesi beroperasi secara identik, tetapi hanya menyimpan data hingga tab ditutup. Jenis penyimpanan ini dapat digunakan ketika data lebih penting dan tidak boleh disimpan jika pengguna lain menggunakan mesin yang sama.

Tip

Dalam aplikasi web yang dirancang dengan buruk, penyimpanan data sisi klien juga dapat mengungkapkan informasi sensitif seperti token autentikasi atau rahasia lainnya.

Terakhir, untuk aplikasi yang lebih kompleks, dukungan peramban untuk IndexedDB tersedia di semua peramban web utama saat ini. IndexedDB adalah basis data object oriented programming (OOP) berbasis JavaScript yang mampu menyimpan dan melakukan kueri secara asinkron di latar belakang aplikasi web.

Karena IndexedDB dapat dikueri, ia menawarkan antarmuka pengembang yang jauh lebih canggih daripada yang dapat dilakukan oleh penyimpanan lokal.

IndexedDB digunakan dalam game berbasis web dan aplikasi interaktif berbasis web (seperti editor gambar).

Anda dapat memeriksa apakah peramban Anda mendukung IndexedDB dengan mengetik perintah berikut di konsol pengembang peramban: if (window.indexedDB) { console.log('true'); }.

Ringkasan

Aplikasi web modern dibangun di atas sejumlah teknologi baru yang tidak ditemukan pada aplikasi lama. Karena peningkatan luas permukaan akibat perluasan fungsionalitas ini, lebih banyak bentuk serangan dapat menargetkan aplikasi masa kini dibandingkan dengan situs web di masa lalu.

Untuk menjadi pakar keamanan dalam ekosistem aplikasi saat ini, Anda tidak hanya membutuhkan keahlian keamanan, tetapi juga beberapa tingkat keterampilan pengembangan perangkat lunak. Para peretas dan pakar keamanan terkemuka dekade ini membawa serta pengetahuan teknik yang mendalam di samping keterampilan keamanan mereka. Mereka memahami hubungan dan arsitektur antara klien dan server suatu aplikasi. Mereka dapat menganalisis perilaku aplikasi dari perspektif server, klien, atau jaringan di antaranya.

Para ahli terbaik juga memahami teknologi yang mendukung ketiga lapisan aplikasi web modern ini. Akibatnya, mereka memahami kelemahan yang melekat pada berbagai basis data, teknologi sisi klien, dan protokol jaringan.

Meskipun Anda tidak perlu menjadi insinyur perangkat lunak ahli untuk menjadi peretas atau insinyur keamanan yang terampil, keterampilan ini akan membantu Anda dan Anda akan merasa sangat berharga. Keterampilan ini akan mempercepat riset Anda dan memungkinkan Anda melihat kerentanan yang mendalam dan sulit yang tidak akan dapat Anda temukan sebelumnya.