Công cụ dành cho nhà phát triển · JSON trình định dạng và xác thực
ID số nguyên lớn trong JSON: tại sao trình định dạng JavaScript có thể làm tròn chúng
· Tại sao nó quan trọng
json developer-workflow xác nhận
JSON cho phép số nguyên có kích thước bất kỳ, nhưng JavaScript biểu thị các số dưới dạng số float 64-bit, do đó, mọi thứ ở trên 2^53 đều có thể thay đổi khi được phân tích cú pháp và sắp xếp lại. Bài đăng này giải thích giới hạn, cách phát hiện thiệt hại và cách bảo vệ ID.
ID đã thay đổi bởi một
ID được một người thay đổi thường có dạng JSON hoàn toàn hợp lệ. Đặt `{"orderId":9007199254740993}` vào JavaScript và `JSON.parse` trả về một Số có giá trị được hiển thị là `9007199254740992`. Phân tích cú pháp thành công vì mã thông báo tuân theo ngữ pháp số JSON; hư hỏng xảy ra khi chuyển đổi các chữ số thập phân đó thành dạng số của JavaScript. Trình định dạng tuần tự hóa giá trị được phân tích sẽ ghi một cách trung thực Số được làm tròn chứ không phải mã thông báo chính xác xuất hiện trong nguồn.
Sự tương phản là ngay lập tức khi các chữ số tương tự được trích dẫn. `JSON.parse("{"orderId":"9007199254740993"}")` trả về chuỗi `9007199254740993`, giữ nguyên mọi ký tự và `JSON.stringify` phát ra các chữ số đó không thay đổi bên trong dấu ngoặc kép. Đây là lý do tại sao việc xác thực cú pháp không thể bảo vệ mã định danh dạng số. So sánh đầu vào và đầu ra bất cứ khi nào số nguyên dài xuất hiện và coi mã định danh là chuỗi tại ranh giới tạo khi số học không phải là một phần ý nghĩa của chúng.
RFC 8259 nói gì về các con số
RFC 8259 xác định cách viết của số JSON nhưng không cung cấp cho mỗi lần triển khai một loại số có độ chính xác tùy ý. Ngữ pháp cho phép dấu trừ tùy chọn, phần số nguyên và phần phân số và số mũ tùy chọn. Nó không bao gồm các tiện ích như ký hiệu thập lục phân, `NaN` và `Infinity`. Do đó, `9007199254740993` có giá trị về mặt cú pháp mặc dù người tiêu dùng JavaScript thông thường không thể biểu thị số nguyên đó chính xác dưới dạng Số.
Hướng dẫn về khả năng tương tác của đặc tả là cảnh báo thực tế: phần mềm thường sử dụng IEEE 754 số nhị phân64 và các số nguyên trong phạm vi từ âm `2^53 + 1` đến dương `2^53 - 1` đều có thể tương tác theo nghĩa thỏa thuận chính xác. Trình xác thực có thể chấp nhận chính xác mã thông báo lớn hơn trong khi trình phân tích cú pháp làm tròn nó sau đó.
2^53 đến từ đâu
Ranh giới `2^53` xuất phát từ độ chính xác có sẵn ở mức ý nghĩa nhị phân64. JavaScript hiển thị số nguyên có thể biểu thị liên tiếp cao nhất là `Number.MAX_SAFE_INTEGER`, tức là `9007199254740991`. Tại và dưới độ lớn đó, các số nguyên liền kề có thể được biểu diễn rõ ràng. Ở trên nó, khoảng cách giữa các giá trị có thể biểu thị tăng lên, do đó một số số nguyên thập phân lân cận ánh xạ tới cùng một Số. Thời gian chạy không cắt ngắn một chuỗi; nó đang chọn giá trị gần nhất có sẵn ở định dạng nhị phân hữu hạn đó.
Kiểm tra bảng điều khiển tiết lộ là `Number.isSafeInteger(9007199254740993)`, sai, mặc dù chữ nguồn đã được làm tròn trước khi hàm nhận được nó. Một cái khác là `9007199254740992 === 9007199254740993`, đánh giá đúng trong JavaScript. Những ví dụ này liên quan đến nhận dạng số nguyên chính xác, chứ không phải liệu mọi số lớn hơn có trở nên không sử dụng được hay không.
Cách phân tích cú pháp và sắp xếp lại mất chữ số
Định dạng phân tích cú pháp và sắp xếp lại có ba giai đoạn: đọc ký tự số, tạo giá trị trong bộ nhớ, sau đó tạo ký tự mới từ giá trị đó. Chi tiết từ vựng biến mất ở giai đoạn giữa. Với `{"ticket":9223372036854775807}`, `JSON.parse` tạo số JavaScript có sẵn gần nhất; `JSON.stringify` sau đó phát ra `9223372036854776000`. Trình tuần tự hóa không làm hỏng mã thông báo được bảo tồn một cách độc lập. Theo thời gian tuần tự hóa, chuỗi chữ số ban đầu không còn xuất hiện trong đối tượng được phân tích cú pháp.
Việc triển khai kho lưu trữ của ToolAcre sử dụng `JSON.parse` và `JSON.stringify`, vì vậy giới hạn này áp dụng cho đầu ra được định dạng của nó. Trình quét cú pháp của nó chạy để cung cấp lý do và vị trí ổn định sau khi phân tích cú pháp không thành công; nó không thay thế các số JavaScript bằng cách biểu diễn có độ chính xác tùy ý. Do đó, kết quả xác thực thành công sẽ thiết lập ngữ pháp, trong khi khác biệt về định dạng có thể cho thấy sự mất chính xác.
Ví dụ hoạt động: so sánh đầu vào và đầu ra
So sánh `{"numeric":9007199254740993,"text":"9007199254740993"}` trước và sau chuyến đi khứ hồi JavaScript. Việc chạy `JSON.stringify(JSON.parse(source), null, 2)` sẽ tạo ra một đối tượng được định dạng có thành viên `numeric` là `9007199254740992`, trong khi `text` vẫn là `"9007199254740993"`. Cả hai thành viên đều hợp lệ ở đầu vào và cả hai vẫn hợp lệ ở đầu ra. Chỉ biểu diễn được trích dẫn mới bảo toàn chính xác mã định danh vì nó được giải mã dưới dạng dữ liệu ký tự chứ không phải là Số.
Một đánh giá hữu ích không chỉ hỏi xem trình định dạng có hiển thị màu xanh lục hay không. Tìm kiếm nguồn cho các chuỗi chữ số không bị gián đoạn, so sánh bất kỳ giá trị nào dài hơn phạm vi an toàn và xác định xem mỗi trường đại diện cho một số lượng hay một nhãn mờ. Nếu nhà sản xuất kiểm soát hợp đồng, hãy thay đổi nhãn thành một chuỗi ở đó và ghi lại lựa chọn đó cho người tiêu dùng.
Bảo vệ ID tại nguồn
Bảo vệ ID tại nguồn bằng cách xác định chúng dưới dạng chuỗi trong lược đồ và tuần tự hóa chúng dưới dạng chuỗi trước khi bất kỳ ứng dụng khách JavaScript nào nhận được tải trọng. ID có thể chỉ chứa các chữ số và vẫn không phải là số: phép cộng, làm tròn và sắp xếp theo độ lớn không phải là các hoạt động hợp pháp trên khóa tài khoản. Một chuỗi cũng bảo toàn các số 0 đứng đầu mà biểu diễn số sẽ loại bỏ ngay cả khi độ lớn của nó nằm trong phạm vi an toàn.
Không suy ra sự an toàn của nhiều ngôn ngữ từ thực tế là một thời gian chạy khác có thể chứa số nguyên lớn hơn. Các trình phân tích cú pháp và loại mục tiêu khác nhau và một trình trung gian được viết bằng JavaScript có thể làm tròn giá trị trước khi dịch vụ sau này nhìn thấy nó. Một số trình phân tích cú pháp chuyên dụng bảo toàn mã thông báo số hoặc xây dựng các số nguyên lớn, nhưng mọi người tham gia phải chia sẻ hợp đồng đó.
Điều này không bao gồm những gì
Điều này không đề cập đến thiết kế rộng hơn của số học thập phân. Các giá trị như `0.1` có hành vi dấu phẩy động nhị phân riêng và tiền có thể yêu cầu số nguyên tỷ lệ hoặc loại thập phân theo hợp đồng ứng dụng. Việc trích dẫn mọi con số cũng không tự động cải thiện một lược đồ. Số lượng, tọa độ và phép đo thường là số hợp pháp. Quyết định phụ thuộc vào việc chính tả thập phân chính xác hay nhận dạng số nguyên chính xác có phải tồn tại với mọi người tiêu dùng trong đường dẫn dữ liệu hay không.
Cuộc thảo luận này cũng không khẳng định rằng JSON tự làm tròn mã thông báo hoặc tất cả các trình phân tích cú pháp đều hoạt động giống như JavaScript. Bằng chứng kho lưu trữ cụ thể hẹp hơn: trình định dạng này gọi `JSON.parse` và `JSON.stringify`, vì vậy JavaScript Ngữ nghĩa số chi phối các giá trị không được trích dẫn ở đây. Thư viện JSON có độ chính xác tùy ý có thể đưa ra các lựa chọn khác nhau nhưng phải xác định cách hiển thị và tuần tự hóa các giá trị.
Bài học rút ra: các số ở trên 2^53 thuộc về chuỗi
Bài học rút ra rất cụ thể: số nhận dạng số nguyên nằm ngoài phạm vi an toàn của JavaScript thuộc về chuỗi khi chúng phải đi qua JavaScript mà không thay đổi. `9007199254740993` dưới dạng số JSON là cú pháp hợp lệ nhưng trở thành `9007199254740992` sau `JSON.parse`; `"9007199254740993"` vẫn chính xác. Các trích dẫn không phải là trang trí. Họ chọn một cách thể hiện để lưu giữ các chữ số dưới dạng dữ liệu và ngăn người tiêu dùng coi nhãn mờ như một số lượng gần đúng.
Trước khi thay thế tài liệu bằng đầu ra định dạng, hãy so sánh các số dài với số gốc và điều tra từng chữ số đã thay đổi. Sửa lỗi trình tạo và lược đồ khi có thể để tất cả các máy khách xuôi dòng đều nhận được biểu mẫu an toàn một cách nhất quán. ToolAcre có thể hiển thị hậu quả vì đầu ra của nó phản ánh giá trị JavaScript được phân tích cú pháp nhưng không thể tái tạo lại các chữ số đã bị mất trong quá trình phân tích cú pháp.