Bộ kiểm tra mã hóa phần trăm

Mã hóa phần trăm không phải một quy tắc mà là nhiều quy tắc, và chúng bất đồng về khoảng một chục ký tự. Trang này chạy cùng văn bản qua encodeURIComponent, encodeURI và cách tuần tự hóa application/x-www-form-urlencoded cạnh nhau, đánh dấu các ký tự mà chúng bất đồng, in một hàng cho mỗi ký tự với byte UTF-8 và lớp RFC 3986 của nó, và cho biết một chuỗi đã được mã hóa bao nhiêu lần. Phía giải mã đọc chuỗi theo quy tắc nghiêm hoặc khoan dung và cho bạn biết escape nào bị hỏng và ở đâu, thay vì trả về một kết quả sai một cách lặng lẽ.
Mọi xử lý diễn ra cục bộ trong trình duyệt của bạn — dữ liệu không bao giờ được tải lên máy chủ nào.

Chưa rõ mã hóa phần trăm là gì? Mã hóa phần trăm giải thích bằng tiếng Anh đơn giản — nó là gì, vì sao khoảng trắng thành %20 hoặc +, và nghĩa là gì khi một URL hiển thị %25.
Việc này làm gì:
Cách đọc các escape:
encodeURIComponent
encodeURI
form (application/x-www-form-urlencoded)
Ctrl+Enter trong ô nhập sẽ chạy chế độ đang chọn.
Chọn một chế độ và nhấn nút hành động của nó. Mã hóa & kiểm tra hiển thị ba cách mã hóa văn bản của bạn cùng một hàng cho mỗi ký tự; Giải mã đọc các escape trở lại và đếm số lớp. Nút Tự kiểm tra chạy lại mọi mẫu có tài liệu, mọi ký tự ASCII in được và các chuỗi lớp đã ghi trong tệp dữ liệu của trang này, và báo số lượng ở đây.
Nơi các bộ mã hóa bất đồng:chỉ những ký tự dưới đây được ba phương pháp xử lý khác nhau. Mọi thứ khác đi qua cả ba không đổi, hoặc được cả ba escape giống hệt nhau.
Mỗi ký tự, một hàng:các byte UTF-8 mà một ký tự thành, lớp mà RFC 3986 xếp nó vào, và escape mà mỗi ngữ cảnh viết. Bốn cột ngữ cảnh là các tập mã hóa phần trăm WHATWG, là thứ trình duyệt dùng khi dựng đường dẫn, truy vấn hay phân đoạn; một hàng tô bóng là ký tự mà ba bộ mã hóa xử lý khác nhau.
Tập chính xác mà trang này escape cho từng phần của URL:được in từ chính bộ mã hóa, nên bảng này và mã điền các cột ở trên không thể lệch nhau. Mọi tập cũng escape các ký tự điều khiển C0 (U+0000 đến U+001F) và mọi điểm mã trên U+007E, đó là lý do văn bản ngoài ASCII luôn bị escape trong mọi ngữ cảnh.
Các tập này được suy ra từ các tập mã hóa phần trăm WHATWG và khác chúng ở hai chỗ có chủ ý, cả hai đều làm việc lắp ráp bằng tay an toàn: một dấu thăng được escape bên trong truy vấn, vì một dấu thăng thô sẽ bắt đầu phân đoạn; và tập thành phần được cố định chính xác vào những ký tự mà encodeURIComponent escape, kể cả dấu nháy đơn, trong khi tập form được cố định vào những gì URLSearchParams escape. Vì vậy hai cột trong bảng trên là hành vi của chính nền tảng chứ không phải một bản viết lại. Danh sách đó cũng giải thích một kết quả mà người ta thấy bất ngờ: dấu phần trăm được encodeURIComponent và encodeURI escape, nhưng nó không nằm trong tập ngữ cảnh WHATWG nào, nên các cột đường dẫn, truy vấn và phân đoạn của bảng từng ký tự để nó yên. Một dấu phần trăm thuần trong một URL bạn lắp ráp bằng tay vẫn là rủi ro, vì chuẩn mong đợi đầu vào đã được kiểm tra trước: %41 bên trong truy vấn là một A duy nhất với nhiều framework máy chủ, và cách đọc nghiêm của chính trang này coi một % đơn là escape hỏng. Tự escape nó thành %25 là thói quen an toàn hơn, và bảng từng ký tự hiển thị cả hai cách viết cạnh nhau.
Chuỗi này đã được mã hóa bao nhiêu lần:mỗi vòng giải mã là một lớp. Một giá trị đã được mã hóa hai lần bắt đầu bằng%25chỗ mà một lần mã hóa đơn lẻ sẽ để lại%20, và một giá trị đã được mã hóa ba lần bắt đầu bằng%2525.
Các ký tự URL có thể mang nguyên dạng:RFC 3986 chia phạm vi ASCII thành ba nhóm, và các quy tắc escape chỉ có nghĩa khi đối chiếu với sự phân chia đó.
Các ký tự dành riêng không bị cấm, chúng là đã chiếm: chúng có nghĩa với bộ phân tích URL. Quy tắc suy ra từ danh sách rất ngắn gọn — nếu một ký tự là dành riêng và bạn muốn nó là dữ liệu, hãy escape nó; nếu nó không dành riêng, bạn không bao giờ cần làm vậy.
Ba phương pháp, và mỗi cái để yên điều gì:
Các danh sách là chính xác các tập mà mỗi phương pháp không escape trong phạm vi ASCII; Tự kiểm tra kiểm tra từng ký tự ASCII in được với chúng, nên bảng không thể lệch khỏi các bộ mã hóa mà nó mô tả.
Trang này nhận đầu vào lớn đến đâu, và vì sao:mã hóa phần trăm là một lượt quét văn bản duy nhất, nên không giống bộ chuyển đổi cơ số trên trang này, chi phí tăng theo đường thẳng chứ không theo bình phương: không có độ dài nào mà phép tính chính nó trở thành vấn đề. Các giới hạn dưới đây tồn tại vì bảng từng ký tự được vẽ trong tài liệu, và một bảng có một triệu hàng chính là thứ thực sự làm trình duyệt dừng.
Mã hóa phần trăm là gì?

