就让我输入数字
摘要
一位用户讲述了瑞士AGOV政府登录系统验证码表单中一个令人沮丧的无障碍性错误,并反思了数字身份、年龄验证和在线匿名性等更广泛的问题。
暂无内容
查看缓存全文
缓存时间: 2026/07/14 10:15
# 就让我输入数字吧 | 博客 | Guillaume Endignoux
来源:https://gendx.dev/blog/2026/07/13/input-digits.html
数字身份及其对网络的意义,在过去几年里已成为热议话题。它们也引发了许多争议:年龄验证法及其对在线匿名性的影响(https://www.eff.org/deeplinks/2025/12/age-verification-coming-internet-we-built-you-resource-hub-fight-back),维基百科可能需要在英国验证其用户身份(https://wikimediafoundation.org/news/2025/09/12/wikimedia-foundation-challenges-uk-online-safety-act-regulations/),依赖官方 iOS 和 Android 操作系统作为数字身份钱包的强制形态(https://github.com/eu-digital-identity-wallet/eudi-app-android-wallet-ui/issues/287),更不用说当你因担任国际刑事法院法官而被取消美国数字账户时,这些相互依赖关系意味着什么(https://www.theguardian.com/law/2026/feb/18/international-criminal-court-icc-judges-trump-sanctions)。
我所经历的故事与瑞士政府登录系统 AGOV(https://www.agov.admin.ch/en)有关。该系统自 2024 年部署以来,现已拥有 160 万账户(https://www.agov.admin.ch/en/agov-statse-2026-04),并且日益成为享受失业保险、报税(强制活动!)以及许多其他交互的登录机制。在苏黎世州,这是申请公民身份的唯一入口(https://www.zh.ch/de/migration-integration/einbuergerung/ordentliche-einbuergerung.html)。
因此,我最终不得不创建一个 AGOV 账户。不幸的是,注册账户成了一项真正的挑战,直到我查明了一个奇怪的可访问性错误的根源。受到《就让我选择文本吧》(https://aartaka.me/select-text.html)的启发,现在轮到我来向大家展示《就让我输入数字吧》。
---
- AGOV 注册(https://gendx.dev/blog/2026/07/13/input-digits.html#agov-registration)
- 联系支持?(https://gendx.dev/blog/2026/07/13/input-digits.html#contacting-support)
- 四处询问(https://gendx.dev/blog/2026/07/13/input-digits.html#asking-around)
- 调查网页(https://gendx.dev/blog/2026/07/13/input-digits.html#investigating-the-web-page)
- 结论(https://gendx.dev/blog/2026/07/13/input-digits.html#conclusion)
- 事后分析(https://gendx.dev/blog/2026/07/13/input-digits.html#post-mortem)
## AGOV 注册
AGOV 注册流程(https://agov.ch/me)的第一步是提供你的邮箱地址,并输入验证码以验证该地址。我的问题是我连这个验证码都输入不了!
具体来说,输入验证码的表单被分成了 6 个框(每个数字一个)。第一个奇怪的地方是,每次我输入一个数字,焦点不会自动跳到下一个框。相反,当前框会积累更多数字,并以红色高亮显示,表示错误。
验证码表单截图,第一个框中有 2 个数字(https://gendx.dev/blog/images/input-digits/screenshot-digits-fr.zEFxM4-uwfRu.png)
起初,我以为这只是一个轻微的 UI 错误,我可以手动在输入每个数字后跳到下一个框。然而,这并不奏效,即使我仔细地每个框只填一个数字,页面最后总会显示“字段为必填”错误。真奇怪。
验证码表单截图,有 6 个数字但显示“字段为必填”错误(https://gendx.dev/blog/images/input-digits/screenshot-field-required-fr.fgJ6FXO-4IJX.png)
我尝试在开发者工具中查看 DOM,看是否有我可以手动调整的表单值,但没发现明显的异常。网页控制台中也没有记录任何错误信息。使用不同的邮箱地址,甚至是一个假地址(比如 `[email protected]`),也都没有任何区别。
考虑到我用的是 Linux 上的 Firefox 浏览器,我起初以为可能是这种组合不受支持,或者 CAPTCHA 验证失败了。这种情况不太可能——如今我几乎不会遇到专门在 Firefox 或 Linux 上出问题的网站——但也不是完全不可能。实际上,就在验证码表单出现之前,注册页面确实连到了 `eu-api.friendlycaptcha.eu`。而且,官方 AGOV 文档(https://help.agov.ch/index.php?c=regrequiremets&l=en)只提到 Windows 和 macOS 是支持的操作系统,尽管另一页关于安全密钥的说明(https://help.agov.ch/index.php?c=seckeys&l=en)提到 Linux 和 Firefox 也受支持。
为了排除这个可能性,我最终尝试了 6 种浏览器和操作系统的组合:Linux 上的 Firefox、Chromium 和 Chrome;macOS 上的 Firefox、Safari 和 Chrome。全都显示相同的错误!
这时,我觉得应该联系官方支持(https://help.agov.ch/index.php?c=contact&l=en)。AGOV 网站有很多帮助板块(https://help.agov.ch/index.php?c=agov&l=en),但不幸的是,这并没有帮到我多少,因为联系支持的唯一方式(https://help.agov.ch/index.php?c=contact1&l=en)是通过一个网页表单……而这个表单首先要向你的邮箱发送一个验证码!正是我无法通过的那个表单。
是的,FAQ 中明确提到(https://help.agov.ch/?c=agovfaq&q=2.3&l=en),该表单是联系支持的唯一方式(强调为原文所加)。
> 对参与机构的支持由二级支持在工作时间内**仅通过在线创建工单**提供。
版权页(https://help.agov.ch/index.php?c=impressum&l=en)也列出了瑞士联邦总理府的实体邮寄地址,但他们很可能不是直接开发该网站的人员。没有其他联系方式:没有邮箱,没有电话号码。另外,原则上可以通过写评论(https://help.agov.ch/index.php?c=contactrems&l=en)来“分享关于 AGOV 的反馈:赞扬、批评、请求”,但这又需要 AGOV 或同等账户才能登录。真正是先有鸡还是先有蛋的问题!
我还查看了 AGOV Access Android 应用(https://play.google.com/store/apps/details?id=ch.agov.accessapp&hl=en)的在线评论,该应用目前的评分为 1.4 星——这是我使用的所有应用中最差的。不幸的是,在 Google Play 商店的评论中,没有人提到与我类似的问题。有趣的是,有许多关于 GrapheneOS(https://grapheneos.org/)(一个基于 Android 的隐私安全移动操作系统)支持的评论,官方 FAQ 现在甚至有了关于它的条目(https://help.agov.ch/?c=agovfaq&q=2.9&l=en)(强调为原文所加)!
> AGOV Access 应用在 GrapheneOS 上无法运行,因为该应用的加固框架用于防止未经授权的篡改,但与 GrapheneOS 不兼容。**联邦总理府已委托供应商确保兼容性。**
听起来很有希望,但目前你只能使用第二因素设备进行注册/登录,这些设备要么是运行原始 iOS 或 Android 的智能手机,要么是安全密钥(好在有键盘选项!)。当然,是在通过邮箱验证之后。
## 四处询问
我并不急于创建 AGOV 账户,所以偶尔向一些朋友提过这个问题,但似乎没有人遇到过这个问题。
我还想到问一些同事,并在我工作用的笔记本电脑上尝试了注册页面,想截个图给他们看证据。令人惊讶的是,这次表单竟然成功了!自动填充会自动跳到下一个框,而且它确实检测到我输入了错误的验证码,而不是根本没输入。
验证码表单截图,有 6 个数字但显示“输入的验证码不正确”错误(https://gendx.dev/blog/images/input-digits/screenshot-incorrect-code-us.ki-RkPvXCl8b.png)这次显示的错误是“输入的验证码不正确”。
但显然,我不想长期依赖雇主提供的电脑与政府交互。即使我设法通过工作笔记本电脑注册了,以后用个人笔记本电脑登录时,总会有无法登录的风险。
不幸的是,我的同事中没有人遇到过这个错误……我还在一个朋友的电脑上试了,macOS 上的三种浏览器都能顺利接收验证码!
难道我真的这么倒霉,拥有世界上仅有的两台会让这个网站出问题的电脑吗?
## 调查网页
这时我已经没什么简单想法了,于是决定检查网页本身。令人惊讶的是,它加载的资源并不多,但主要的 JavaScript 文件有 2.4MB,展开后超过 10 万行半混淆/压缩的代码。这看起来相当棘手,因为某些部分乍一看非常混淆。
```
...
var Pc = o(3980),
eh = o(4572),
wa = o(9974),
El = o(2326),
Tp = o(6450),
th = o(1203),
Ld = o(9326);
function Yl(...M) {
const m = (0, Ld.ms)(M);
return m
? (0, th.F)(Yl(...M), (0, Tp.I)(m))
: (0, wa.N)((I, _) => {
(0, eh.P)([I, ...(0, El.K)(M)])(_);
});
}
...
```
所以我决定再等一等,看看有没有人能有什么想法。
> 根据 FAQ(https://help.agov.ch/?c=agovfaq&q=4.10&l=en),AGOV 系统的源代码将于 2026 年底发布,以满足法律要求。> 第 9 条 EMBAG(https://www.fedlex.admin.ch/eli/fga/2023/787/de)第 1 款规定:“受本法管辖的联邦当局,应公开其为履行任务而开发或委托开发的软件的源代码,除非第三方权利或安全相关原因阻止或限制此类公开。”2026 年 12 月,AGOV 的源代码将根据这一要求发布。这是代表 AGOV 项目目标实现的 AGOV 版本,即包含 AGOV e-ID 验证器的 AGOV。后续版本将在发布说明 www.agov.ch/rn(https://www.agov.ch/rn)中描述,发布后准备公开。如果能提前获得源代码,并且有地方可以直接向开发人员报告错误,那将有助于我的调查,但不幸的是,AGOV 在其源代码发布之前就已经是某些服务的强制登录系统了!
我终于回头继续研究,幸运的是,搜索“field required”发现了一些逻辑线索。
```
get errorMessage() {
return this.translateService.instant(
this.emailControl.errors?.required
? "GENERAL.text.fieldRequired"
: this.emailControl.errors?.maxlength?.actualLength > ht.Z.EMAIL_MAX_LENGTH
? "GENERAL.text.inputTooLong"
: "GENERAL.text.emailAddressError",
);
}
```
继续搜索“required”,我找到了这个邮箱验证函数,它把状态码设置为“REQUIRED”。
```
onEmailVerification() {
var _ = this;
return (0, ve.A)(function* () {
((_.showResendSuccessfulBanner = !1),
_.verificationCode
? _.verificationCode.length === _.verificationCodeLength
? (_.statusCode.set(""),
(_.isLoading = !0),
yield (0, Qt.s)(
_.registrationService.emailVerification(_.verificationCode, _.userEmail),
)
.then((S) => {
"VALID" === S.status
? _.onEmailVerificationSuccess()
: ((_.showCodeValidation = !0), _.statusCode.set(S.status ?? "WRONG"));
})
.catch(() => {
(_.statusCode.set("WRONG"), (_.showCodeValidation = !0));
}),
(_.isLoading = !1))
: (_.statusCode.set("WRONG"), (_.showCodeValidation = !0))
: (_.statusCode.set("REQUIRED"), (_.showCodeValidation = !0)),
_.changeDetectorRef.detectChanges());
})();
}
```
最终,我找到了下面这个函数,它调用了邮箱验证的回调。
此时,错误对我来说变得清晰了!你发现了吗?
```
verificationCodeEntered(_, S) {
if ("Shift" === this.lastKeyPressed) return;
switch (S.key) {
case "Control":
case "Meta":
case "Shift":
case "v":
case "r":
return;
case "Enter":
return (S.preventDefault(), void this.emailVerificationCallback());
case "Backspace":
return (this.setCode(_, "", "backward"), void S.preventDefault());
case "ArrowLeft":
case "ArrowDown":
return void this.goBackward(_);
case "ArrowUp":
case "ArrowRight":
case "Tab":
return void (_ !== this.codeLength - 1 && (this.goForward(_), S.preventDefault()));
}
S.preventDefault();
const O = Number(S.key);
isNaN(O) ? (this.getInput(_).value = "") : this.setCode(_, String(O));
}
```
看来这个函数拦截了按键事件,并将验证码收集到一个局部 JavaScript 变量中。所以事后不可能通过修改 DOM 中的输入表单来改变验证码,必须找到那个 JavaScript 变量并直接注入!
关键部分在于修饰键被拒绝。
而我的问题是……我用的是一款 AZERTY(https://en.wikipedia.org/wiki/AZERTY)标准法语键盘布局,在这种布局中,数字只能通过按住 Shift 键来输入。换句话说:屏蔽 Shift 键就等于屏蔽了数字。
AZERTY 键盘布局(https://gendx.dev/blog/images/input-digits/AZERTY.2MiSgW6cY72Z.svg)
来源:https://en.wikipedia.org/wiki/File:KB_France.svg,许可证:CC BY-SA 3.0(https://creativecommons.org/licenses/by-sa/3.0/deed.en)。
事实证明,AZERTY 是世界上少数几种(https://en.wikipedia.org/wiki/Keyboard_layout)需要按住 Shift 键才能输入数字的键盘布局之一。在工作中我使用的是英语 QWERTY(https://en.wikipedia.org/wiki/QWERTY)键盘布局,而我询问过的人都不是法国人,所以在测试的电脑中,显然只有我那两台“坏掉”的笔记本电脑会受影响。
QWERTY 键盘布局(https://gendx.dev/blog/images/input-digits/QWERTY.3_pfJz4KTSb5.svg)
来源:https://en.wikipedia.org/wiki/File:KB_United_States.svg,许可证:CC BY-SA 3.0(https://creativecommons.org/licenses/by-sa/3.0/deed.en)。
在我的操作系统中切换键盘布局,确实修复了我那两台“坏掉”笔记本电脑上的问题;反之,在那些“正常工作”的设备上切换成法语键盘布局,也触发了该错误。
此时你可能会好奇,一款瑞士应用怎么会在法语键盘上出问题:法语难道不是瑞士的官方语言之一吗?答案是,标准的瑞士法语键盘布局属于 QWERTZ(https://en.wikipedia.org/wiki/QWERTZ)系列(常见于中欧),以便于同时高效输入法语和德语。当然,在这种布局下,输入数字不需要按住 Shift 键。
QWERTZ 键盘布局(https://gendx.dev/blog/images/input-digits/QWERTZ.MJ3ZjoG1_3PK.svg)
来源:https://en.wikipedia.org/wiki/File:KB_Swiss.svg,许可证:GFDL 1.2(https://commons.wikimedia.org/wiki/Commons:GNU_Free_Documentation_License,_version_1.2)。
尽管如此,根据瑞士联邦统计局(https://www.bfs.admin.ch/bfs/en/home/statistics/population/migration-integration/foreign/composition.html)的数据,瑞士约有 17.1 万法国国籍居民,更不用说那些可能与瑞士政府打交道的跨境工作者了。因此从统计上看,我很惊讶这个错误竟然没有被人注意到并修复!
## 结论
在切换键盘布局后,我终于能够验证我的邮箱地址,并成功注册了一个 AGOW 账户。
相似文章
让我输入数字就好
一篇博客文章详细介绍了瑞士AGOV登录系统中的一个无障碍性漏洞,该漏洞导致数字输入失败,并揭示了数字身份系统中更广泛的问题。
欧盟年龄验证有什么问题?(没问题)
一篇博客文章认为,对欧盟网络年龄验证方法的批评往往缺乏根据,解释了为何对儿童进行年龄限制是必要的,并提出了一种使用签名认证而非完全身份披露的隐私保护方法。
Web 安全太难了
Eric Lawrence 批评了 Cloudflare 新的 Wallet 注册流程,指出它与同意式钓鱼攻击极为相似,说明了为什么 Web 安全如此困难。
布鲁塞尔推出年龄核验App,黑客仅用2分钟即成功攻破
布鲁塞尔推出了用于在线场景的年龄核验应用,但安全研究人员仅用2分钟便成功绕过了其安全防护机制,这引发了对该类政府强制要求的年龄验证系统实际有效性的严重担忧。
互联网的‘请出示证件’时代将摧毁你的隐私
一篇评论文章指出,强制性的年龄验证法律(例如澳大利亚禁止16岁以下青少年使用社交媒体的规定)通过迫使用户向第三方提供身份证等敏感数据来威胁隐私,而该法律本身也被证明无效。