OAuth Security Pitfalls — Security

OAuth 2.0 dan Keamanannya OAuth 2.0 adalah protokol authorization yang memungkinkan aplikasi pihak ketiga mengakses resource pengguna tanpa mengetahui…

OAuth 2.0 adalah cara sebuah aplikasi mendapat izin memakai akunmu di layanan lain, tanpa pernah tahu password-mu. Tombol "Login dengan Google" itu OAuth. Bayangin hotel: kamu menunjukkan KTP ke resepsionis, bukan ke tamu lain, lalu resepsionis memberi kartu kamar yang cuma membuka pintu tertentu dan cuma berlaku beberapa hari. Aplikasi itu tamunya, layanan login itu resepsionisnya, dan kartu kamarnya disebut access token.

🧪 Tebakan cepat: penyerang berhasil mencegat authorization code dari URL callback. Aplikasinya memakai PKCE. Bisakah penyerang menukar code itu jadi access token? Jawabannya ada di bagian "PKCE: bukti bahwa kamu yang memulai".

Pemain dan istilahnya

Alur authorization code, langkah demi langkah

Ini alur yang dijalankan Lab, dengan nilai contoh dari spesifikasi OAuth (RFC 6749 dan RFC 7636):

Langkah Siapa Yang terjadi
1 Kelasku membuat state acak dan code_verifier acak, lalu menghitung code_challenge
2 Browser dibawa ke login.example/authorize dengan state dan code_challenge
3 login.example mencocokkan redirect_uri, Rina login dan setuju, code dibuat dan dicatat bersama challenge-nya
4 Browser kembali ke kelasku.example/callback?code=...&state=..., Kelasku mencocokkan state
5 Server Kelasku menukar code + code_verifier di /token
6 login.example mengecek verifier, lalu memberi access token

Request langkah 2 dan 5 persis seperti di Lab:

GET https://login.example/authorize?response_type=code&client_id=kelasku-web
    &redirect_uri=https%3A%2F%2Fkelasku.example%2Fcallback&scope=profile
    &state=af0ifjsldkj&code_challenge=E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM
    &code_challenge_method=S256

POST https://login.example/token
grant_type=authorization_code&code=SplxlOBeZQQYbYS6WxSbIA
&redirect_uri=https%3A%2F%2Fkelasku.example%2Fcallback&client_id=kelasku-web
&code_verifier=dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk

state: penangkal CSRF

state adalah teks acak yang dibuat Kelasku di langkah 1 dan disimpan di session Rina. Waktu callback datang, Kelasku mencocokkan state di URL dengan yang di session. Gak cocok, atau gak ada, berarti alur ini bukan dimulai Rina: misalnya situs lain membuat browser Rina membuka callback dengan code milik penyerang, supaya akun Kelasku Rina tersambung ke akun penyerang. Itu serangan CSRF (Cross-Site Request Forgery), dan parameter yang mencegahnya adalah state. Kelasku harus menolak dan membatalkan login.

PKCE: bukti bahwa kamu yang memulai

PKCE (Proof Key for Code Exchange, dibaca "pixy") menambah satu bukti. Sebelum alur mulai, client membuat code_verifier: 32 byte acak dalam base64url (43 karakter). Yang dikirim di URL cuma sidik jarinya, code_challenge = base64url(SHA-256(verifier)). SHA-256 gak bisa dibalik, jadi melihat challenge gak membuka verifier-nya.

function base64url(bytes) {
  return btoa(String.fromCharCode(...bytes)).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
}

async function makePkce() {
  const verifier = base64url(crypto.getRandomValues(new Uint8Array(32))); // 43 karakter acak
  const digest = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(verifier));
  return { verifier, challenge: base64url(new Uint8Array(digest)) };
}

Di /token, server menghitung SHA-256 dari verifier yang dikirim dan membandingkannya dengan challenge yang dicatat. Penyerang yang cuma memegang code gak punya verifier-nya, jadi jawabannya 400 {"error":"invalid_grant"} dan gak ada token. Itu jawaban tebakannya: gak bisa. Sekarang PKCE dianjurkan untuk semua client, bukan cuma aplikasi mobile.

redirect_uri harus persis sama

Kalau authorization server gak memvalidasi redirect_uri, penyerang bisa membuat link login yang redirect_uri-nya ke domain dia sendiri. Rina login seperti biasa, lalu code-nya dikirim ke penyerang. Masalah ini disebut open redirect. Penangkalnya: server mencocokkan redirect_uri persis sama dengan alamat yang didaftarkan client, karakter demi karakter. Gak cocok, server menampilkan error dan gak me-redirect ke mana pun. Di Lab, https://penyerang.example/callback ditolak di langkah 3.

⚠️ Jebakan yang sering ketemu

TL;DR: OAuth 2.0 memberi aplikasi access token tanpa tahu password user. Alur yang dipakai: authorization code + PKCE. state acak dicek di callback untuk mencegah CSRF. PKCE mengirim code_challenge = base64url(SHA-256(code_verifier)) di awal dan code_verifier saat menukar code, jadi code curian ditolak dengan invalid_grant. redirect_uri harus cocok persis dengan yang terdaftar, kalau gak, code bisa dikirim ke penyerang (open redirect). Hindari Implicit Flow, minta scope seperlunya, dan simpan token di tempat yang gak terjangkau JavaScript.

Yang akan kamu pelajari