认证bug少见而授权bug普遍存在,关键原则是永远不信任客户端传来的标识符,十五秒内说清答案。
你不是来面试安全工程师的。面试官要考察的是你的一种习惯:描述系统时,是否会注意到谁可能滥用它。
安全类问题很少会贴上标签。它们出现在普通技术对话的追问环节,而那些关键的问题都回归到同一种本能:把输入当作敌对的,追问谁信任什么、为什么信任。
每个人都能说出它们的区别。真正有意思的是:认证的 bug 很少见,授权的 bug 却无处不在,因为认证是一条经过充分测试的代码路径,而授权是每个端点上都要做的决策。
值得专门点名的一种失败模式是:信任来自客户端的标识符。一个端点根据 URL 中的 id 去取记录,检查调用者已登录,然后就返回——这是有问题的:调用者通过了身份验证,但对那条记录没有访问权。能一口气说清这一点,比任何定义都更有价值。
这是一个几乎必问的问题,好的答案 15 秒就能说完。
初级答案:哈希加盐,绝不明文存储,用 bcrypt 而不是 MD5 或 SHA-256,因为后两者太快了,不是为密码设计的。
高级答案:故意慢的、内存硬的哈希算法,比如 bcrypt 或 argon2,工作因子根据登录路径能承受的开销来调,并随着硬件变便宜而提高。慢才是关键,因为真正的威胁是数据泄露后的离线破解,而不是登录时的在线攻击。盐是每个用户独立的,和哈希值存在一起,这样彩虹表就无法覆盖所有账号。我还会让失败路径的响应时间保持恒定,因为如果一个登录对不存在的邮箱返回明显更快,就会泄露哪些账号存在。
这个时序侧信道就是追问。主动提到它会让你进入另一个类别的候选人。
通常会讨论无状态令牌与服务端 Session 的区别,而且这确实是一个权衡题,而不是有标准答案的问题。
无状态令牌省去了每次请求的查询,所以扩展性很好。代价是无法即时撤销:在它过期之前,被盗的令牌始终有效,服务端没有记录可以删除。这就是核心权衡,成熟的答案是短生命周期的 access token 加上 refresh token,只在风险值得那一趟往返的路径上维护一个拒绝列表。
永远不要接受令牌本身携带的算法。在服务端固定它。
验证签名、issuer、audience 和过期时间,而不只是验证它能解码。
payload 对持有者的任何人都是可读的。已签名不等于私有。
尽量把它存在脚本读不到的地方,通常意味着 httpOnly cookie 而不是 localStorage。
这个问题依然在问,依然值得精确回答,因为大多数候选人说的是"转义",而正确答案其实是"分离"。
参数化查询并不清理输入,它把查询和数据分开发送,所以数据库永远不会把用户输入的文本解析成 SQL。这个区别很重要,因为转义是一个可能出错的过滤器,而分离是结构性的。同样的思路也适用于 shell 命令和其他任何解释器:传参数,不要拼接字符串。
// 可注入的:值会成为解析后语句的一部分
db.query("SELECT * FROM users WHERE email = '" + email + "'");
// 参数化的:值永远不会被解析成 SQL
db.query("SELECT * FROM users WHERE email = $1", [email]);
第一个版本把用户输入拼进了语句里。第二个版本把语句和值通过不同通道发送,所以值里的任何东西都无法改变查询的结构。
值得能干净利落地区分开来,因为候选人经常被问到其中一个时给出另一个的缓解方案。
跨站脚本攻击是可信的内容在你的页面中变成可执行的,防御手段是上下文相关的输出编码加上 Content Security Policy。跨站请求伪造是另一个网站导致浏览器向你的站点发起一个已认证的请求,防御手段是一个其他网站读不到令牌加上 SameSite Cookie。一个是关于什么在你的页面里运行,另一个是关于谁发起了请求。如果你有 XSS,你的 CSRF 防御就毫无意义,因为攻击者已经在你的源里运行了——能说出这一点说明你理解了两者的关系,而不只是记住了标签。
通常会问得很实际:你的凭证放在哪里。期望的答案是运行时从密钥管理服务或平台环境注入,绝不提交到仓库,而且可以在不修改代码的情况下轮换。
值得准备的一个追问是:密钥泄露后怎么办。先轮换,再评估影响,并且假设任何提交到仓库的内容都永久泄露了,无论后续是否删除了那个提交。面试官会留意最后这一点。
越来越常见,尤其是任何有登录表单或昂贵端点的系统。
需要展示的推理是你在限制什么、为什么。限制 IP 容易但效果弱,因为攻击者会轮换地址,而共享网络会伤害真实用户。按账号限制可以防止针对一个目标的凭证填充。在不同阈值下两者结合,通常是诚实的答案。一个好的追问是:当你的服务运行在多个进程上时,计数器放在哪里——这会把你带回到共享状态的问题,也就是 Node 进程水平扩展时遇到的同样问题。
不是一张清单。面试官在听的是你会不会主动说出滥用场景,就像你会主动说出权衡一样。说出自己的设计中会先攻击哪个端点的人,已经展示出了这一轮在寻找的本能。
了解常见的攻击类别和一种威胁建模的本能。没人指望你会漏洞利用。但每个人都期望你注意到用户的输入会到达一个查询。
了解类别,能properly描述两到三个。背诵十个标签但说不出机制,是透明的,而且得分很低。
如实说,从你构建过的系统出发推理。说出你对自己过去的工作会在哪里担心,是一个有力且诚实的答案。
安全类问题几乎都是追问环节的事情,定义在那里就不管用了。跑一场以安全为主题的面试,看看你的会在哪里结束。