15 Desember 2021

Fetch: *request Cross-Origin*

Jika kita mengirim request fetch dari situs web lain, itu mungkin akan gagal.

Misalnya, mari coba fetch dari http://example.com:

try {
  await fetch('http://example.com');
} catch(err) {
  alert(err); // Failed to fetch (Gagal untuk *fetch*)
}

Fetch akan gagal, seperti yang diperkirakan.

Konsep dasarnya dari origin – tiga serangkai domain/port/protokol.

request cross-origin – yang dikirimkan ke domain lain (atau sobdomain) atau protokol atau port – membutuhkan header khusus dari sisi remote.

Kebijakan yang disebut “CORS”: Cross-Origin Resource Sharing.

Kenapa CORS dibutuhkan? sejarah singkat

CORS ada untuk melindungi internet dari hacker jahat.

Seriously. Let’s make a very brief historical digression. Serius, Mari kita membuat sejarah yang singkat.

Untuk bertahun-tahun skrip dari satu situs tidak bisa mengakses konten dari situs lainnya

Aturan aman yang kuat tersebut adalah dasar dari keamanan internet. Contoh skrip jahat dari situs web hacker.com tidak bisa mengakses pesan dari pengguna di situs web gmail.com. Orang-orang merasa aman.

Javascript juga tidak memiliki metode khusus untuk menampilkan request jaringan pada saat itu. Ini adalah bahasa mainan untuk menghias halaman web.

Tetapi pengembang web menuntut lebih banyak kuasa. Bebagai trik dibuat untuk mengatasi batasan dan membuat request kepada situs web lainnya.

Menggunakan Form

Satu cara untuk berkomunikasi dengan server lain adalah dengan mengirimkan <form>. Orang-orang mengirimkannya pada <iframe>, hanya untuk tetap di halaman saat ini, seperti ini:

<!-- form target -->
<iframe name="iframe"></iframe>

<!-- form bisa dihasilkan secara dinamis dan dikirikan oleh Javascript -->
<form target="iframe" method="POST" action="http://another.com/…">
  ...
</form>

Jadi, ada kemungkinan untuk membuat request GET/POST dari situs lain, meski tanpa metode jaringan, karena form bisa mengirimkan data dari mana saja. Tetapi dilarang untuk mengakses konten <iframe> dari situs lainnya, tidak ada kemungkinan untuk membaca responnya.

Tepatnya, sebenarnya ada trik untuk melakukannya, mereka membutuhkan skrip khusus dari iframe dan halaman. Jadi komunikasi dengan iframe secara teknis mungkin. Saat ini tidak ada gunanya untuk membahas lebih detail, biarkan dinosaurus ini istirahat dengan tenang.

Menggunakan Skrip

Trik lainnya adalah menggunakan tag script. Skrip bisa memiliki src, dengan domain apapun, contoh <script src="http://another.com/…">. Ada kemungkinan untuk menjalankan skrip dari situs web apa saja.

Jika sebuah situs web, contoh another.com berniat untuk membuka data untuk akses seperti ini, kemudian protokol yang disebut “JSONP (JSON with padding)” akan digunakan.

Begini cara kerjanya.

Mari kita katakan jika, di sisi kita, perlu untuk mengambil data dari http://another.com, seperti cuaca:

  1. Pertama, terlebih dahulu, kita mendeklarasikan fungsi global untuk menerima data, contoh gotWeather.

    // 1. Deklarasikan fungsi untuk memproses data cuaca
    function gotWeather({ temperature, humidity }) {
      alert(`temperature: ${temperature}, humidity: ${humidity}`);
    }
  2. Selanjutnya kita membuat tag <script> dengan src="http://another.com/weather.json?callback=gotWeather", menggunakan nama fungsi sebagai parameter URL callback.

    let script = document.createElement('script');
    script.src = `http://another.com/weather.json?callback=gotWeather`;
    document.*body*.append(script);
  3. server remote another.com menghasilkan skrip dinamis yang memanggil gotWeather(...) dengan data yang ingin kita terima.

    // Jawaban dari *server* yang diharapkan seperti ini:
    gotWeather({
      temperature: 25,
      humidity: 78
    });
  4. When the remote script loads and executes, gotWeather runs, and, as it’s our function, we have the data.

  5. Jika skrip remote memuat dan dijalankan, gotWeather dijalankan di fungsi kita, kita memiliki data.

Itu berhasil, dan tidak melanggar keamanan, karena kedua sisi setuju untuk meluluskan data dengan cara ini. Dan jika kedua sisi setuju, ini bukanlah peretasan. Masih ada servis yang menyediakan akses seperti ini, karena cara ini bisa berfungsi bahkan untuk browser lama.

Kemudian, metode jaringan muncul di browser Javascript.

Mulanya, request cross-orogin itu dilarang. Tetapi dari hasil diskusi yang panjang, request cross-origin diizinkan, tetapi dengan kemampuan baru yang memerlukan izin tegas dari server, diekpresikan di header khusus.

Request yang aman

Ada dua tipe dari request cross-origin:

  1. Safe requests / Permintaan Aman.
  2. All the others.

Permintaan Aman lebih mudah dibuat, jadi mari kita mulai dengannya.

Permintaan aman jika memenuhi dua kondisi:

  1. Safe method: GET, POST or HEAD
  2. Safe headers – satu-satunya header khusus yang diizinkan adalah:
    • Accept,
    • Accept-Language,
    • Content-Language,
    • Content-Type dengan nilai application/x-www-form-urlencoded, multipart/form-data atau text/plain.

request lainnya akan dipertimbangkan “tidak aman”. Misalnya, request dengan metode PUT atau dengan API-key header HTTP tidak sesuai batasannya.

Perbedaan yang mendasar adalah “request yang aman” bisa dibuat menggunakan <form> atau <script>, tanpa menggunakan metode khusus apa saja.

Jadi, meski server sudah lama tetap bisa menerima request yang aman.

Bertentangan dengan itu, request dengan header tidak standar. contoh metode DELETE tidak bisa dibuat seperti ini. Jadi server lama mungkin berasumsi bahwa request ini berasal dari sumber istimewa, “karena situs web tidak bisa mengirimkan request”.

Saat kita mencoba untuk membuat request tidak aman, browser mengirimkan request khusus “preflight” yang menanyakan server – apakah bisa untuk menerima request cross-origin atau tidak?

Dan, kecuali server secara eksplisit mengonfirmasi hal itu dengan header, maka request tidak aman tidak akan dikirimkan.

Sekarang kita akan membahasnya lebih lanjut.

CORS untuk request yang aman

Jika request adalah request cross-origin, maka browser akan selalu menambahkan header Origin di dalam request.

Misalnya, jika kita request https://anywhere.com/*request* dari https://javascript.info/page, header akan seperti ini:

GET /*request*
Host: anywhere.com
Origin: https://javascript.info
...

Seperti yang kamu lihat, header Origin berisi persis seperti asal (domain/protocol/port), tanpa path.

Server bisa memeriksa Origin dan jika setuju untuk menerima request tersebut, tambahkan header khusus Access-Control-Allow-Origin di responnya. header tersebut harus berisi asal yang diizinkan (di kasus kita https://javascript.info), atau bintang * . Maka responnya sukses, atau mungkin saja error.

Browser memainkan peran sebagai penengah terpercaya disini:

  1. Browser memastikan Origin yang benar dikirimkan dengan request cross-origin.
  2. Browser memeriksa untuk perizinan di responnya, jika ada maka Javascript akan diizinkan untuk mengakses respon, jika tidak maka akan gagal dengan error.