1 điểm bởi GN⁺ 4 giờ trước | 1 bình luận | Chia sẻ qua WhatsApp
  • Zone IPv6 là cách ký hiệu để phân biệt giao diện đích như fe80::4%eth0 khi nhiều giao diện cùng dùng phạm vi link-local fe80::
  • Trong URL, địa chỉ IPv6 được đặt trong dấu ngoặc vuông như [fe80::4]:80 để phân biệt cổng với dấu hai chấm, và khi thêm zone thì ký hiệu ngoặc vuông trở thành dạng [fe80::4%eth0]:80
  • net/url của Go diễn giải ]:80 thành URL escape %et không hợp lệ và gây lỗi invalid URL escape
  • RFC 6874 định nghĩa IPv6 literal có zone là IPv6address "%25" ZoneID, nên trong URL ký tự % phải được percent-encode thành %25
  • Nếu Anubis cần trỏ tới địa chỉ IPv6 có zone thì phải dùng cách viết này, và đây vẫn là một trường hợp biên liên quan cả origin lẫn khả năng tương thích thư viện như các vấn đề ở trình duyệt, nginx và Requests

Xung đột giữa zone IPv6 và cú pháp URL

  • Địa chỉ link-local của IPv6 có thể nằm trong dải fe80::whatever trên từng giao diện, nên khi có hai giao diện mạng, muốn phân biệt đích fe80::4 thì cần dùng IPv6 scope/zone)
  • Định dạng giá trị zone khác nhau tùy hệ điều hành: trên Linux dùng tên giao diện, còn trên Windows dùng ID giao diện
  • Trong ví dụ, eth0 là tên thiết bị Ethernet và địa chỉ được biểu diễn như sau
fe80::4%eth0
  • Host và port thường được phân tách bằng dấu hai chấm, nhưng địa chỉ IPv6 cũng dùng dấu hai chấm để ngăn nhóm hex, nên fe80::4 với cổng 80 phải được đặt trong ngoặc vuông như sau
[fe80::4]:80
  • Nếu thêm zone thì sẽ có dạng sau
[fe80::4%eth0]:80
  • Nếu đưa trực tiếp vào hostname của URL như http://[fe80::4%eth0]:80 thì net/url của Go sẽ hiểu %et là URL escape không hợp lệ và thất bại
panic: parse "http://[fe80::4%eth0]:80";: invalid URL escape "%et"

Cách giải quyết theo chuẩn và những vấn đề còn lại

  • Các giá trị không phù hợp với cú pháp URL phải dùng percent-encoding; ví dụ %20 trong URL là cách mã hóa khoảng trắng ASCII vốn không hợp lệ trong URL
  • Ký tự % của zone IPv6 cũng phải được mã hóa, nên trong Go phải viết như http://[fe80::4%25eth0]:80 thì kết quả Hostname() mới là fe80::4%eth0
  • RFC 9844 đưa ra hướng dẫn xử lý zone IPv6 trong giao diện người dùng, còn RFC 6874 định nghĩa cú pháp IPv6 literal có zone trong URL như sau
IP-literal = "[" ( IPv6address / IPv6addrz / IPvFuture  ) "]"

ZoneID = 1*( unreserved / pct-encoded )

IPv6addrz = IPv6address "%25" ZoneID
  • Trường hợp biên tương tự cũng xuất hiện trong ticket của nginx, issue của Requestsbản thảo BCP về HTTP link-local URI
  • Trình duyệt hiện chưa hỗ trợ zone IPv6 vì điều đó phá vỡ khái niệm “origin”, vốn được dùng cho nhiều hành vi tinh vi; bản thảo nói trên là nỗ lực nhằm định nghĩa origin cho zone IPv6 để trình duyệt có thể sử dụng
  • Nếu Anubis cần trỏ tới địa chỉ IPv6 có zone thì ký tự % phải được percent-encode, và dưới chính sách không fork thư viện chuẩn Go thì phải chấp nhận UX kém của trường hợp biên này

