1 poin oleh GN⁺ 2025-08-25 | 1 komentar | Bagikan ke WhatsApp
  • RFC 9839 secara jelas mendefinisikan karakter Unicode bermasalah yang dapat muncul di field teks saat pengembangan perangkat lunak
  • RFC ini membahas masalah akibat kurangnya konsistensi penanganan karakter tersebut di berbagai bahasa dan library
  • RFC 9839 mengusulkan tiga subset yang lebih sedikit menimbulkan masalah sehingga bisa digunakan secara opsional
  • Dibandingkan framework PRECIS yang sudah ada, penerapannya lebih mudah dan sederhana
  • Library bahasa Go untuk RFC 9839 juga dirilis untuk membantu penggunaan di dunia nyata

Latar belakang dan ringkasan RFC 9839

  • Unicode digunakan sebagai standar di hampir semua pemrosesan data teks
  • Namun, jika semua karakter Unicode diizinkan saat merancang struktur data atau protokol, masalah bisa muncul
  • Paul Hoffman dan penulis mengajukan draft individual ke IETF untuk memberikan kriteria yang jelas atas masalah Unicode yang terus berulang
  • Setelah diskusi selama dua tahun, dokumen ini diadopsi sebagai standar resmi dan diterbitkan sebagai RFC 9839
  • Dokumen ini menjelaskan secara rinci jenis karakter bermasalah, alasan mengapa karakter tersebut bermasalah (alasan teknis dan standar), serta tiga subset yang bisa dipilih pengguna

Pokok utama RFC 9839

  • Ini adalah dokumen yang wajib dijadikan rujukan saat merancang field teks di lingkungan perangkat lunak dan jaringan
  • RFC 9839 terdiri dari 10 halaman, relatif ringkas untuk dokumen standar IETF
  • Penjelasannya dibuat agar mudah dipahami terutama oleh pengembang perangkat lunak dan insinyur jaringan

Contoh karakter Unicode yang bermasalah

  • Sebagai contoh, field username dalam JSON dapat berisi string seperti berikut
    {  
        "username": "\u0000\u0089\uDEAD\uD9BF\uDFFF"  
    }  
    
  • Masalah pada tiap code point
    • U+0000 : karakter NULL yang tidak bermakna dan dapat mengganggu perilaku beberapa bahasa pemrograman
    • U+0089 : kode kontrol C1 (CHARACTER TABULATION WITH JUSTIFICATION), yang perilakunya kompleks dan tidak konsisten
    • U+DEAD : karakter surrogate yang tidak berpasangan, masalah yang berasal dari keterbatasan UTF-16. Ini menghasilkan data yang tidak ideal
    • \uD9BF\uDFFF (sebenarnya U+7FFFF) : noncharacter yang menurut standar dilarang untuk dipertukarkan
  • Code point seperti di atas dapat menyebabkan ketidakmampuan penanganan yang konsisten di dalam struktur data dan protokol serta memicu error tak terduga
  • RFC 9839 secara resmi mendefinisikan karakter bermasalah semacam ini dan menjelaskan dengan jelas jenis yang harus dikecualikan

Desain dan keterbatasan JSON

  • Ini bukan tanggung jawab pencipta JSON, Doug Crockford
  • JSON dirancang ketika Unicode belum cukup matang, sehingga tidak bisa membatasi himpunan karakter secara ketat
  • Karena standar ini kini tidak dapat diubah, diperlukan pendekatan empiris untuk mengecualikan karakter bermasalah

Perbedaan dengan framework PRECIS milik IETF

  • Bahkan sebelum RFC 9839 pada tahun 2025, IETF sudah menyediakan berbagai standar seperti RFC 8264 (PRECIS Framework)
    • Framework ini membahas secara rinci cara membersihkan, menerapkan, dan membandingkan string internasionalisasi
    • Dengan panjang 43 halaman, dokumen ini komprehensif baik dari sisi latar belakang maupun solusi
  • PRECIS sangat bergantung pada versi Unicode, kompleks, dan sulit diterapkan
  • RFC 9839 dibuat ringkas dan berfokus pada kepraktisan, sehingga mudah diadopsi dengan cepat saat mendefinisikan protokol baru

