Pengkodean, pelolosan, dan hashing
Base64 bukan enkripsi, btoa bukan UTF-8, encodeURI bukan encodeURIComponent, dan SHA-256 bukan hash kata sandi. Inilah yang sebenarnya dilakukan masing-masing hal tersebut, dan kesalahan spesifik yang diakibatkan oleh asumsi sebaliknya.
Pengkodean bukanlah enkripsi, dan bukan kompresi
Pengkodean mengubah cara data ditulis. Enkripsi mengubah siapa yang dapat membacanya. Kompresi mengubah berapa banyak ruang yang dibutuhkan. Ini adalah tiga pekerjaan yang berbeda, dan base64 hanya melakukan pekerjaan pertama — buruknya, jika Anda mengharapkan salah satu dari dua pekerjaan lainnya.
Base64 mengambil tiga byte sekaligus dan menulis ulangnya sebagai empat karakter yang diambil dari alfabet simbol 64. Empat karakter yang membawa tiga byte berarti output selalu 33% lebih besar dari input, ditambah padding. Ini ada karena banyak infrastruktur — header email, header HTTP, nilai string JSON, URL, atribut XML — dirancang untuk teks dan merusak atau menolak byte sewenang-wenang. Base64 adalah adaptor yang memungkinkan Anda memasukkan byte melalui pipa berbentuk teks.
Siapapun bisa membalikkannya secara instan, tanpa kunci, karena tidak ada kuncinya. Jika Anda menggunakan kata sandi base64, Anda telah menerbitkan kata sandi dalam format yang agak merepotkan. Hal ini penting karena keluaran base64 terlihat acak-acakan di mata manusia, yang merupakan properti yang membuat orang memercayainya untuk hal-hal yang tidak dapat dilakukannya.
Mengapa btoa() rusak, dan dua cara berbeda untuk rusak
Browser memberi Anda btoa() dan atob(), dan keduanya lebih tua dari API teks modern. btoa didefinisikan melalui "string biner": string yang setiap unit kodenya adalah satu byte, dari 0 hingga 255. Teks bukan itu.
Kegagalan pertama sangat keras. Panggil btoa("世界") dan Anda mendapatkan InvalidCharacterError, karena U+4E16 tidak muat dalam satu byte. Kegagalan besar adalah hal yang baik - Anda segera menyadarinya dan mencari perbaikan.
Kegagalan kedua tidak bersuara, dan itulah yang mencapai produksi. Karakter é adalah U+00E9, yang muat dalam satu byte. Jadi btoa("café") kembali dengan gembira, mengkodekan é sebagai byte tunggal 0xE9. Tapi é di UTF-8 adalah dua byte, 0xC3 0xA9. Base64 yang baru saja Anda hasilkan diterjemahkan, di setiap sistem lain di dunia, ke sesuatu yang bukan teks Anda. Anda akan mengetahui beberapa minggu kemudian ketika nama dalam database telah berubah menjadi karakter pengganti.
Cara mengatasinya adalah berhenti memperlakukan teks sebagai byte dan mengubahnya secara eksplisit. TextEncoder menghasilkan UTF-8 byte; menyandikan itu. TextDecoder mengubah byte kembali menjadi teks, dan membangunnya dengan { fatal: true } membuatnya menampilkan urutan yang tidak valid alih-alih secara diam-diam mengganti U+FFFD, sehingga dekode yang tidak mungkin benar akan gagal daripada mengembalikan omong kosong yang tampak masuk akal. Itulah alur yang digunakan oleh perangkat ini, itulah sebabnya emoji, yang menggabungkan tanda dan skrip dari kanan ke kiri, semuanya berjalan bolak-balik dengan tepat.
- Ubah teks menjadi byte dengan TextEncoder — jangan pernah mengindeks ke dalam string.
- Enkode byte ke base64.
- Untuk membalikkan: dekode base64 menjadi byte, lalu dekode byte sebagai UTF-8 dengan fatal: true.
- Jika langkah UTF-8 gagal, payloadnya adalah biner, bukan teks. Tunjukkan itu sebagai kutukan daripada berpura-pura.
base64 versus base64url, dan pertanyaan padding
Base64 standar menggunakan + dan / sebagai dua simbol terakhirnya. Keduanya bermakna dalam URL: + dapat dibaca sebagai spasi yang disandikan dalam string kueri, dan / merupakan pemisah jalur. Jadi RFC 4648 mendefinisikan alfabet kedua, base64url, yang menggantikan - dan _ sebagai gantinya. JWT menggunakannya, seperti halnya sebagian besar format token dan banyak API.
Padding adalah variabel lainnya. Bantalan base64 standar dengan = sehingga panjang keluaran selalu kelipatan empat. base64url biasanya menghilangkan padding, karena = itu sendiri merupakan karakter yang aneh dalam URL dan panjangnya dapat dipulihkan secara aritmatika. Dekoder yang menerapkan padding akan menolak segmen JWT yang benar-benar valid.
Saran praktis: decoder Anda harus menerima kedua alfabet dan mentoleransi padding yang hilang, karena Anda jarang mengontrol apa yang diberikan. Pembuat enkode Anda harus menjelaskan secara eksplisit apa yang dipancarkannya, karena penerima mungkin peduli. Utilitas base64 di sini melakukan hal tersebut — ia menerima apa pun yang masuk akal dan memungkinkan Anda memilih dengan tepat apa yang dihasilkannya.
encodeURI dan encodeURIComponent: perbedaannya dalam satu kalimat
Keduanya dikodekan persen menggunakan UTF-8. Mereka hanya berbeda dalam karakter mana yang mereka tinggalkan, dan perbedaan itu adalah keseluruhan cerita: encodeURIComponent lolos dari pembatas yang dicadangkan, encodeURI tidak.
Pembatas yang dicadangkan adalah karakter yang memberikan struktur URL: : / ? # [ ] @ ! $&' ( )*+ , ; =. encodeURI mengasumsikan Anda menyerahkan URL yang sudah terstruktur dengan benar dan harus tetap seperti itu, sehingga mempertahankannya — ini tidak akan mengubah https:// menjadi https%3A%2F%2F. encodeURIComponent mengasumsikan Anda menyerahkan satu bidak yang akan dijatuhkan ke dalam slot, sehingga ia lolos, memastikan bidak tersebut tidak dapat keluar dari slotnya.
Bug yang dihasilkan ini sepenuhnya bersifat mekanis. Ambil nilai pencarian a&b=c. Encode dengan encodeURI dan tambahkan sebagai ?q=a&b=c, dan Anda telah membuat dua parameter secara diam-diam: q sekarang hanya "a", dan b=c liar telah muncul. Encode dengan encodeURIComponent dan Anda mendapatkan ?q=a%26b%3Dc, satu parameter, nilai yang benar. Kelas bug yang sama memungkinkan nilai yang dibuat memasukkan parameter ke dalam URL kode Anda dibuat — itulah sebabnya "gunakan formulir komponen untuk nilai" adalah aturan keamanan, bukan hanya aturan kebenaran.
Pengkodean formulir adalah aturan ketiga yang terlihat seperti aturan kedua. application/x-www-form-urlencoded menulis spasi sebagai +, bukan %20. Jika Anda mendekode badan formulir dengan decodeURIComponent biasa, setiap tanda tambah di data menjadi spasi. Setiap kotak pencarian yang pernah mengubah "C++" menjadi "C" adalah bug ini.
HTML entitas, dan mengapa mendekodekannya dengan innerHTML adalah kebiasaan buruk
Melarikan diri untuk HTML sempit dan dipahami dengan baik: & menjadi &, < menjadi <, > menjadi >, dan di dalam nilai atribut " dan ' perlu di-escape juga. Lima karakter. Melarikan diri lebih dari itu — mengubah setiap huruf beraksen menjadi entitas bernama — adalah solusi untuk masa-masa pengkodean karakter yang tidak pasti, dan sekarang menjadi gaya opsional, bukan keamanan.
Decoding adalah tempat tinggalnya kebiasaan buruk. Trik satu baris yang muncul di setiap jawaban adalah dengan menetapkan string ke innerHTML elemen terpisah dan membaca kembali konten teksnya. Ini berhasil, dan itu adalah ide yang buruk. Anda telah memberikan masukan yang tidak tepercaya ke parser HTML, yang membangun DOM node nyata darinya. Sebuah <img src=x onerror=...> dalam string itu menjadi elemen gambar aktual dengan penangan kesalahan aktual terpasang; jika subpohon itu dimasukkan ke dalam dokumen, subpohon itu akan berjalan. Ini juga secara diam-diam menghancurkan data Anda: tag dalam masukan akan hilang alih-alih bolak-balik, karena parser menafsirkannya sebagai markup, bukan teks.
Penguraian kode entitas dengan benar tidak memerlukan parser sama sekali: cocokkan referensinya, cari namanya di tabel, atau lakukan aritmatika untuk referensi numerik. Itu adalah beberapa lusin baris, ia tidak dapat mengeksekusi apa pun, dan ia berjalan bolak-balik dengan setia. Toolkit ini melakukannya dengan cara itu, itulah sebabnya menempelkan tag skrip ke dekoder entitas akan menampilkan tag skrip kepada Anda.
Memilih hash, dan tiga pertanyaan yang memutuskannya
Hash kriptografi mengubah masukan apa pun menjadi intisari dengan panjang tetap, sehingga menemukan dua masukan dengan intisari yang sama menjadi tidak mungkin dilakukan. Properti itulah yang memungkinkan intisari menggantikan data — dalam tanda tangan, pemeriksaan integritas, atau alamat konten.
Pertanyaan pertama: apakah Anda melindungi dari kecelakaan atau musuh? Sebuah checksum yang melindungi unduhan yang rusak hanya perlu menangkap pembalikan acak; CRC32 baik-baik saja. Intisari yang dapat dimanfaatkan oleh penyerang dari tabrakan memerlukan hash yang masih ada. Perbedaan itulah yang menyebabkan SHA-1 tidak sekadar "tua".
SHA-1 rusak, secara konkret. Dalam 2017 pekerjaan SHAttered menghasilkan dua file PDF berbeda dengan intisari SHA-1 yang sama. Dalam 2020, "SHA-1 is a Shambles" mendemonstrasikan tabrakan awalan yang dipilih — varian yang lebih kuat dan jauh lebih berbahaya, karena memungkinkan penyerang membenturkan dua dokumen yang sangat berbeda, bukan dua blob yang dibuat dengan cermat. Jika keamanan sistem bertumpu pada SHA-1 ketahanan terhadap tabrakan, keamanan tersebut akan hilang. SHA-1 tetap ada dalam perangkat ini karena id objek git dan tanda tangan API lama masih menggunakannya, dan Anda harus dapat mereproduksi nilai-nilai tersebut. Mereproduksi suatu nilai tidak sama dengan mengandalkannya.
Pertanyaan kedua: apakah inputnya berupa kata sandi? Jika iya, maka tidak ada satu pun dari jawaban di atas. SHA-256 dirancang agar cepat, dan cepat justru salah untuk kata sandi: artinya penyerang dengan database Anda dapat mencoba miliaran tebakan per detik. Kata sandi memerlukan fungsi yang sengaja lambat dan membebani memori dengan garam per pengguna — Argon2id, scrypt, atau bcrypt. Ini bukanlah sebuah nuansa; menggunakan SHA-256 untuk kata sandi adalah satu-satunya kesalahan hashing serius yang paling umum.
Pertanyaan ketiga: apakah Anda memerlukan intisari yang dikunci? Jika Anda mengautentikasi pesan alih-alih mengambil sidik jarinya, Anda menginginkan HMAC, bukan hash kosong. Menggabungkan rahasia dan melakukan hashing adalah tujuan bunuh diri klasik melawan serangan ekstensi panjang; HMAC ada karena konstruksi tersebut lebih sulit diperbaiki daripada yang terlihat.
Untuk yang lainnya — mengambil sidik jari pada file, alamat konten, atribut integritas — SHA-256 adalah default yang masuk akal, dan SHA-512 sering kali lebih cepat pada perangkat keras 64-bit sekaligus memberikan intisari yang lebih luas.
Mengapa hash di sini berasal dari browser
Intisari dalam toolkit ini dihitung oleh SubtleCrypto, implementasi Web Crypto milik browser, bukan oleh JavaScript yang dikirimkan dari situs ini. Itu adalah pilihan yang disengaja: penerapan browser diaudit, dipelihara, dan biasanya dijalankan sebagai kode asli yang dioptimalkan. SHA-256 yang ditulis tangan dalam satu bundel halaman lebih merupakan kode yang dapat dipercaya tanpa manfaat apa pun.
Hal ini memiliki satu konsekuensi yang terlihat. Web Crypto hanya diekspos dalam konteks aman, artinya https:// atau localhost. Buka halaman ini melalui HTTP biasa pada alamat LAN dan crypto.subtle tidak akan terdefinisi, sehingga utilitas hash akan memberi tahu Anda dengan jelas daripada gagal secara diam-diam atau mengganti sesuatu yang lebih lemah.
Alasan yang sama menggerakkan generator UUID. crypto.randomUUID() juga hanya konteks aman, jadi jika tidak tersedia, toolkit akan kembali ke crypto.getRandomValues() — yang masih merupakan sumber kriptografi yang aman — dan menyetel versi dan bit variannya sendiri. Apa yang tidak akan pernah dilakukannya adalah kembali ke Math.random(). Itu adalah PRNG non-kriptografi cepat yang keadaan internalnya dapat dipulihkan dari keluaran jangka pendeknya, dan pengidentifikasi memiliki kebiasaan yang tidak menguntungkan untuk dipromosikan menjadi kunci sesi dan tautan pengaturan ulang kata sandi. Jika tidak ada sumber aman, alat ini tidak menghasilkan apa pun dan menjelaskan alasannya.
Apa yang terjadi pada apa yang Anda tempel
- Setiap konversi, hash, decode dan diff berjalan di tab browser Anda. Tidak ada masukan yang diunggah, dicatat atau disimpan di server, karena tidak ada server yang terlibat setelah halaman dimuat.
- Hash berasal dari implementasi Web Crypto milik browser, dan UUID dari generator acak yang aman secara kriptografis. Tidak ada yang melibatkan panggilan jaringan.
- Tidak ada yang Anda ketikkan yang ditulis ke penyimpanan lokal atau cookie. Memuat ulang halaman akan membuangnya; menutup tab akan membuangnya.
- Analisis seluruh situs hanya berjalan pada host produksi kanonik yang dikonfigurasi dan diungkapkan dalam Kebijakan Privasi; host lokal dan pratinjau menolaknya. Nilai, token, URL, dan konten file yang ditempelkan dikecualikan dari peristiwa analisis ToolAcre sendiri. Periklanan dinonaktifkan dalam konfigurasi saat ini.
- Artinya: kunci JWT atau API adalah kredensial aktif. Kebiasaan yang aman adalah jangan pernah menempelkannya ke halaman web yang tidak Anda tulis, betapapun dapat dipercayanya klaim tersebut — termasuk yang ini.
Pertanyaan
Apakah base64 merupakan cara untuk menyembunyikan data?
Tidak. Ini adalah representasi teks yang dapat dibalik tanpa kunci, dapat didekodekan oleh siapa pun dalam sepersekian detik. Hal ini membuat data bertahan dalam saluran yang hanya berupa teks; itu tidak menjadikannya rahasia. Apa pun yang benar-benar sensitif memerlukan enkripsi, dan hasil yang dienkripsi sering kali dikodekan dengan base64 untuk transportasi — yang merupakan sumber kebingungan.
Mengapa base64 saya lebih panjang dari input?
Karena empat karakter keluaran membawa tiga byte masukan, maka keluarannya kira-kira berukuran 4/3, ditambah hingga dua karakter pengisi. Itu melekat pada formatnya. Jika ukuran penting, kompres sebelum pengkodean — jangan pernah setelahnya, karena output base64 dikompres dengan buruk.
Fungsi pengkodean URL mana yang harus saya gunakan?
Gunakan encodeURIComponent untuk setiap bagian yang Anda masukkan ke dalam URL: nilai kueri, segmen jalur, fragmen. Gunakan encodeURI hanya jika Anda memiliki URL keseluruhan yang sudah terstruktur dan hanya berisi spasi atau non-ASCII. Jika Anda membuat string kueri, pilih URLSearchParams, yang menerapkan aturan yang benar untuk Anda dan menangani perbedaan spasi sebagai plus.
Mengapa dekoder saya menampilkan "URI format salah"?
Karena % pada input tidak diikuti oleh dua digit heksadesimal. Biasanya teks berisi tanda persen literal — "diskon 50%" — yang tidak pernah dikodekan. Persen literal harus ditulis sebagai %25. Utilitas URL di sini melaporkan posisi sebenarnya dari pelarian yang melanggar, bukan hanya menolak.
Bisakah saya menggunakan SHA-256 untuk menyimpan kata sandi?
Tidak. SHA-256 dirancang dengan cepat, yang berarti penyerang yang mencuri database Anda dapat menguji miliaran kandidat kata sandi per detik pada perangkat keras komoditas. Kata sandi memerlukan fungsi yang lambat, sulit diingat, dan asin: Argon2id, scrypt, atau bcrypt. Ini adalah kesalahan serius paling umum di bidang ini.
Mengapa SHA-1 masih ada jika rusak?
Karena Anda masih perlu mereproduksi nilai SHA-1 yang sudah ada: id objek git, sidik jari sertifikat TLS lama, tanda tangan permintaan API lama. Mampu menghitung nilai interoperabilitas berbeda dengan mengandalkannya untuk keamanan. Setiap tempat SHA-1 yang muncul dalam perangkat ini diberi label yang sesuai.
Mengapa dua alat memberikan hash berbeda untuk teks yang sama?
Hampir selalu perbedaannya terletak pada byte, bukan algoritmanya. Penyebab yang umum adalah baris baru yang tertinggal (file diakhiri dengan satu; kotak teks tidak boleh), pengkodean teks yang berbeda, atau CRLF versus akhiran baris LF. Alat ini melakukan hash pada UTF-8 byte dari apa yang Anda ketikkan dan menunjukkan jumlah byte, yang biasanya membuat perbedaan menjadi jelas.
Keterbatasan
- Tabel entitas bernama HTML mencakup subset praktis — karakter penting markup, tipografi, mata uang, panah, matematika, Yunani dan Latin-1 — tidak semua 2,231 HTML5 referensi bernama. Nama-nama yang tidak dikenali dilaporkan dan dibiarkan persis seperti yang tertulis, bukan hanya dugaan.
- Penguraian kode entitas memerlukan penghentian titik koma. HTML5 mentolerir beberapa referensi lama tanpa referensi tersebut, namun mendekodekannya dengan benar bergantung pada konteks markup di sekitarnya, yang tidak dimiliki oleh alat teks mandiri.
- Pembuatan hashing dan UUID memerlukan konteks yang aman (https:// atau localhost) karena Web Crypto tidak akan terekspos sebaliknya. Alat ini melaporkan hal ini dibandingkan mengganti penerapan yang lebih lemah.
- Hanya SHA-1, SHA-256, SHA-384 dan SHA-512 yang tersedia, karena itulah yang diimplementasikan oleh SubtleCrypto. MD5 tidak hadir karena pilihan dan juga karena kebutuhan.
- Tidak ada HMAC, tidak ada derivasi kunci, dan tidak ada enkripsi di sini. Hal tersebut memerlukan manajemen kunci, yang bukan sesuatu yang harus ditangani oleh halaman yang Anda temukan di internet.
- Semuanya dibatasi oleh memori perangkat Anda, karena semuanya berjalan dalam satu tab browser. Input dibatasi — beberapa megabita per utilitas — dan alat ini menolak pekerjaan berukuran besar alih-alih membeku.