RFC 9839 dan Unicode yang Bermasalah
(tbray.org)- 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
usernamedalam 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 pemrogramanU+0089: kode kontrol C1 (CHARACTER TABULATION WITH JUSTIFICATION), yang perilakunya kompleks dan tidak konsistenU+DEAD: karakter surrogate yang tidak berpasangan, masalah yang berasal dari keterbatasan UTF-16. Ini menghasilkan data yang tidak ideal\uD9BF\uDFFF(sebenarnyaU+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
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
stringutf8 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 sayaPer 2025, saya rasa hanya representasi string berikut yang secara praktis masih layak dibela untuk dipakai pada wire protocol level rendah
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 interoperabilitasSaya 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
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
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), danunicodedata.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 jelasKarakter 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
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