HTTP基本身份验证应该使用哪种编码?

95

RFC2617规定将用户名和密码编码为base64,但未指明创建输入到base64算法的八位字节时使用哪种字符编码。

我应该假设是US-ASCII还是UTF8?或者已经有人在某处解决了这个问题吗?


2
相关:HTTP头应该使用什么字符编码? - Hawkeye Parker
4个回答

89

原始规范 - RFC 2617

RFC 2617 可以被解读为“ISO-8859-1”或“未定义”。由你决定。众所周知,许多服务器使用 ISO-8859-1(不管你喜欢与否),并且在发送其他内容时会失败。因此,唯一安全的选择可能是坚持使用 ASCII。

有关更多信息和修复该情况的提案,请参见草案"HTTP基本身份验证的编码参数"(这是 RFC 7617 的基础)。

新规范 - RFC 7617

自2015年以来,RFC 7617已经取代了RFC 2617。与旧的RFC相比,新的RFC明确定义了用于用户名和密码的字符编码。

  • 默认编码仍未定义。它只需要与US-ASCII兼容(意味着它将ASCII字节映射到ASCII字节,就像UTF-8一样)。
  • 服务器可以在其挑战中可选地发送附加的身份验证参数charset="UTF-8",如下所示:
    WWW-Authenticate: Basic realm="myChosenRealm", charset="UTF-8"
    这表明服务器将接受用户名/密码中的非ASCII字符,并且期望它们以UTF-8编码(具体来说是规范化形式C)。请注意,只允许使用UTF-8。

完整版本:

阅读规范。它包含了其他细节,例如确切的编码过程和应支持的Unicode代码点列表。

浏览器支持

