HTTP Basic Auth 用户名中的 UTF-8 字符损坏

Posted

技术标签:

【中文标题】HTTP Basic Auth 用户名中的 UTF-8 字符损坏【英文标题】:UTF-8 characters mangled in HTTP Basic Auth username 【发布时间】:2010-10-16 16:50:16 【问题描述】:

我正在尝试使用 Ruby on Rails 构建 Web 服务。用户通过 HTTP Basic Auth 进行身份验证。我想在用户名和密码中允许任何有效的 UTF-8 字符。

问题是浏览器在将基本身份验证凭据中的字符发送到我的服务之前会对其进行处理。为了进行测试,我使用“カタカナカタカナカタカナカタカナカタカナカカカナカタカナカタカナカタカナ”作为我的用户名(不知道这是什么意思 - 如果它是冒犯的家伙,请原谅我)。 /p>

如果我把它看作一个字符串和做username.unpack(“H *”)将其转换为十六进制,我得到:“3e28ba3e28fb3e28ba3e38a83e28ba3e28fb3e28ba3e38a83e28ba3e28fb3e28ba3e38a83e28ba3e28fb3e28ba3e38a83e28ba3e28fb3e28ba3e38a83e28ba3e28fb3e28ba3e38a83e28ba3e28fb3e28ba3e38a83e28ba3e28fb3e28ba3e38a8”这似乎大约为右32个汉字字符(每3字节/ 6个十六进制数字) .

如果我对通过 HTTP 基本身份验证传入的用户名执行相同操作,我会得到: 'bafbbaacbafbbaacbafbbaacbafbbaacbafbbaacbafbbaacbafbbaacbafbbaac'。显然要短得多。使用 Firefox Live HTTP Headers 插件,这是发送的实际标头:

Authorization: Basic q7+ryqu/q8qrv6vKq7+ryqu/q8qrv6vKq7+ryqu/q8o6q7+ryqu/q8qrv6vKq7+ryqu/q8qrv6vKq7+ryqu/q8o=

这看起来像'bafbba ...'字符串,高半字节和低半字节交换(至少当我将它粘贴到 Emacs 时,base 64 解码,然后切换到 hexl 模式)。这可能是用户名的 UTF16 表示形式,但除了乱码之外,我没有得到任何东西来显示它。

Rails 将 content-type 标头设置为 UTF-8,因此浏览器应该以该编码发送。我得到了正确的表单提交数据。

问题发生在 Firefox 3.0.8 和 IE 7 中。

那么...是否有一些神奇的方法可以让 Web 浏览器通过 HTTP Basic Auth 发送 UTF-8 字符?我在接收端处理错误吗? HTTP Basic Auth 是否不适用于非 ASCII 字符?

【问题讨论】:

琐事:“随机字符”并不令人反感。他们是日本人,在片假名脚本en.wikipedia.org/wiki/Katakana 中说“片假名”(8 次),这通常用于拼写非日语单词和声音。 (这很奇怪,因为“片假名”一个日语单词,所以通常不拼成片假名:-) 琐事附录:我看过很多用片假名写的。最初我把它归结为那些试图写诗的人,但我只是在 Jisho 中查找它,它说它“通常是用假名写的”。 【参考方案1】:

这是我们今天用来规避同事密码中非 ascii 字符问题的解决方法:

curl -u "USERNAME:`echo -n 'PASSWORT' | iconv -f ISO-8859-1 -t UTF-8`" 'URL'

USERNAMEPASSWORDURL 替换为您的值。此示例使用shell command substitution 将密码字符编码转换为UTF-8,然后再执行curl 命令。

注意:我在这里使用` ... ` 评估而不是$ ... ,因为如果密码包含! 字符,它不会失败... [shell 喜欢! 字符;-)]

non-ASCII 字符会发生什么的说明:

echo -n 'zz<zz§zz$zz-zzäzzözzüzzßzz' | iconv -f ISO-8859-1 -t UTF-8

【讨论】:

【参考方案2】:

如果您正在为 Windows 8.1 编码,请注意HttpCredentialsHeaderValue 文档中的示例(错误地)使用 UTF-16 编码。相当好的解决方法是切换到 UTF-8(因为CryptographicBuffer.ConvertStringToBinary 不支持 ISO-8859-1)。

见http://msdn.microsoft.com/en-us/library/windows/apps/windows.web.http.headers.httpcredentialsheadervalue.aspx。

【讨论】:

【参考方案3】:

我可能完全无知,但我是在寻找一个问题时发现这篇文章的,同时在 ajax 调用中发送一个 UTF8 字符串作为标头。

我可以通过在发送字符串之前在 Base64 中编码来解决我的问题。这意味着您可以在提交之前使用一些简单的 JS 将表单转换为 base64,这样就可以将其转换回服务器端。

这个简单的工具让我可以将 utf8 字符串作为简单的 ASCII 发送。我发现这要归功于这个简单的句子