Mã hóa phần trăm — còn gọi là mã hóa URL, và trong tài liệu cũ là escape — là quy tắc cho phép một URL mang các ký tự mà nó không được phép chứa. Một escape phần trăm là ba ký tự ASCII: một dấu phần trăm và hai chữ số hex, đại diện cho một byte. %20 là byte 0x20, tức khoảng trắng. %2F là byte 0x2F, tức dấu gạch chéo. Không có gì khác liên quan: escape đặt tên cho một byte, không phải một ký tự.

Vì nó đặt tên cho các byte, văn bản trước hết phải được chuyển thành byte, và mã hóa byte là UTF-8. Đó là nơi các escape dài đến từ: một ký tự Trung Quốc là ba byte UTF-8 và do đó ba escape, và một emoji là bốn byte và mười hai ký tự ASCII.

Phiên bản 30 giây: khoảng trắng là %20 như một giá trị URL nhưng là + trong dữ liệu form; # là %23; dấu phần trăm thuần là %25. Ba bộ mã hóa trên trang này đồng ý về chữ cái, chữ số và -_., và bất đồng về khoảng một chục dấu câu, trong đó có dấu ngã — đó chính xác là lý do encodeURIComponent và encodeURI cho ra chuỗi khác nhau với cùng một văn bản, và vì sao dùng nhầm một trong hai có thể tách một giá trị thành hai tham số truy vấn.
Vì sao URL cần nó

URI được định nghĩa là một dãy ký tự ASCII (RFC 3986 mục 2.1), và trong phạm vi ASCII đó một số ký tự là dành riêng: chúng là các dấu câu tạo nên cấu trúc của URL. Dấu hai chấm và hai dấu gạch chéo phân tách lược đồ, dấu @ phân tách thông tin người dùng với máy chủ, dấu hỏi bắt đầu truy vấn, dấu thăng bắt đầu phân đoạn, dấu và phân tách các tham số truy vấn, và dấu phần trăm đưa vào một escape.