截至2018年,现代浏览器通常会默认使用UTF-8编码,即使服务器不使用charset参数,用户输入非ASCII字符作为用户名或密码时也是如此。

  • Chrome 浏览器也似乎使用UTF-8编码
  • Internet Explorer 不使用UTF-8 (问题 #11879588)
  • Firefox 正在进行一项更改的实验,计划在v59版本中推出 (bug 1419658)

Realm

即使在RFC 7617中,realm参数仍然只支持ASCII字符。


1
你的答案必须是最好的。我可以将其转换为ASCII,如果你很幸运,可能是ISO-8859-1。 - Dobes Vandermeer
RFC 7617指出:“‘realm’参数携带的数据可以被视为文本;然而,[RFC7235]没有定义一种可靠地传输非美国ASCII字符的方式。这是一个已知的问题,在修订该规范时需要解决。” 但是realm是一个quoted-string,并且RFC 7230中定义的quoted-string允许使用高达0xFF的八位字节,因此人们认为UTF-8可以被使用。 - Remy Lebeau
@RemyLebeau - 不,引号字符串中的非ASCII字符是不被鼓励的,并且没有约定的字符编码。 - Julian Reschke
@JulianReschke 是的,我意识到了。 - Remy Lebeau
1
我在阅读这个答案时发现了一个关于RFC 7617的矛盾之处:它说“新的RFC明确定义了用于用户名和密码的字符编码”,但接着又说“默认编码仍未定义”-这意味着它并没有被明确定义... - Dai
显示剩余5条评论

40
简短回答:除非按照RFC2047(MIME)的规定使用编码单词,否则应该使用iso-8859-1。
更长的解释: RFC2617,第2节(HTTP身份验证)定义了基本凭据
basic-credentials = base64-user-pass
base64-user-pass  = <base64 encoding of user-pass, 
                     except not limited to 76 char/line>
user-pass         = userid ":" password
userid            = *<TEXT excluding ":">
password          = *TEXT

阅读本规范时应参考RFC2616(HTTP 1.1)中的BNF定义(如上所述):

本规范是HTTP/1.1规范2的附属规范。 它使用该文档的增强型BNF第2.1节,并依赖于 该文档中定义的非终端符号和HTTP/1.1规范的其他方面。

RFC2616,第2.1节定义了TEXT(我强调):

TEXT规则仅用于描述性字段内容和值, 这些内容和值不打算由消息解析器解释。* TEXT的单词 当按照RFC 2047的规则进行编码时,可以包含来自字符集的字符 ISO-8859-1以外的字符集。

TEXT           = <any OCTET except CTLs, but including LWS>

所以,除非您按照RFC2047(MIME第3部分)规则检测到其他编码方式,否则它肯定是iso-8859-1。
// Username: Mike
// Password T€ST
Mike:=?iso-8859-15?q?T€ST?=

在这种情况下,单词中的欧元符号将根据iso-8859-15编码为0xA4。我的理解是,您应该检查这些编码的字词分隔符,然后根据指定的编码对内部字词进行解码。如果不这样做,您将认为密码是=?iso-8859-15?q?T¤ST?=(请注意,当作为iso-8859-1解释时,0xA4将被解码为¤)。
这是我的理解,我找不到比这些RFC更明确的确认。其中一些似乎相互矛盾。例如,RFC2047(MIME,pt. 3)的四个声明目标之一是重新定义:
引用:

消息格式以允许使用字符集其他于US-ASCII的文本标题信息。

但是,RFC2616(HTTP 1.1)使用TEXT规则定义标题,默认为iso-8859-1。那么这意味着此标题中的每个单词都应该是一个编码单词(即=?...?=形式)吗?

同样相关的是,目前没有任何浏览器可以做到这一点。它们使用utf-8(Chrome,Opera),iso-8859-1(Safari),系统代码页(IE)或其他一些编码方式(例如Firefox只使用utf-8的最高有效位)。

编辑:我刚意识到这个答案更多地从服务器端的角度来看待这个问题。


RFC 2047 编码在这种情况下不适用。 - Julian Reschke
@JulianReschke 嗯,规范明确指出“只有按照RFC 2047的规则进行编码时才能这样做”。我知道RFC2047中的规则可能不适用于HTTP头,但规范在提到它时非常清楚。我已经补充了事实,即没有浏览器实际执行此操作。 - Michiel van Oosterhout
4
HTTPbis规范将不再提及RFC 2047。 - Julian Reschke
非常详细的写作,感谢@MichielvanOosterhout! - ToastyMallows
RFC 7617更新了“user-id”和“password”的定义。它不再允许其中包含“LWS”(线性空格)。在其中,所有控制字符都是被禁止的。“user-id”和“password”中“不能包含任何控制字符(请参见[RFC5234]附录B.1中的‘CTL’)。” - Константин Ван

4
如果您对浏览器在登录提示中输入非ASCII字符时的操作感兴趣,我刚试了一下Firefox。
它似乎会将所有内容懒惰地转换为ISO-8859-1,方法是取每个Unicode值的最低有效字节,例如:
User: 豚 (\u8c5a)
Password: 虎 (\u864e)

编码与以下相同:

User: Z (\u005a)
Password: N (\u004e)

将0x5a 0x3a 0x4e转换为base64编码后为WjpO。


2
是的,这是 Firefox 的旧行为。它已经被更改(似乎是在 V57 中),现在使用 UTF-8。 - sleske
2
V59,不是V57。目前正在进行测试版。 - Julian Reschke
我尝试过V59和V60,但对我没有起作用。我还添加了“charset=utf-8”。 - Rohit Gaikwad

4
除了RFC之外,在Spring框架中,默认的BasicAuthenticationFilter类使用的编码是UTF-8
我认为选择这种编码的原因是UTF-8能够编码所有可能的字符,而ISO-8859-1(或ASCII)不能。尝试使用系统不支持的用户名和密码可能会导致错误行为或(更糟糕的是)降低安全性。

1
使用UTF-8并不能解决对方不知道它的问题。因此,如果Spring框架实现了https://greenbytes.de/tech/webdav/rfc7617.html#rfc.section.2.1中描述的字符集参数,那就太好了。 - Julian Reschke
1
@JulianReschke 我告诉了你它是如何在最常见的框架中实现的,以及可能的原因。不要攻击信使! - holmis83

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接