Subset RFC 9839 dan contoh penggunaannya

  • RFC 9839 mengajukan tiga subset praktis (scalars, XML, assignables)
  • Masing-masing subset sedikit berbeda dalam cakupan karakter bermasalah yang dikecualikan
  • Berikut ringkasan tabel tentang bagaimana format data utama dan subset RFC 9839 menangani karakter bermasalah
    • Beberapa format seperti CBOR, TOML, XML, YAML sebagian mengecualikan surrogate atau karakter kontrol
    • I-JSON mengecualikan surrogate dan noncharacter
    • JSON umum, Protobufs tidak mengecualikannya
    • XML, YAML karena karakteristik charset-nya hanya sebagian mengecualikan noncharacter/kode kontrol
      • Catatan: XML dan YAML tidak mengecualikan noncharacter di luar Basic Multilingual Pane

Library RFC 9839 untuk bahasa Go

  • Sebuah library Go kecil telah dirilis untuk mendukung validasi karakter terhadap tiga subset RFC 9839
  • Library ini sudah diuji dengan cukup baik, meski optimisasi masih berlangsung
  • Pengujian dan masukan dari penggunaan nyata sangat diharapkan

Makna RFC 9839 dan proses pengerjaannya

  • RFC 9839 diterbitkan secara resmi setelah melalui umpan balik bersama para co-author dan lebih dari 15 revisi draft
  • Melalui diskusi dan kontribusi dari banyak ahli komunitas, dokumen ini berkembang menjadi jauh lebih matang daripada draf awalnya
  • Kontributor dicantumkan di bagian “Acknowledgements”

Pengalaman pengajuan RFC secara individual

  • RFC 9839 diproses sebagai individual submission
  • Dibandingkan pendekatan tradisional melalui Working Group, beban usaha dan prosedurnya lebih besar
  • Jika dibandingkan dengan pengalaman berpartisipasi di Working Group, pendekatan tradisional lebih efisien dan lebih direkomendasikan