Điều đó tạo ra một vấn đề và một lỗ hổng. Vấn đề: một giá trị có chứa chính một trong các ký tự đó — một ô tìm kiếm chứa "a&b", một tên tệp chứa dấu gạch chéo, một mật khẩu chứa dấu hai chấm — nếu không sẽ đổi cấu trúc của URL mà nó đi qua. Lỗ hổng: chính dấu phần trăm, nên bộ mã hóa có thể mở rộng bảng chữ cái của chính nó. % được viết là %25, và hai chữ số hex theo sau là các ký tự không dành riêng thông thường.

Ví dụ có giải

Đây là đầu ra chính xác của ba bộ mã hóa mà trang này chạy, trên các đầu vào được chọn vì chúng hành xử khác nhau. Trang tính lại chúng trước mắt bạn, và Tự kiểm tra kiểm tra lại từng hàng với chính các cài đặt của nền tảng.

Ba phương pháp, và khi nào mỗi cái là đúng

Phiên bản ngắn giúp tránh phần lớn lỗi: mọi thứ đi vào một giá trị tham số đơn đều qua encodeURIComponent (hoặc qua một bộ tuần tự hóa form, làm cùng việc với tập an toàn khác), và một URL hoàn chỉnh đã được lắp ráp đi qua encodeURI nếu cần escape. Dùng nhầm một trong hai cái này là lỗi mã hóa phần trăm phổ biến nhất trong mã web.

Các ký tự một URL có thể mang, đầy đủ
Hai dấu cộng, hai nghĩa

Khoảng trắng là ký tự duy nhất mà việc mã hóa phụ thuộc vào nơi văn bản sẽ đi tới, và dấu cộng là lý do.

Cái bẫy nằm ở giữa bảng đó. Trong dữ liệu form, dấu cộng nghĩa là khoảng trắng, nên một giá trị thực sự chứa dấu cộng — một số điện thoại viết như +44 20 7946 0958, một biểu thức số học, một địa chỉ email có thẻ cộng — phải đi dưới dạng %2B. Nếu nó đi dưới dạng dấu cộng thuần, bên nhận đọc lại thành khoảng trắng, và +44 đến nơi thành " 44".

Được mã hóa hai lần, và cách nhận ra nó

Mã hóa kép xảy ra khi một chuỗi đã được escape và rồi lại được escape. Dấu phần trăm của escape đầu là một ký tự như mọi ký tự khác, nên nó thành %25, và lớp bên dưới còn lại như văn bản nhìn thấy được.

Nhận ra nó là vấn đề đếm: giải mã chuỗi một lần và xem kết quả. Nếu nó vẫn chứa các escape trông có chủ ý — một %XX hợp lệ hoặc một dấu cộng form ở chỗ cần khoảng trắng — thì còn một lớp nữa. Nếu nó chứa %25 theo sau bởi hai chữ số hex, lớp kế tiếp chắc chắn là một escape.

Hai lưu ý thực tế. Thứ nhất, giải mã hai lần không vô hại: đó là cách nổi tiếng để khiến một bộ lọc bảo mật và mã phía sau thấy các chuỗi khác nhau, nên một bộ lọc chạy trước lần giải mã thứ hai đang lọc sai văn bản. Thứ hai, số lớp ở đây là thuộc tính của chuỗi, không phải lời hứa về ý định: một giá trị hợp lệ có thể chỉ sâu một lớp và một giá trị hỏng có thể sâu hai lớp.

