D
P
0
← Semua artikel Read in English

Cache & CDN: Deploy Sukses, Perubahan Tak Muncul

Semua Email Hasil Export Jadi `[email protected]` dan Satu Baris di Halaman Berhenti di Tanda `<`? Itu Email Obfuscation di Edge

· · 5 menit baca
Semua Email Hasil Export Jadi `[email protected]` dan Satu Baris di Halaman Berhenti di Tanda `<`? Itu Email Obfuscation di Edge

Satu kolom di spreadsheet hasil export berisi nilai yang sama persis di setiap baris. Bukan mirip, tapi sama persis, dari baris pertama sampai baris terakhir:

[email protected]
[email protected]
[email protected]
...

Itu daftar kontak. Setiap barisnya datang dari orang yang berbeda-beda, jadi tidak ada satu pun cara yang masuk akal untuk semuanya berakhir identik. Export-nya sendiri saya buat dengan cara yang biasa saya pakai kalau sebuah layar admin WordPress tidak menyediakan tombol export: buka daftarnya, jalankan potongan JavaScript di console, baca textContent tiap sel, susun jadi baris CSV.

Tebakan pertama saya, selectornya meleset dan saya mengambil kolom yang salah. Tebakan kedua, ada plugin privasi yang menyensor alamat di layar daftar. Dua-duanya keliru, dan keduanya baru gugur setelah saya berhenti membaca hasil script dan mulai membaca HTML yang benar-benar ada di dalam sel itu.

Alamatnya memang tidak ada di HTML

Isi selnya seperti ini:

<td class="column-email">
  <a class="__cf_email__" data-cfemail="a1c4ccc0c8...">[email protected]</a>
</td>

Teks yang terlihat itu bukan alamat yang disamarkan sebagian. Itu placeholder, dan sifatnya konstan untuk semua orang. Alamat aslinya pindah ke atribut data-cfemail dalam bentuk heksadesimal. Jadi textContent saya tidak salah baca apa pun. Dia membaca dengan benar sekali sebuah string yang memang tidak mengandung informasi.

Pelakunya fitur Email Obfuscation di CDN. Fitur itu memindai HTML yang lewat, menemukan apa pun yang berbentuk alamat email, lalu menggantinya dengan anchor tadi. Di browser biasa, sebuah script kecil dari CDN itu mendekode data-cfemail dan mengembalikan teks aslinya, jadi pengunjung tidak pernah tahu ada yang terjadi. Script saya di console berjalan lebih dulu, atau setidaknya membaca DOM di titik yang salah, dan yang dia dapat adalah bentuk mentahnya.

Yang penting untuk dipahami: penulisan ulang itu terjadi di edge, bukan di origin. PHP di server mengeluarkan alamat yang benar. Yang sampai ke browser sudah bukan itu lagi.

Mendekodenya

Skema penyandiannya sederhana dan tidak dirahasiakan. Pasangan heksa pertama adalah kunci XOR, dan setiap pasangan sesudahnya adalah satu byte alamat yang sudah di-XOR dengan kunci itu.

function decodeCfEmail(hex) {
  const key = parseInt(hex.substr(0, 2), 16);
  let out = '';
  for (let i = 2; i < hex.length; i += 2) {
    out += String.fromCharCode(parseInt(hex.substr(i, 2), 16) ^ key);
  }
  return out;
}

Godaannya adalah memakai fungsi itu hanya di kolom email, karena di situlah masalahnya kelihatan. Saya sempat melakukan itu dan hasilnya masih bolong. Alamat juga muncul di tempat yang tidak saya duga, misalnya terkubur di dalam badan pesan yang ditulis pengirimnya sendiri, dan semua alamat itu ikut ditelan oleh penulisan ulang yang sama.

Jadi penggantiannya saya lakukan di level sel, bukan di level kolom. Salin selnya, ganti setiap node __cf_email__ di dalamnya dengan teks hasil dekode, baru ambil teksnya:

function cellText(td) {
  const clone = td.cloneNode(true);
  clone.querySelectorAll('.__cf_email__').forEach((node) => {
    const decoded = decodeCfEmail(node.dataset.cfemail || '');
    node.replaceWith(document.createTextNode(decoded));
  });
  return clone.textContent.trim();
}

Setelah itu kolom emailnya berisi alamat yang benar-benar berbeda tiap baris, dan alamat yang menumpang di dalam badan pesan ikut terselamatkan.

Beberapa minggu kemudian, gejala yang kelihatannya tidak berhubungan

Di projek lain, saya menambahkan satu baris kecil di sebuah form: nama pemohon diikuti alamat email yang dipakainya, supaya orang itu tahu permintaannya terikat ke akun yang mana. Baris itu tayang, lalu berhenti di tengah jalan:

MENGAJUKAN SEBAGAI Nama Pengguna <

Persis di tanda <, lalu tidak ada lagi. Saya cek keluaran PHP-nya dan alamat itu ada di sana, lengkap. Saya cek template-nya dan tidak ada yang salah. Di posisi tempat alamat itu seharusnya berada, HTML yang sampai ke browser berisi markup __cf_email__ yang sama seperti yang dulu merusak export saya.

Template-nya benar dan halamannya salah. Itu kalimat yang perlu waktu untuk saya terima, karena selama ini keduanya selalu satu hal yang sama.

Perbaikannya satu baris, tanpa JavaScript

Untuk alamat yang memang sengaja ingin ditampilkan, CDN itu menyediakan penanda opt-out sendiri. Bungkus bagian yang tidak boleh disentuh, dan penulisan ulang melewatinya:

<!--email_off-->Nama Pengguna &lt;alamat@contoh.com&gt;<!--/email_off-->

Tidak perlu mematikan fitur obfuscation untuk seluruh situs, dan tidak perlu mendekode apa pun di sisi klien. Yang berubah cuma satu hal: bagian ini dinyatakan bukan alamat yang perlu disembunyikan.

Untuk export tadi, opt-out bukan jawabannya, karena saya tidak ingin alamat pelanggan telanjang di HTML admin hanya demi memudahkan script saya. Di sana dekodernya yang tetap dipakai.

Kenapa saya tidak menemukannya lebih cepat

Kebiasaan saya kalau ada yang aneh di situs berpelindung CDN adalah memverifikasi di origin. Lewati edge, panggil server langsung, lihat apa yang sebenarnya dikeluarkan aplikasi. Itu cara paling cepat memastikan bahwa yang saya kejar bug kode dan bukan cache basi.

Kebiasaan itu benar untuk masalah cache dan justru menyesatkan untuk masalah penulisan ulang. Origin selalu menjawab dengan alamat yang benar, karena origin memang tidak pernah punya masalah. Setiap kali saya memeriksa di sana, saya sedang mengumpulkan bukti bahwa tidak ada yang rusak, padahal yang rusak ada di lapisan yang sengaja saya keluarkan dari pemeriksaan. Alat ukur saya bekerja sempurna dan mengukur benda yang salah.

Begitu sebuah CDN menyunting isi HTML, edge berubah dari sekadar pengantar menjadi satu-satunya tempat yang jujur. Origin cuma menceritakan niat aplikasi. Yang dibaca orang dan dibaca script adalah byte yang keluar dari edge.

Yang saya bawa pulang

Dua gejala itu ternyata bug yang sama dilihat dari dua sisi. Yang pertama saya temukan lewat script yang membaca HTML, yang kedua lewat mata seseorang yang membaca halaman. Kalau saya sudah paham penyebabnya sejak kasus pertama, kasus kedua tidak akan pernah tayang.

Pembedanya sekarang saya rumuskan begini: kalau yang dihasilkan sebuah fitur adalah tata letak, memeriksa template biasanya cukup, karena tata letak dihitung ulang di browser dari aturan yang sudah saya tulis. Kalau yang dihasilkan adalah konten, periksa byte yang benar-benar sampai. Konten bisa disunting oleh siapa pun yang berdiri di antara aplikasi dan pembaca, dan CDN berdiri tepat di sana.