1 komentar

 
GN⁺ 2025-08-25
Komentar Hacker News
  • Saya rasa jelas ada karakter tertentu yang memang menimbulkan masalah, tetapi rasanya skenario terburuk justru ketika perancang struktur data atau protokol cenderung tidak mau mengizinkan semua jenis karakter secara arbitrer, bahkan yang sudah di-escape dengan benar. Misalnya, menurut saya validasi nama pengguna seharusnya ditangani di layer lain. Misalnya memeriksa nama pengguna harus di bawah 60 karakter, melarang emoji atau karakter zalgo, melarang null byte, dan mengembalikan error yang sesuai di API. Saya tidak ingin hal seperti ini gagal pada tahap parsing JSON alih-alih lewat validasi sebelumnya. Tentu saja ada kelas karakter tertentu yang jelas tidak pantas untuk nama pengguna. Namun jika saya mengirim file teks yang benar-benar memakai karakter tab dan semacamnya, saya berharap apa pun yang bisa ditangani tipe string utf8 di bahasa saya juga bisa di-encode. Terutama null byte punya banyak kegunaan dan memang cukup sering terlihat di JSON. Tetapi jika memang harus memakai himpunan Unicode "normal" yang terbatas, saya tetap merasa lebih baik ada standar daripada setiap orang membuat standar mini sendiri. Pada akhirnya, idenya sendiri terlihat bagus, tetapi argumen yang dipakai di tulisan blog itu kurang meyakinkan bagi saya

    • Per 2025, saya rasa hanya representasi string berikut yang secara praktis masih layak dibela untuk dipakai pada wire protocol level rendah

      • "Unicode Scalars" (UTF-16 yang well-formed, tipe string Python)
      • "UTF-16 yang berpotensi tidak valid" (WTF-8, tipe string JavaScript)
      • "UTF-8 yang berpotensi tidak valid" (array byte, tipe string Go)
      • Salah satu dari cara di atas dengan opsi tambahan "tanpa U+0000" (saat berinteraksi dengan bahasa/library yang dirancang sebelum kerentanan buffer overflow)
    • Terus terang, saya berharap file teks polos tidak memakai karakter C0 (kecuali newline, dan dengan enggan HT) maupun karakter C1. Saya paham orang ingin menyimpan hal seperti markup warna ANSI, tetapi dalam kasus seperti itu sebenarnya itu bukan teks polos, melainkan semacam format markup teks. Mirip Markdown, hanya saja memakai encoding dari rentang C0. Hanya karena datanya terlihat rapi saat ditampilkan dengan perintah seperti cat, bukan berarti itu teks polos. Saya paham ada banyak format markup yang di-encode sebagai teks polos demi interoperabilitas

    • Saya rasa pandangan bahwa mulai melarang kelompok karakter arbitrer dalam struktur data dan protokol adalah hal terburuk justru cukup jauh dari realitas. Yang benar-benar terburuk adalah ketika cacat perangkat lunak seperti parser menyebabkan pelanggaran keamanan

    • Saya malah bertanya-tanya apakah ada sistem yang mengizinkan UTF-8 di nama pengguna. Sudah sewajarnya semua identifier yang dimanipulasi atau dievaluasi secara terprogram (nama login, kata sandi, dan sebagainya) wajib ASCII. Bukan ISO-8859-1, hanya ASCII. Unicode tidak cocok untuk penggunaan seperti ini. Kalau hanya ditampilkan sebagai nama pengguna mungkin tidak masalah, tetapi sebagai identifier untuk login sistem, encoding non-ASCII harus dilarang tanpa pengecualian. Bahkan perangkat lunak keyboard pun tidak bisa menjamin konsistensi UTF-8 untuk representasi visual jika sudah keluar dari ASCII, dan itu makin membingungkan tergantung OS serta konfigurasi. Tidak ada jaminan bahwa binary yang tersisa di masa depan dan AI penafsir Unicode akan sepakat. Soal konsistensi juga, baik RFC 9839 maupun artikelnya tidak jelas apakah kasus IVS atau normalisasi (NFC/NFD/NFKC/NFKD) dibahas tegas sebagai bagian dari cakupan atau di luar cakupan. Rasanya bagian tujuan bahkan tidak ada. Yang ada hanya penyebutan samar seperti adanya "code point non-karakter"

    • Saya penasaran kenapa emoji harus dilarang dalam nama pengguna

  • Saya ingin menekankan bahwa IETF tidak menunggu sampai 2025 untuk menangani Bad Unicode. Sejak dulu RFC 8264: PRECIS Framework sudah membahas berbagai masalah Bad Unicode secara luas. RFC terkait seperti RFC 8265(tautan), 8266(tautan) dan lainnya juga layak dilihat. Secara umum, hal-hal seperti kata sandi yang bisa membalik arah teks atau ter-encode berbeda tergantung perangkat input memang tidak boleh dipakai pada username/password. Melalui profil RFC seperti ini, penanganannya bisa dibuat aman. Untuk tujuan seperti ini, "failing closed" (memblokir dengan lebih ketat) lebih aman. Walaupun emoji baru terus muncul, saya tetap lebih memilih dilarang dan bersikap konservatif daripada diizinkan pada nama pengguna lalu memengaruhi semua halaman

    • Tetapi jika terlalu tertutup, 20 tahun lagi emoji dari 20 tahun lalu pun bisa saja masih belum didukung, dan pada akhirnya yang bertambah hanya keluhan pengguna
  • Unicode memang punya bagian yang "baik", tetapi tetap mengecewakan bahwa kita harus tahu ada karakter tertentu yang harus dikecualikan secara khusus. Ini akibat upaya menerima cara penulisan bahasa secara terlalu menyeluruh hingga jadi sangat rumit. Melelahkan karena kita harus selalu memikirkan karakter mana yang perlu diperlakukan khusus. Karena itu saya memperlakukan string Unicode sebagai unit data yang berdiri sendiri. Saya terima, simpan, render, dan bandingkan untuk kesetaraan data, tetapi saya tidak berusaha menafsirkan isinya. Bahkan menggabungkan atau memanipulasi string pun terasa mengkhawatirkan

    • Unicode terasa seperti jurang trivia tanpa akhir dan keputusan buruk. Misalnya, RFC terkait memberi peringatan soal karakter kontrol ASCII kuno (karena risiko kebingungan tampilan), tetapi sama sekali tidak menyinggung karakter pengubah arah teks yang punya masalah keamanan fatal seperti Explicit Directional Overrides

    • Contoh sederhananya, kalau string pertama berakhir dengan modifier emoji yatim dan string kedua dimulai dengan emoji yang bisa dimodifikasi, masalah sudah muncul. Semakin banyak kasus yang lebih kompleks, semakin besar pula masalahnya

    • Kompleksitasnya memang besar, tetapi untuk hal-hal seperti surrogate dan kode kontrol, itu bukan demi pencatatan bahasa, melainkan hasil desain aneh dari masa lalu yang dipertahankan demi kompatibilitas

    • Unicode memang merepotkan, tetapi menurut saya tetap kurang merepotkan dibanding standar encoding lama lainnya

  • Saya rasa sebagian besar masalah bisa ditangani dengan menolak urutan byte UTF-8 yang tidak valid atau mengembalikan error secara menyeluruh. Misalnya surrogate dan sebagainya pada dasarnya ilegal dalam UTF-8, jadi bahasa yang memakai utf-8 seharusnya mengembalikan error untuk urutan seperti itu. Menurut saya yang benar-benar bermasalah adalah "code point" yang problematis (non-printing, dll.). Ini lebih berguna jika diperlakukan sebagai konsep yang jelas terpisah dari urutan byte ilegal

    • Menurut saya itu cukup masuk akal. Pilihan seperti ini adalah urusan implementor aplikasi, bukan sesuatu yang harus diputuskan library umum. Saya belum pernah melihat parser JSON yang hanya menangani nama pengguna
  • Unicode sendiri sudah mendefinisikan kategori setiap code point (General Category) untuk mengklasifikasikan jenis karakter yang aneh. Bisa lihat artikel Wikipedia terkait. Misalnya, di Python unicodedata.category(chr(0)) mengembalikan "Cc" (control), dan unicodedata.category(chr(0xdead)) mengembalikan "Cs" (surrogate)

  • Menurut saya berlebihan jika semua "legacy control" dilarang bukan hanya sebagai literal, tetapi juga dalam string escape (mis. "\u0027"). C1 memang jarang dipakai jadi tidak masalah, tetapi sebagian karakter C0 punya contoh penggunaan nyata. escape, EOF, NUL, dan sebagainya menurut saya masih punya kegunaan yang jelas

    • Karakter C0 yang agak aneh (seperti U+001E Record Separator) menurut saya sangat berguna dalam stream data. Untuk dokumen mungkin bisa diblokir, tetapi untuk data stream itu berguna

    • Saya pernah melihat karakter form feed (U+000C) dipakai di source code program. Emacs sejak lama mendukung navigasi per halaman, jadi hal seperti ini kadang memang muncul

  • Saya tidak menganggap Unicode itu bagus. Apa pun himpunan karakternya, jenis karakter yang benar-benar boleh dipakai (karakter kontrol, karakter grafis, panjang maksimum, dll.) pada akhirnya tetap harus ditentukan sesuai tiap aplikasi. Mencoba memasukkan atau mengecualikannya di JSON dan sejenisnya tidak banyak membantu. Entah itu Unicode, ASCII, atau charset lain, memberi nama pada subset tertentu (atau superset) kadang memang berguna, tetapi jangan mengira itu pilihan yang baik untuk semua orang. RFC 9839 memang memberi nama pada beberapa subset Unicode, tetapi itu tidak menjamin otomatis benar untuk layanan yang saya bangun. Kesimpulan saya, sebaiknya juga mempertimbangkan untuk tidak memakai atau tidak memaksakan Unicode sama sekali

    • Masalah sebenarnya adalah karakter gabungan (combining character). Karena ini, Unicode berubah dari kumpulan karakter menjadi DSL untuk mendeskripsikan karakter
  • Saya sedang mempertimbangkan apakah harus mengontrol input, atau membungkusnya dalam tipe data yang aman untuk output terhadap input tak tepercaya (untuk web+log+debug)

  • Saya berharap ada batas standar untuk jumlah nilai skalar Unicode yang bisa masuk dalam satu unit grafis. Terakhir kali saya cek (meski sudah beberapa tahun lalu), standar tidak punya batas seperti itu, dan hanya memberi rekomendasi agar aplikasi streaming membatasi unit grafis hingga 128 byte. Jika batas seperti ini ditegaskan di standar, implementasi akan jauh lebih mudah dan pembatasan yang tidak perlu bisa dihindari

  • Saya pernah benar-benar menemui kasus program rusak karena berasumsi "tidak ada karakter kontrol" (padahal form feed umum dipakai untuk pemisah halaman, karakter escape untuk terminal, dan sebagainya). Asumsi "semuanya UTF-8" juga kadang gagal (file data lama, log, dll.). Jika tidak melakukan pemrosesan bermakna terhadap teks, yang terbaik adalah cukup meneruskannya sebagai urutan byte tanpa mengubah isi. Tetapi karena Microsoft Windows, kadang kita terpaksa meneruskan urutan char16_t. UTF-16 pada dasarnya berbeda dari UTF-8 dalam I/O. Saat mengonversi, untuk data eksternal → bentuk internal masing-masing harus memakai pendekatan WTF-8 (UTF-16) dan surrogate escape (UTF-8). Kedua pendekatan itu tidak bisa dicampur