base64(此编码旨在使二进制数据能够通过非 8 位干净的传输层传输)。 http://www.webtoolkit.info/javascript-base64.html

我希望这会有所帮助。只是想回馈社区一点点!

【讨论】:

【参考方案4】:

HTTP Digest 身份验证也不能解决这个问题。它遇到了同样的问题,即客户端无法告诉服务器它正在使用什么字符集,并且服务器无法正确假设客户端使用了什么。

【讨论】:

【参考方案5】:

我想在用户名和密码中允许任何有效的 UTF-8 字符。

放弃所有希望。基本身份验证和 Unicode 不能混用。

对于如何在 base64 化之前将非 ASCII 字符编码为基本身份验证用户名:密码令牌,没有标准 (*)。因此,每个浏览器都会做一些不同的事情:

Opera 使用 UTF-8; IE 使用系统的默认代码页(您无法知道,除了它从来不是 UTF-8),并使用 Windows 默默地破坏不适合它的字符“猜测一个看起来像有点像你想要的,也可能不是'秘方; Mozilla 仅使用字符代码点的低字节,这具有编码为 ISO-8859-1 的效果,并且不可挽回地破坏非 8859-1 字符... except 在执行 XMLHttpRequests 时,在这种情况下,它使用 UTF-8; Safari 和 Chrome 编码为 ISO-8859-1,使用非 8859-1 字符时根本无法发送授权标头。

*:有些人将标准解释为:

它应始终为 ISO-8859-1,因为它是包含直接包含在标头中的原始 8 位字符的默认编码; 应该以某种方式使用 RFC2047 规则对其进行编码。

但是这些提议都不是包含在 base64 编码的身份验证令牌中的主题,并且 HTTP 规范中的 RFC2047 参考确实根本不起作用,因为它可能被使用的所有地方都被明确禁止RFC2047 本身的“原子上下文”规则,即使 HTTP 标头遵守 RFC822 家族的规则和扩展,但它们不这样做。

总之:呃。几乎没有希望在标准或 Opera 以外的浏览器中修复此问题。这只是促使人们远离 HTTP 基本身份验证转而采用非标准和不易访问的基于 cookie 的身份验证方案的另一个因素。真的很丢脸。

【讨论】:

我碰巧不同意 Opera 在某种程度上是正确的。您不能单方面更改编码。 与其说是“正确”,不如说是“OP 想要它做什么”。尽管没有一个替代方案是“正确的”,但 UTF-8 至少与任何其他可能的选项一样好。 至少 UTF-8 不会破坏某些字符 :) 非常感谢这个答案(它扩展了 Julian 的答案——他们都很好地回答了这个问题)。我做了很多谷歌搜索,但找不到一个可靠的讨论。是时候改变我的规格了。 有一个新的希望:新的RFC 7617 允许服务器请求UTF-8 编码,解决了歧义。然后,合规的客户将做出相应的响应。 – 当然,这并不意味着所有客户端软件都会立即实施 RFC 7617;这个问题可能需要数年时间才能被称为“基本解决” @chirlu:确实!我们要感谢朱利安。交叉手指现在实施......【参考方案6】:

Basic 身份验证不支持非 ISO-8859-1 字符是一个众所周知的缺点。

众所周知,一些 UA 使用 UTF-8(想到 Opera),但也没有互操作性。

据我所知,没有办法解决这个问题,除非定义一个新的身份验证方案来处理所有 Unicode。并部署它。

【讨论】:

【参考方案7】:

您是否使用curl 之类的方法进行了测试,以确保这不是 Firefox 问题? HTTP Auth RFC 对 ASCII 与非 ASCII 保持沉默,但它确实表示在标头中传递的值是用户名和用冒号分隔的密码,我在 Firefox 报告的字符串中找不到冒号发送。

【讨论】:

那里有一个冒号,一旦你对它进行base64解码。它最终是 32 个 16 位字符(至少 Emacs 认为它​​们是字符)、冒号,然后是相同的 16 位字符(我使用相同的字符串作为密码)。我用 IE 试了一下,得到了同样的结果,所以它不仅仅是一个 Firefox 的东西。 我只是使用一些 OS X 仪表板小部件进行转换,但在 base64 解码后肯定没有找到冒号。它一定是在尝试使用 MacRoman 什么的。

以上是关于HTTP Basic Auth 用户名中的 UTF-8 字符损坏的主要内容,如果未能解决你的问题,请参考以下文章

nginx配置指令auth_basic、auth_basic_user_file及相关知识

Flutter HTTP 请求使用 Basic Auth + 传递用户和密码来接收用户数据

Apache HTTP Basic Auth - 提供用户名和密码后出现 401 错误

Nginx实现基于用户的访问控制(Ngx_http_auth_basic_module模块)

Nginx实现基于用户的访问控制(Ngx_http_auth_basic_module模块)

shell之批量新增用户脚本(http-basic-auth)