Title Text:My password is just every Unicode codepoint concatenated into a single UTF-8 string.
Origin:https://xkcd.com/2700/
https://www.explainxkcd.com/wiki/index.php/2700:_Account_Problems
账户难题
http://xkcd.in/comic?lg=cn&id=2700
Cueball请求Ponytail帮助他,因为他无法登录他的帐户。Ponytail 过去曾尝试解决Cueball 的技术问题,但他的回答却是恐惧。Cueball 承诺“这次是正常问题”,Ponytail 同意查看。但随后 Cueball 透露,他在创建帐户时在密码中包含了一个空字符串终止符,现在他无法登录。
在计算机系统中,每个“字符”(字母、数字、标点符号等)都表示为一个整数。例如,小写字母“a”表示为数字 97,数字“1”表示为数字 49(使用ASCII字符编码或Unicode字符编码时)。“字符串”是指一系列字符,可用于存储任意文本(例如名称、消息、密码)。字符串可以任意长,因此必须使用某种机制来记录它们的长度。一种方法是显式存储长度(Pascal 字符串)。另一种方法是使用特定字符(通常是空字符)来标记字符串的结尾(用数字 0 表示);这样的字符串称为空终止字符串,由C 编程语言使用。两种方法都有优点和缺点。以 null 结尾的字符串的一个限制是它们不能用于表示包含嵌入的空字符的文本。这通常不是问题,因为普通文本从不包含空字符。但是,如果空字符以某种方式在字符串中结束,则会导致问题:使用该字符串的任何代码都会假定此空字符标记字符串的结尾,因此字符串将被有效地截断。
账户注册系统通常对密码提出要求,试图鼓励用户选择更强的密码。例如,他们可能会要求密码至少包含一个“特殊字符”(例如!@#$%^&*
)。Cueball 将此要求误解为指的是诸如空字符(更准确地称为控制字符)之类的字符。Cueball 设法以某种方式键入空字符作为其密码的一部分(在某些系统上,可以使用某些键盘快捷键(例如Ctrl
+ Space
、Ctrl
+ @
、Ctrl
+2
或Alt+0
使用数字键盘来键入空字符)), 但运行注册系统的软件写得不好,无法解决这个问题——它允许他用那个密码创建一个帐户,但是当他试图用同样的密码登录时,系统不接受。
目前还不清楚这种特殊情况在实际软件中是如何出现的,但有一个类似的情况在实践中很容易发生:假设一个网站的注册表单允许用户的新密码最多有 20 个字符,但由于程序员的错误登录页面只接受最多 18 个字符的密码。如果用户选择一个中等长度的密码(比如 12 个字符),一切都很好。但是,如果用户选择一个包含 20 个字符的密码,他们将能够注册但无法登录(Cueball 就是这种情况)。下面描述了一些额外的情况。
标题文本描述了一个密码,它“只是”将每个 Unicode 字符连接成一个字符串。Unicode是表示许多书写系统字符的标准,在本漫画出版时它有 149,186 个字符[1](随着时间的推移会添加新字符)。包含所有这些字符的密码会非常长;手动输入是不切实际的,而且对于几乎所有的帐户注册系统来说都太长了。(“代码点”是分配给字符的编号,UTF-8是一种通用编码系统,用于将每个 Unicode 代码点表示为字节序列.) 此外,由于 Unicode 包含空字符,因此密码与 Cueball 的密码存在相同的问题。此外,如果帐户注册系统将空字符视为字符串终止符(如在 C 中),则密码将等同于空密码(假设它按顺序包含 Unicode 代码点,从空字符开始)。
琐事[编辑]
- 包含不安全字符的用户输入先前曾出现在著名漫画327:妈妈的功绩中。
- 以下是带有特殊字符的密码可能会停止工作的其他一些情况:
- 注册表单允许密码包含空字符,但登录表单会删除空字符(例如,因为它是由不同的开发人员/团队编写的,或者因为它已随时间更新)。当 Cueball 尝试登录时,登录表单会删除空字符,因此生成的密码永远不会与此类存储的密码(包含空字符)匹配。
- 密码系统最初接受 Unicode 字符,但后来更改为只接受 ASCII 密码。在密码中包含 é 或 ö 等非 ASCII 字符的用户将被锁定在他们的帐户之外,因为他们不再被允许提交这些字符。
- 包含非 ASCII 字符的密码通常会有问题,因为可能无法在用于登录的键盘上键入它们。例如,在 Mac OS 上,登录用户可以将其密码更改为包含表情符号的密码,但是登录屏幕上的键盘不支持输入表情符号。[2] [3]。
- 一个商业网络可能有多个系统连接到一个包含用户名和密码的中央数据库。如果系统有不同的密码处理规则,用户可能会发现某些系统不支持他们的密码(例如,因为密码包含在特定系统上被禁止的字符)。
- 有几种技术可用于安全地处理可能包含不安全字符(例如空字符)的密码和其他用户输入:
- 验证:检查用户输入是否包含不安全字符,如果包含则向用户显示错误消息。
- 清理:从用户输入中删除不安全的字符,以防止它们引起问题。
- 编码/引用/转义:用适当的字符序列替换每个不安全字符(取决于上下文)。例如,可以通过将空字符编码为
%00
. 此技术与密码处理不是很相关,但例如在生成的网页中包含用户输入或将用户输入传递给数据库查询时是相关的。 - 对于空字符的特定情况:使用支持空字符的字符串表示(例如 Pascal 字符串),并且非常小心不要将此类字符串传递给无法处理嵌入空字符的函数。
- 在 C 语言中,字符串通常存储在分配给已知大小的内存块中。可以存储在此类缓冲区中的字符串的最大大小比缓冲区的大小小一个字符,因为最后一个字符用于空终止符。对字符串进行操作的语言函数,例如那些返回指定字符串长度或比较两个字符串的函数,寻找终止符作为标记。但是,使用此功能存在风险:如果该终止符以某种方式被其他值覆盖,则假定仍然存在停止点的函数可能会在碰巧找到不相关的终止符之前远远超出预期的内存区域或以其他方式被迫停止寻找。这可能会产生严重的安全隐患,并可能导致错误和崩溃。反而,安全编程使用包含最大允许长度规范的字符串函数版本。例如,
strlen()
函数接受一个指向字符串的指针,计算字符数,直到遇到空终止符,然后返回该数字:不包括终止符的字符串长度。该函数采用指向字符串的指针和最大长度,并对字符进行计数,直到找到终止符或达到最大值。strnlen()
- xkcd 漫画的编号是 2700。当将其解释为两个串联的八进制数 \27 + \00 时,它代表ETB和空字符,这两个字符在遗留系统(例如大型机)中处理时可能会导致问题电脑)。当将 2700 解释为十六进制 0x27 + 0x00 数字时,它代表 ‘ 字符和空字符 -当它以非转义形式放置在 SQL 命令中时可能导致SQL 注入的序列。