Những sai lầm trang này được xây để bắt
  • Giá trị được mã hóa bằng encodeURI. Các dấu phân tách còn nguyên, nên một giá trị chứa & hoặc = bị tách thành nhiều tham số ở đầu bên kia. So sánh hai cột ở đầu trang này với đầu vào a&b=c: cột component escape cả hai ký tự, cột encodeURI giữ chúng.
  • Dấu cộng thuần được gửi như dữ liệu form. Nó đến như một khoảng trắng. Dùng %2B.
  • Giá trị được escape hai lần "cho chắc". Nó không an toàn hơn. Bên nhận giải mã một lần và có được một chuỗi vẫn chứa escape phần trăm, cùng lỗi như một bảng ký tự sai, chỉ nằm ở một tầng thấp hơn.
  • Giải mã thứ chưa từng được mã hóa. Dấu phần trăm thuần trong văn bản — 100% sure — không phải một escape. Theo cách đọc nghiêm, nó là một escape hỏng, tại vị trí trang này báo cáo; theo cách đọc khoan dung, nó được giữ lại, đó là lý do khoan dung là mặc định ở đây chứ không phải ngược lại.
  • Giả định escape phải viết hoa. Giải mã coi %2f và %2F là cùng một byte, và cả hai đều hợp lệ. Viết hoa chỉ là cách viết chính tắc mà RFC 3986 khuyến nghị, và là cách các công cụ khác sẽ tạo ra.
  • Giả định mã hóa phần trăm làm văn bản an toàn. Nó làm văn bản không mơ hồ trong một URL. Nó không ngăn tiêm mã, không phải mã hóa, và không phải bộ làm sạch: việc escape phải là bước cuối cùng trước khi lắp ráp, cho ngữ cảnh mà văn bản sẽ đi vào.
Trả lời nhanh

"Dấu cộng có phải khoảng trắng trong URL không?"

Chỉ trong dữ liệu form, và chỉ ở phần truy vấn của một lần gửi form. Trong đường dẫn, trong phân đoạn và trong truy vấn của một liên kết thường, dấu cộng là dấu cộng thuần. Đó là lý do trang này có một công tắc cho nó thay vì đoán.

"Vì sao liên kết của tôi hiển thị %25?"

Vì có thứ đã mã hóa dấu phần trăm. Hoặc một dấu phần trăm thuần đã đi qua một bộ mã hóa — 100% sure thành 100%25%20sure — hoặc một chuỗi đã chứa escape được mã hóa lần thứ hai. Giải mã chuỗi một lần và xem điều gì trả về: nếu kết quả vẫn chứa %XX, còn một lớp nữa.

"Dấu gạch chéo trong giá trị truy vấn là %2F hay / có quan trọng không?"

Với bộ phân tích URL, không: cả hai đều đến máy chủ dưới dạng dấu gạch chéo, và tập truy vấn WHATWG để dấu gạch chéo yên. Với khả năng tương tác, đôi khi có: nhiều framework máy chủ tự giải mã chuỗi truy vấn thô, và vài cái trong số đó bất đồng về những ký tự chưa từng được escape. Khi một giá trị được lắp ráp bằng tay thay vì bằng bộ tuần tự hóa, escape rõ ràng các ký tự dành riêng là thói quen an toàn hơn.

"Vì sao cột form escape dấu ngã mà hai cột kia để yên?"

Vì tập an toàn của bộ mã hóa form hẹp hơn: chỉ chữ cái, chữ số và -_.*. Bộ mã hóa form WHATWG escape ~ ! ' ( ) dù RFC 3986 liệt kê ~ là không dành riêng. Cả hai cách viết giải mã ra cùng giá trị; sự khác biệt chỉ quan trọng khi so sánh chuỗi theo từng byte.

"Các ký tự này có thể xuất hiện không bị escape trong thanh địa chỉ trình duyệt không?"

Có, và đó là thanh địa chỉ, không phải URL. Trình duyệt hiển thị một dạng dễ đọc của URL và mã hóa nó khi gửi, nên 中文 trong một liên kết thành %E4%B8%AD%E6%96%87 trên đường truyền. Sao chép URL từ thanh địa chỉ và bạn có thể nhận một trong hai dạng tùy trình duyệt.

"Mọi escape ở đây trông hợp lệ, nhưng ký tự đi ra không phải ký tự tôi mong đợi. Vì sao?"

