<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Authentication on Armstrong Yan</title><link>https://yanqian.github.io/zh/tags/authentication/</link><description>Recent content in Authentication on Armstrong Yan</description><generator>Hugo</generator><language>zh-CN</language><lastBuildDate>Tue, 21 Jul 2026 08:29:11 +0800</lastBuildDate><atom:link href="https://yanqian.github.io/zh/tags/authentication/index.xml" rel="self" type="application/rss+xml"/><item><title>扫描 Singpass 二维码后，系统里发生了什么</title><link>https://yanqian.github.io/zh/posts/publish/what-happens-when-you-scan-a-singpass-qr-code/</link><pubDate>Tue, 21 Jul 2026 08:29:11 +0800</pubDate><guid>https://yanqian.github.io/zh/posts/publish/what-happens-when-you-scan-a-singpass-qr-code/</guid><description>&lt;p&gt;你在笔记本电脑上打开一家银行的网站。&lt;/p&gt;
&lt;p&gt;点击“使用 Singpass 登录”。&lt;/p&gt;
&lt;p&gt;页面上出现一个二维码。&lt;/p&gt;
&lt;p&gt;你打开 Singpass 应用，扫描二维码，核对服务名称，然后确认登录。网站随即知道可以让你登录了。&lt;/p&gt;
&lt;p&gt;在用户眼里，整个过程简单得近乎不可思议：网页显示二维码，手机完成确认，浏览器接着登录。&lt;/p&gt;
&lt;p&gt;但真正关键的问题是：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;这个二维码究竟证明了什么？
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;答案不是“二维码里装着我的身份信息”。&lt;/p&gt;
&lt;p&gt;设计得当的二维码登录，恰恰不该如此。二维码里不应包含你的 NRIC、姓名或个人资料，也不应放入长期有效的登录令牌。它只应是一份短期有效的邀请，请你完成一笔特定的身份验证事务。&lt;/p&gt;
&lt;p&gt;用系统设计的语言来说：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;二维码登录，不是把身份装进二维码。
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;它发起的是一项短期有效的挑战，
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;由可信的移动端凭据批准，
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;再交给网页端的登录会话使用。
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这正是整个设计的核心。&lt;/p&gt;
&lt;figure&gt;&lt;img src="https://yanqian.github.io/posts/publish/what-happens-when-you-scan-a-singpass-qr-code/assets/singpass-trust-infrastructure/familiar/singpass-qr-login.jpg"
 alt="Singpass QR login app screen" width="280"&gt;
&lt;/figure&gt;

&lt;p&gt;这就是大家熟悉的表面体验：扫描或轻点二维码，就能登录。真正重要的是，这个二维码被允许代表什么、它会多快过期，以及手机上的批准最终会绑定到哪个浏览器会话。&lt;/p&gt;
&lt;p&gt;&lt;img src="https://yanqian.github.io/posts/publish/what-happens-when-you-scan-a-singpass-qr-code/assets/singpass-trust-infrastructure/02-singpass-qr-login-flow.svg" alt="Singpass 二维码登录流程"&gt;&lt;/p&gt;
&lt;h2 id="二维码只是一道入口"&gt;
 二维码只是一道入口
 &lt;a class="heading-link" href="#%e4%ba%8c%e7%bb%b4%e7%a0%81%e5%8f%aa%e6%98%af%e4%b8%80%e9%81%93%e5%85%a5%e5%8f%a3"&gt;
 &lt;i class="fa-solid fa-link" aria-hidden="true" title="链接到标题"&gt;&lt;/i&gt;
 &lt;span class="sr-only"&gt;链接到标题&lt;/span&gt;
 &lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;第一个常见误区，是把二维码本身当成秘密。&lt;/p&gt;
&lt;p&gt;人们很容易以为，二维码携带着某种神奇的身份数据。毕竟，用户一扫码，浏览器就登录成功了。但如果仅凭二维码就能登录，那么任何看到它的人都可以复制、转发，甚至反复使用。&lt;/p&gt;
&lt;p&gt;这样的设计非常危险。&lt;/p&gt;
&lt;p&gt;更稳妥的做法，是让二维码只包含一个不透明的挑战标识：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;qr_challenge_id = &amp;#34;看似随机、短期有效的标识符&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这个标识符本身没有多少意义。它指向服务器端的一条状态记录，而这条记录是在浏览器发起登录请求时创建的。&lt;/p&gt;
&lt;p&gt;服务器掌握的信息包括：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;哪个依赖方请求了此次登录&lt;/li&gt;
&lt;li&gt;哪个浏览器会话正在等待&lt;/li&gt;
&lt;li&gt;请求涉及哪些权限或操作&lt;/li&gt;
&lt;li&gt;挑战何时过期&lt;/li&gt;
&lt;li&gt;挑战是否已经被扫描、批准、拒绝、使用，或已经过期&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;二维码只是让移动应用向系统表明：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;我正在回应这一次特定的登录尝试。
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;因此，二维码登录不能只做“生成二维码”这一步，后台还必须维护一个生命周期很短的状态机。&lt;/p&gt;
&lt;p&gt;屏幕上的流程看似简单，底层却更接近这样：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;已创建 -&amp;gt; 已扫描 -&amp;gt; 已批准 -&amp;gt; 已使用
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; \-&amp;gt; 已过期
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; \-&amp;gt; 已拒绝
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; \-&amp;gt; 已取消
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;状态机之所以重要，是因为登录不是一个静止的页面，而是一笔横跨两台设备的交互事务。系统必须拒绝不安全的状态变化，例如：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;已过期 -&amp;gt; 已批准
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;已使用 -&amp;gt; 已批准
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;已批准 -&amp;gt; 再次批准
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;重试、网络延迟或重复点击，都可能触发两次批准请求。但系统不应因此建立两个浏览器会话，也不应签发两个授权码。只有第一次有效批准可以生效。&lt;/p&gt;</description></item></channel></rss>