你在笔记本电脑上打开一家银行的网站。
点击“使用 Singpass 登录”。
页面上出现一个二维码。
你打开 Singpass 应用,扫描二维码,核对服务名称,然后确认登录。网站随即知道可以让你登录了。
在用户眼里,整个过程简单得近乎不可思议:网页显示二维码,手机完成确认,浏览器接着登录。
但真正关键的问题是:
这个二维码究竟证明了什么?
答案不是“二维码里装着我的身份信息”。
设计得当的二维码登录,恰恰不该如此。二维码里不应包含你的 NRIC、姓名或个人资料,也不应放入长期有效的登录令牌。它只应是一份短期有效的邀请,请你完成一笔特定的身份验证事务。
用系统设计的语言来说:
二维码登录,不是把身份装进二维码。
它发起的是一项短期有效的挑战,
由可信的移动端凭据批准,
再交给网页端的登录会话使用。
这正是整个设计的核心。

这就是大家熟悉的表面体验:扫描或轻点二维码,就能登录。真正重要的是,这个二维码被允许代表什么、它会多快过期,以及手机上的批准最终会绑定到哪个浏览器会话。
二维码只是一道入口 链接到标题
第一个常见误区,是把二维码本身当成秘密。
人们很容易以为,二维码携带着某种神奇的身份数据。毕竟,用户一扫码,浏览器就登录成功了。但如果仅凭二维码就能登录,那么任何看到它的人都可以复制、转发,甚至反复使用。
这样的设计非常危险。
更稳妥的做法,是让二维码只包含一个不透明的挑战标识:
qr_challenge_id = "看似随机、短期有效的标识符"
这个标识符本身没有多少意义。它指向服务器端的一条状态记录,而这条记录是在浏览器发起登录请求时创建的。
服务器掌握的信息包括:
- 哪个依赖方请求了此次登录
- 哪个浏览器会话正在等待
- 请求涉及哪些权限或操作
- 挑战何时过期
- 挑战是否已经被扫描、批准、拒绝、使用,或已经过期
二维码只是让移动应用向系统表明:
我正在回应这一次特定的登录尝试。
因此,二维码登录不能只做“生成二维码”这一步,后台还必须维护一个生命周期很短的状态机。
屏幕上的流程看似简单,底层却更接近这样:
已创建 -> 已扫描 -> 已批准 -> 已使用
\-> 已过期
\-> 已拒绝
\-> 已取消
状态机之所以重要,是因为登录不是一个静止的页面,而是一笔横跨两台设备的交互事务。系统必须拒绝不安全的状态变化,例如:
已过期 -> 已批准
已使用 -> 已批准
已批准 -> 再次批准
重试、网络延迟或重复点击,都可能触发两次批准请求。但系统不应因此建立两个浏览器会话,也不应签发两个授权码。只有第一次有效批准可以生效。
这也解释了为什么挑战必须很快过期。危险不在于二维码包含你的身份——设计得当的二维码本来就不应包含这些信息。真正的风险在于,它指向一笔确实存在、仍在等待批准的登录事务。
例如,攻击者可以先在自己的浏览器中发起登录,复制二维码,再通过另一段对话或另一个网页把它展示给受害者。如果受害者扫码后没有核对服务名称和上下文就直接批准,最终获准登录的可能不是受害者原本想打开的会话,而是攻击者的浏览器会话。
缩短有效期,可以压缩攻击者中继挑战或制造上下文混淆的时间窗口。设计良好的挑战应当短期有效、只能使用一次、与最初的登录事务绑定,并在批准、拒绝、过期或使用后立即失效。
浏览器在等待。
手机在批准。
服务器要判断:这两件事是否属于同一次身份验证。
为什么手机如此重要 链接到标题
第二个常见误区,是以为手机只负责转发二维码。
移动应用的作用远不止于此。用户已经在这里建立了可信的 Singpass 应用凭据。
Singpass 的公开资料把 Singpass Login 描述为一种登录方式:居民可以通过 Singpass 应用登录数字服务,无需输入密码。这里真正重要的设计理念是,应用不只是一个更方便的界面,它本身也是身份验证器。
应用扫描二维码后,安全的设计不应把完整的用户身份记录发送到后台,而只需提交一项精简请求,例如:
challenge_id
device_or_credential_id
timestamp
signature_over_the_challenge_context
其中,device_or_credential_id 只是帮助后台查找记录的线索。后台仍须读取已保存的凭据元数据,确认这台设备有效且处于启用状态,并且确实关联着正确的账户。
真正关键的证明,是签名。
移动设备可以使用与应用凭据关联的密钥,为挑战上下文签名。服务器再用已保存的公钥验证签名。因此,服务器信任的不是二维码本身,而是这样一个事实:某个有效且已经注册的身份验证器,批准了这一项确切的挑战。
说得更直白一些:
手机不是在告诉浏览器“我是 Alice”。
手机是在向 Singpass 证明:
一份有效的 Singpass 应用凭据批准了这次登录请求。
这样理解,才更接近系统真正建立起来的信任关系。
用户必须看清自己批准的内容 链接到标题
二维码登录不该在用户毫无察觉的情况下完成。
批准之前,应用应明确显示这项请求的内容。至少要让用户看到服务名称,以及足以辨认此次请求的上下文。
这个看似细微的交互设计,其实也是安全模型的一部分。
如果应用只显示一个笼统的“批准登录”按钮,用户就可能批准错误的请求。假如攻击者诱骗用户扫描了另一个网站的二维码,用户必须有机会发现服务名称或上下文不对。
所以,批准页面不是装饰。人的真实意图,正是在这里进入协议流程。
设计良好的批准页面应当回答:
- 是谁在发出请求?
- 我正在批准什么操作?
- 这是普通登录,还是风险更高的交易?
- 它与我刚才在浏览器中的操作一致吗?
系统可以验证密钥和令牌,但只有用户本人能确认自己的意图。
因此,严肃的身份验证系统既需要密码学检查,也需要清楚明确的人工确认。
浏览器如何得知结果 链接到标题
用户在手机上批准后,浏览器还要接着完成余下的流程。
实现方式有好几种。浏览器可以订阅 WebSocket 或 Server-Sent Events 通道,也可以每隔几秒向后台轮询一次。具体技术虽有不同,基本逻辑却一致:
- 浏览器发起一笔登录事务。
- 后台创建二维码挑战。
- 浏览器等待状态更新。
- 移动应用扫码并批准。
- 后台将挑战标记为已批准。
- 浏览器收到成功事件。
对于第一方网站,后台可以创建浏览器会话。
如果用户登录的是银行、ActiveSG 等获准接入的依赖方,验证结果通常会进入 OpenID Connect 流程。在这层关系中,Singpass 是 OpenID Provider,服务方是依赖方。
这条边界很重要。
Singpass 应通过标准流程验证用户身份,并签发相应结果;依赖方则应验证结果,再建立自己的本地会话。
依赖方不应只凭前端传来的一句“登录成功”就放行。它的后台必须验证相关内容确实由 Singpass 签发。
Singpass 的公开开发者文档将其描述为新加坡采用 OpenID Connect 的国家数字身份认证提供方,并说明 Singpass 支持授权码流程。在这种模式下,浏览器不应直接收到长期有效的令牌。依赖方先通过重定向流程取得授权码,再由自己的后台用授权码换取令牌。
这个细节听起来很技术化,但对用户来说,结论很简单:
浏览器并不是看到二维码后就登录成功了。
它之所以能完成登录,是因为依赖方与 Singpass
走完了一套可信的身份验证流程。
哪些地方可能出问题 链接到标题
二维码登录很方便,但便利也会划出新的风险边界。
第一种风险,是上下文混淆引发的钓鱼。用户以为自己正在登录某项服务,扫到的二维码却可能属于另一项服务。显示服务名称和请求上下文有助于降低风险,但这道防线能否奏效,也取决于用户是否认真核对。
第二种风险,是挑战中继。如果有人把一个尚未失效的二维码复制到别处,扫码便可能脱离用户原本理解的场景。因此,挑战应很快过期,应要求使用有效的应用凭据进行批准,而且批准前应清楚显示依赖方。
第三种风险,是会话绑定错误。手机上的批准应与最初生成二维码的那笔浏览器事务绑定。否则,一次手机批准可能误让另一个浏览器会话登录成功。
第四种风险,是重放。即使攻击者截获了一条批准消息,也不应再次使用它。时间戳、签名、类似 nonce 的挑战数据和一次性使用机制,都有助于降低这类风险。
这些并非什么离奇的问题,而是跨设备身份验证中的常见问题。
用户看到的只是扫了一次码。
系统看到的却是一套分布式状态机。
为什么它比密码登录更好 链接到标题
用密码登录,意味着用户要在网站上输入一个双方共享的秘密。
随之而来的,都是人们熟悉的问题:
- 密码可能被重复使用
- 密码可能被钓鱼窃取
- 密码可能过于简单
- 用户可能把密码输进假网站
- 服务方必须提供忘记密码后的找回流程
二维码登录改变了整个交互方式。
用户不必在依赖方的网站上输入密码,而是通过 Singpass 应用批准请求;这款应用早已同用户的设备和凭据绑定。
这并不意味着攻击从此绝迹。任何身份验证方法都做不到这一点。但这种方式消除了网络安全中最薄弱的习惯之一:要求用户反复向许多不同的网站输入秘密。
它也为升级身份验证提供了更合适的路径。
对于低风险登录,在应用中批准或许已经足够。遇到风险更高的操作,系统则可以要求更强的验证,例如生物特征验证、人脸验证或明确的交易签名。
背后更大的设计原则是:
身份验证的强度,应与操作风险相匹配。
二维码登录只是这个模型中的一环。
一个简单动作背后的庞大架构 链接到标题
扫描 Singpass 二维码时,多组信任关系正在同时发挥作用:
- 依赖方信任 Singpass 这一身份提供方。
- Singpass 验证已注册的移动应用凭据后,信任该凭据。
- 浏览器只等待自己发起的那一笔特定登录事务。
- 用户核对服务名称后,再批准操作。
- 依赖方验证身份验证结果后,才建立自己的会话。
所以,这项功能用起来很简单,背后的系统却绝不简单。
二维码只是用户看得见的部分。真正撑起整个系统的,是:
- 短期有效的挑战状态
- 移动应用凭据
- 签名验证
- 浏览器与服务器之间的事件更新
- 依赖方的重定向流程
- 令牌验证
- 登录活动记录
- 审计事件
这正是 Singpass 最让我感兴趣的地方。
用户只做了一个很小的动作:扫码并批准。
底层基础设施承担的任务却严肃得多:
它把用户在一台设备上那一刻的意图,
变成另一台设备上可信的登录结果。
因此,二维码登录不只是一套更顺畅的登录界面。
它是国家信任基础设施融入日常生活的一个缩影。