Hai thất bại khác nhau nấp sau "giải mã sai", và trang báo cáo chúng khác nhau. Thứ nhất là một escape hỏng: một dấu phần trăm không theo sau bởi hai chữ số hex, như 100% ở cuối hoặc %zz. Đó là vấn đề cú pháp; cách đọc nghiêm từ chối nó và nói rõ vị trí, còn cách đọc khoan dung giữ nó như văn bản thuần và liệt kê nó. Thứ hai là một escape có byte hoàn toàn hợp lệ nhưng các byte không tạo thành ký tự UTF-8 hợp lệ, như %FF đứng một mình hay một ký tự ba byte bị cắt thành %E4%B8. Đó không phải vấn đề cú pháp, nên cả hai cách đọc đều trả về văn bản, thay các byte không giải mã được bằng ký tự thay thế U+FFFD ("�"), và liệt kê một cảnh báo nói như vậy. Cảnh báo đó mới là phần hữu ích: văn bản đến dưới dạng ký tự thay thế thường có nghĩa là người gửi dùng bảng ký tự khác, hoặc chuỗi đã được escape quá một lần. Mã hóa phần trăm đặt tên cho byte, và trang này, giống chuẩn URL, luôn đọc các byte đó là UTF-8.

"Trang này không làm gì?"

Nó không tách một URL thành lược đồ, máy chủ, đường dẫn và truy vấn — đó là việc của Bộ phân tích URL. Nó không kiểm tra liệu một liên kết có an toàn để mở, không liên lạc với gì, và không đoán bảng ký tự của các byte đằng sau một escape: nó luôn đọc chúng là UTF-8, đúng như chuẩn URL yêu cầu.

"Giải mã cần cơ chế dự phòng nào, và làm sao tôi biết nó đúng?"

Nhấn Tự kiểm tra. Nó so sánh các bộ mã hóa của trang với encodeURIComponent, encodeURI và URLSearchParams của nền tảng cho mọi ký tự ASCII in được, chạy lại mọi ví dụ trong các bảng ở trên, kiểm tra lại các chuỗi lớp đã ghi, rồi báo số lượng kiểm tra ở đây.

Tiêu chuẩn và phạm vi

Mã hóa phần trăm được quy định trong RFC 3986 mục 2.1, với định nghĩa ký tự dành riêng và không dành riêng ở mục 2.2 và 2.3 và quy tắc viết hoa ở mục 6.2.2.1. Các tập mà trình duyệt thực sự dùng khi dựng một URL nằm trong Chuẩn URL WHATWG, tách theo ngữ cảnh: một tập cho phân đoạn, một cho truy vấn, một cho truy vấn đặc biệt, một cho đường dẫn, một cho thông tin người dùng, và tập hẹp hơn dùng cho một thành phần đơn. Việc gửi form, kể cả dấu cộng, nằm trong Chuẩn HTML dưới application/x-www-form-urlencoded. Hai hàm encodeURI và encodeURIComponent là một phần của chính ECMAScript.

Trang này điền các cột đường dẫn, truy vấn và phân đoạn từ các tập theo ngữ cảnh của riêng nó, và in danh sách chính xác cho mỗi ngữ cảnh cạnh kết quả. Các tập đó được suy ra từ các tập WHATWG và khác chúng ở hai chỗ có chủ ý: một dấu thăng được escape bên trong truy vấn, vì một dấu thăng thô sẽ bắt đầu phân đoạn, và tập thành phần được cố định chính xác vào những ký tự mà encodeURIComponent escape. Khi một hàm của nền tảng là định nghĩa của đáp án — encodeURIComponent, encodeURI và cách tuần tự hóa form — hàm đó được gọi trực tiếp, nên các cột đó là hành vi của chính trình duyệt chứ không phải một bản viết lại của nó.

Văn bản trang được viết cho trang web này. Mọi giá trị trong các bảng ở trên được tạo bởi chính mã của trang khi tải và đã được kiểm tra với các cài đặt của nền tảng trong công cụ kiểm thử của trang web này; Tự kiểm tra lặp lại phép so sánh đó trong trình duyệt của bạn.