1 bình luận

 
Ý kiến trên Lobste.rs
  • Kết luận kiểu TL;DR: máy tính là một sai lầm có phải hơi giống kiểu đổ nước tắm mà quăng luôn cả em bé không
    Nói đúng nghĩa đen thì giống logic của phản diện trong anime như Trigun: vì con người có thể gây ra tội ác kinh khủng nên hãy xóa sổ toàn bộ loài người
    Dù biết đây là cách nói nửa đùa nửa thật, nhưng vẫn thấy thú vị, và tôi định lấy nó làm chủ đề cho bài nói chuyện tiếp theo mình đang chuẩn bị
    • Đúng là tiêu đề rất kịch tính và câu view
      Nhưng tôi vẫn đồng cảm với ý chính. Bản thân tình huống này đã khá khôi hài rồi mà
  • Việc không thể xử lý đúng địa chỉ IPv6 phạm vi link-local trong URL là một sai lầm
    Tuy vậy, chuyện không xử lý đúng địa chỉ IPv6 link-local cũng không phải là hiếm
    • Ngược lại, để xử lý zone trong địa chỉ IPv6 bên trong URL thì độ phức tạp tăng lên khá nhiều
      Giờ đây % chỉ mang nghĩa khác trong phần host của URL, và cũng chỉ khi host là địa chỉ IPv6 nằm trong [...]
      Cú pháp có thể vẫn không hẳn là mơ hồ, nhưng đó không phải điểm chính. Càng nhiều trường hợp ngoại lệ như thế này thì URL parser càng dễ bỏ sót một ngoại lệ cụ thể, và từ khác biệt giữa các parser rất dễ nảy sinh những lỗi bẩn hoặc vấn đề bảo mật
      Cá nhân tôi thích phương án hỗ trợ IPv6 zone trong URL hơn, nhưng vì từng có hướng dẫn rằng % phải được URL-encode, nên nếu giờ đảo ngược lại thì đúng là sẽ tạo ra sự mơ hồ thực sự. Khá đáng tiếc
  • Nếu là thư viện hoặc triển khai tương thích với RFC 3986 thì sẽ gặp vấn đề này, vì đặc tả định nghĩa chuỗi percent-encoding như sau
    pct-encoded = "%" HEXDIG HEXDIG
    Ở đây HEXDIG được định nghĩa là [a-fA-F], nên %et sẽ bị parse thành một chuỗi không hợp lệ
    Đặc tả cũng nói rằng ký tự % được dùng làm dấu hiệu cho octet đã được percent-encode, nên nếu muốn dùng như dữ liệu trong URI thì phải percent-encode thành %25. Nếu decode lại một chuỗi đã được decode thì có thể hiểu nhầm octet dữ liệu phần trăm là phần mở đầu của một percent-encoding, và nếu encode lại một chuỗi đã được encode thì sẽ gặp vấn đề ngược lại, vì thế triển khai không được encode hoặc decode cùng một chuỗi quá một lần
    Vì vậy đúng là khá khó chịu, nhưng trên thực tế tôi không cho rằng đây có thể xem là bug
  • Tôi không hiểu vì sao việc dùng trực tiếp % trong địa chỉ có zone lại bị xem là khủng khiếp đến vậy. Ký tự đã encode vẫn được cho phép trong phần host, còn nếu để nguyên % trong địa chỉ zone thì sẽ tạo ra sự mơ hồ
    Bản thân % không phải là ký tự unreserved, nên percent-encode nó là đúng
    Việc net/urlnet/http của Go xung đột với URL RFC vốn chẳng có gì mới. Đặc biệt sự tồn tại của net/url.URL.Path và cách nó được dùng trong net/http khá phiền toái, vì nó làm hỏng %2F. net/http.Redirect cũng dùng path.Clean, nên còn gộp sai // thành /
    Có rất nhiều lý do để muốn fork phần xử lý URL trong thư viện chuẩn Go, hoặc đề xuất thứ gì đó như net/url/v2. Nhưng xét từ những gì bài viết này cho thấy, cách Go xử lý địa chỉ IPv6 có zone có vẻ là hợp lý và đúng đắn