Alat pembangun · JSON pemformat & pengesah
ID integer besar dalam JSON: mengapa pemformat JavaScript boleh membulatkannya
· Mengapa ia penting
json aliran kerja pembangun pengesahan
JSON membenarkan integer dalam sebarang saiz, tetapi JavaScript mewakili nombor sebagai 64-bit terapung, jadi apa-apa sahaja di atas 2^53 boleh berubah apabila dihuraikan dan disiri semula. Siaran ini menerangkan had, cara mengesan kerosakan dan cara melindungi ID.
ID yang berubah satu
ID yang ditukar oleh satu selalunya tiba sebagai sah sempurna JSON. Masukkan `{"orderId":9007199254740993}` ke dalam JavaScript dan `JSON.parse` mengembalikan Nombor yang nilainya dipaparkan ialah `9007199254740992`. Penghuraian berjaya kerana token mengikut tatabahasa nombor JSON; kerosakan berlaku semasa menukar digit perpuluhan tersebut kepada perwakilan angka JavaScript. Pemformat yang menyerikan nilai yang dihuraikan dengan setia menulis Nombor bulat, bukan token tepat yang muncul dalam sumber.
Perbezaannya adalah serta-merta apabila digit yang sama disebut. `JSON.parse("{"orderId":"9007199254740993"}")` mengembalikan rentetan `9007199254740993`, mengekalkan setiap aksara dan `JSON.stringify` mengeluarkan digit tersebut tidak berubah dalam petikan. Inilah sebabnya mengapa pengesahan sintaks sahaja tidak dapat melindungi pengecam berangka. Bandingkan input dan output apabila integer panjang muncul, dan layan pengecam sebagai rentetan pada sempadan yang menghasilkan apabila aritmetik bukan sebahagian daripada maksudnya.
Apa yang RFC 8259 katakan tentang nombor
RFC 8259 mentakrifkan ejaan nombor JSON tetapi tidak memberikan setiap pelaksanaan jenis numerik ketepatan arbitrari. Tatabahasa membenarkan tanda tolak pilihan, bahagian integer, dan bahagian pecahan dan eksponen pilihan. Ia tidak termasuk kemudahan seperti tatatanda heksadesimal, `NaN` dan `Infinity`. Akibatnya, `9007199254740993` adalah sah dari segi sintaksis walaupun pengguna JavaScript biasa tidak boleh mewakili integer itu tepat sebagai Nombor.
Panduan kebolehoperasian spesifikasi ialah amaran praktikal: perisian biasanya menggunakan IEEE 754 nombor binari64, dan integer dalam julat daripada negatif `2^53 + 1` hingga positif `2^53 - 1` boleh beroperasi dalam erti kata persetujuan yang tepat. Pengesah boleh menerima token yang lebih besar dengan betul sementara penghurai kemudian membundarkannya.
Dari mana 2^53 berasal
Sempadan `2^53` berasal daripada ketepatan yang terdapat dalam significand binari64. JavaScript mendedahkan integer boleh diwakili berturut-turut tertinggi sebagai `Number.MAX_SAFE_INTEGER`, iaitu `9007199254740991`. Pada dan di bawah magnitud itu, integer bersebelahan boleh diwakili dengan jelas. Di atasnya, jarak antara nilai yang boleh diwakili bertambah, jadi beberapa integer perpuluhan jiran dipetakan kepada Nombor yang sama. Masa jalan tidak memotong rentetan; ia memilih nilai terdekat yang tersedia dalam format binari terhingga itu.
Semakan konsol mendedahkan ialah `Number.isSafeInteger(9007199254740993)`, iaitu palsu, walaupun literal sumber telah dibundarkan sebelum fungsi menerimanya. Satu lagi ialah `9007199254740992 === 9007199254740993`, yang menilai benar dalam JavaScript. Contoh-contoh ini melibatkan identiti integer tepat, bukan sama ada setiap nombor yang lebih besar menjadi tidak boleh digunakan.
Cara menghurai dan mensirikan semula kehilangan digit
Pemformatan huraikan dan siri semula mempunyai tiga peringkat: baca aksara angka, cipta nilai dalam memori, kemudian jana aksara baharu daripada nilai tersebut. Butiran leksikal hilang pada peringkat pertengahan. Dengan `{"ticket":9223372036854775807}`, `JSON.parse` mencipta Nombor JavaScript terdekat yang tersedia; `JSON.stringify` kemudian mengeluarkan `9223372036854776000`. Penyeri bersiri tidak secara bebas merosakkan token yang dipelihara. Dengan masa bersiri, jujukan digit asal tidak lagi terdapat dalam objek yang dihuraikan.
Pelaksanaan repositori ToolAcre menggunakan `JSON.parse` dan `JSON.stringify`, jadi pengehadan ini digunakan pada output terformatnya. Pengimbas sintaksnya berfungsi untuk memberikan sebab dan lokasi yang stabil selepas penghuraian gagal; ia tidak menggantikan nombor JavaScript dengan perwakilan ketepatan arbitrari. Oleh itu, hasil pengesahan yang berjaya mewujudkan tatabahasa, manakala perbezaan pemformatan boleh mendedahkan kehilangan ketepatan.
Contoh kerja: membandingkan input dan output
Bandingkan `{"numeric":9007199254740993,"text":"9007199254740993"}` sebelum dan selepas JavaScript perjalanan pergi dan balik. Menjalankan `JSON.stringify(JSON.parse(source), null, 2)` menghasilkan objek berformat yang ahli `numeric`nya ialah `9007199254740992`, manakala `text` kekal `"9007199254740993"`. Kedua-dua ahli adalah sah dalam input, dan kedua-duanya kekal sah dalam output. Sahaja perwakilan yang dipetik mengekalkan pengecam dengan tepat kerana ia dinyahkodkan sebagai data aksara dan bukannya Nombor.
Semakan berguna bukan sahaja bertanya sama ada pemformat menunjukkan hijau. Cari sumber untuk jujukan digit yang tidak terganggu, bandingkan sebarang nilai yang lebih panjang daripada julat selamat dan tentukan sama ada setiap medan mewakili kuantiti atau label legap. Jika pengeluar mengawal kontrak, tukar label kepada rentetan di sana dan dokumenkan pilihan itu untuk pengguna.
Melindungi ID di sumber
Lindungi ID pada sumber dengan mentakrifkannya sebagai rentetan dalam skema dan mensirikannya sebagai rentetan sebelum mana-mana pelanggan JavaScript menerima muatan. ID mungkin mengandungi sahaja digit dan masih bukan angka dalam maksud: penambahan, pembundaran dan susunan mengikut magnitud bukanlah operasi yang sah pada kunci akaun. Rentetan juga mengekalkan sifar pendahuluan, yang akan dibuang oleh perwakilan angka walaupun magnitudnya berada dalam julat selamat.
Jangan menyimpulkan keselamatan merentas bahasa daripada fakta bahawa masa jalan lain boleh memegang integer yang lebih besar. Penghurai dan jenis sasaran berbeza-beza, dan perantara yang ditulis dalam JavaScript boleh membulatkan nilai sebelum perkhidmatan kemudian melihatnya. Sesetengah penghurai khusus mengekalkan token nombor atau membina integer besar, tetapi setiap peserta mesti berkongsi kontrak tersebut.
Perkara ini tidak meliputi
Perkara ini tidak meliputi reka bentuk aritmetik perpuluhan yang lebih luas. Nilai seperti `0.1` mempunyai gelagat titik terapung binari mereka sendiri dan wang mungkin memerlukan integer berskala atau jenis perpuluhan mengikut kontrak aplikasi. Petikan setiap nombor juga tidak menambah baik skema secara automatik. Kiraan, koordinat dan ukuran selalunya adalah angka yang sah. Keputusan bergantung pada sama ada ejaan perpuluhan tepat atau identiti integer tepat mesti bertahan setiap pengguna dalam laluan data.
Perbincangan ini juga tidak mendakwa bahawa JSON sendiri membulatkan token atau bahawa semua penghurai berkelakuan seperti JavaScript. Bukti repositori konkrit adalah lebih sempit: pemformat ini memanggil `JSON.parse` dan `JSON.stringify`, jadi JavaScript Semantik nombor mengawal nilai yang tidak disebut di sini. Pustaka JSON ketepatan arbitrari boleh membuat pilihan yang berbeza, tetapi ia mesti menentukan cara nilai didedahkan dan disiri.
Bawa pulang: nombor di atas 2^53 tergolong dalam rentetan
Bawa pulang adalah khusus: pengecam integer di luar julat selamat JavaScript tergolong dalam rentetan apabila ia mesti melalui JavaScript tanpa berubah. `9007199254740993` sebagai nombor JSON ialah sintaks yang sah tetapi menjadi `9007199254740992` selepas `JSON.parse`; `"9007199254740993"` kekal tepat. Petikan itu bukan hiasan. Mereka memilih perwakilan yang mengekalkan digit sebagai data dan menghalang pengguna daripada menganggap label legap sebagai kuantiti anggaran.
Sebelum menggantikan dokumen dengan output pemformat, bandingkan nombor panjang dengan yang asal dan siasat setiap digit yang berubah. Betulkan pengeluar dan skema apabila boleh supaya semua pelanggan hiliran menerima borang selamat secara konsisten. ToolAcre boleh mendedahkan akibatnya kerana outputnya mencerminkan nilai JavaScript yang dihuraikan, tetapi ia tidak boleh membina semula digit yang telah hilang semasa penghuraian.