想象你走进一座名为“奇幻园”的主题乐园。开业第一年,乐园采用储物柜方案:游客在售票处录入信息,员工把资料锁进一排中央储物柜,再发一个带编号的手环,手环号就是柜子号。每个项目入口的检票员看见游客抬手,就跑去柜区核对身份,确认后才放行。这套机制让所有身份信息都存在乐园自己手里,叫做“有状态会话”。后来游客太多,柜区前排起长队,乐园决定升级:不再寄存信息,而是发给游客一张盖着防伪章的“通票”,通票上印着游玩权限和有效期,检票员只需验证印章真伪就能放行——这就是“无状态令牌”。再后来,通票也会过期、也会被伪造,乐园又陆续引入了续期窗口、反窃听与反推搡的安保措施、第三方旅行社联票,以及一页实名认证信息。今天,让我们沿着奇幻园的进化史,把会话管理与 Token 安全的关键知识点一次学透。
会话与 Token 安全是 Web 后端与安全工程师的必备基本功。学完本文,你就能独立设计出抗 XSS、抗 CSRF 的认证系统,为你的 AI 应用和业务数据筑起第一道防线。
目录
可视化演示
展示JWT签发→验证→刷新全流程,对比Session有状态与JWT无状态两种模型,标注XSS/CSRF攻击面。
本节配有交互动效(进入视口后自动加载,约 3KB)。
Token认证流程 Client→Auth签发JWT→API验证→DB查询,关注XSS/CSRF攻击面 JWT = Header.Payload.Signature HttpOnly Cookie + SameSite Strict 双重防护
会话机制基础——储物柜与手环
奇幻园开业第一年,检票员个个都是“脸盲症”患者——每个项目入口都不记得游客刚买的到底是套票还是单票。于是乐园在中央大厅设置了一排储物柜:游客买票时,售票员把购票信息写在小纸条上锁进柜子,交给他一个印着柜号的手环。每个项目入口的检票员看到手环号,就跑去柜区取出纸条核对,再放行。这个方案正是 Session 会话机制的雏形:机密信息全部锁在服务器端的“柜子”里,浏览器手里只有一个编号。
HTTP 协议天生无状态,服务器处理完一个请求就不记得客户端了。Session 机制就是为解决这个问题而生:服务器为每个登录用户开辟一份会话存储(储物柜),把用户 ID、角色、购物车等状态放进去,再通过 Cookie 把一个随机的会话 ID(手环编号)交给浏览器。浏览器每次请求自动携带这个 ID,服务器据此找到对应的储物柜,恢复用户状态。
会话 ID 的生成绝不能使用自增数字——否则攻击者把 1001 改成 1000 就能冒充他人。业界要求它足够随机、不可预测,通常由哈希函数对用户标识、时间戳和随机盐计算而来。会话的生命周期包含创建、使用、过期与销毁四个阶段:过期时间一到,服务器自动清理柜子,防止存储无限膨胀;用户主动退出时,也应立即销毁会话。
储物柜方案的逻辑很清晰:机密锁在柜里,浏览器只拿编号,泄露一个手环号顶多暴露一次会话。但分布式时代到来后,乐园在隔壁城市开了分园,总园买票的游客到分园却被拒之门外——分园查不到总园的柜子。Session 在集群中扩展困难的痛点,正是 Token 登场的契机。
- 拼接原始信息把用户标识、当前时间戳、随机盐拼成一条字符串,保证不同用户不同时刻生成的 ID 都不重复。
- 哈希运算对拼接串执行不可逆的 SHA-256 哈希,得到固定长度的摘要。
- 输出会话 ID把摘要转成 URL 安全的字符串,作为手环编号交给浏览器。
Token签发验证——盖章通票
管理层决定取消中央柜区,改发“盖章通票”:售票员把游客信息和游玩权限直接印在票面上,底部盖上一个官方防伪章。每个项目入口的检票员不再跑柜区,而是对照防伪特征快速验章,几十秒就能放行一人。通票本身没有存放任何服务器状态,验证凭据全在票面上——这就是 Token 认证的核心理念。
传统 Session 是“有状态”的:服务器必须记住每个用户。Token 则是“无状态”的:服务器用只有自己知道的密钥对用户信息签名,客户端保存签名后的凭证,后续请求带上它,服务器验签通过就信任信息。这种模式天然适合分布式系统和跨域场景——任何一台服务器只要持有密钥,都能独立完成验证,不需要共享会话存储。
签名算法主要有两类。对称算法(HMAC-SHA256)用同一个密钥签名和验签,性能高,但密钥一旦泄露所有令牌都能被伪造;非对称算法(RSA/ECDSA)用私钥签名、公钥验签,私钥只保存在签发服务器上,适合多方信任的场景。签发流程是:服务端组装用户信息 → 用密钥签名 → 返回令牌;验证流程是:客户端携带令牌 → 服务端验签 → 解析内容 → 放行或拒绝。
盖章通票解决了排队问题,却也带来新矛盾:票面既要印丰富信息,又要让检票员快速识别伪造。怎么设计一张信息完整又防伪可靠的通票?这就催生了三页合订的 JWT。
- 拼接消息把头部和载荷编码后拼成一条消息,作为待签名内容。
- 计算签名用对称密钥对消息执行 HMAC-SHA256,得到固定长度的摘要。
- 验签比对接收方重新计算签名,并与令牌携带的签名比对,一致才通过。
JWT结构解析——三页合订通票
JWT 像一张三页合订的通票。第一页是封面,写明防伪算法(alg)和票据类型(typ);第二页是内容页,印着游客标识、有效期、发行方等声明;第三页是官方验真章——对前两页内容计算出的签名。三页各自用统一的 Base64Url 格式编码后,用句点装订成一体,形成长长的"xxx.yyy.zzz"形态。
JWT(JSON Web Token)的三段结构各司其职。Header 声明签名算法和令牌类型;Payload 承载标准声明,包括 sub(用户唯一标识)、exp(过期时间戳)、iat(签发时间戳)、iss(发行方)、aud(受众);Signature 对前两段的编码串拼接后做签名,是防篡改的最终关卡。
需要特别警惕:Payload 只是 Base64Url 编码,不是加密!任何人都能解码看到内容,所以绝不能放密码、身份证号等敏感信息。哪怕攻击者只改动 Payload 里的一个字符,重新验签时就会因为签名不匹配而失败。Base64Url 与普通 Base64 的区别是把 +、/ 换成 -、_ 并去掉尾部等号,保证令牌能安全放进 URL 和 HTTP Header 中传输。
三页合订的通票确实漂亮,但检票员发现了新问题:通票印着“当日有效”,游客排三小时队后票却过期了。面对“生命周期长怕泄露”与“有效期短体验差”的矛盾,乐园在广场中央开设了续期窗口。
- 编码 Header把 JSON 格式的算法声明转成 Base64Url 字符串。
- 编码 Payload把用户声明 JSON 转成 Base64Url 字符串。
- 生成签名对前两段拼接串执行签名运算。
- 三段拼接用句点把三段连接为最终令牌。
刷新机制设计——续期窗口
乐园规定通票每两小时失效,可热门项目排队就要三小时。于是乐园开设续期窗口:游客凭一张“续期凭证”就能换到新通票,不必重新买票进园;续期凭证的保质期更长,游客离园时还可以主动作废它。这套双票制完美平衡了安全与体验。
这就是 Access Token 与 Refresh Token 的分工。Access Token 有效期短(15 分钟到 2 小时),即使泄露,攻击者能利用的时间窗口也很小;Refresh Token 有效期长(7 天到 30 天),专门用来换取新的 Access Token。客户端收到 401 响应后,携带 Refresh Token 向授权服务器申请新令牌,用户全程无感知。
刷新机制有两个关键设计。一是令牌轮换:每次刷新时不仅签发新的 Access Token,也签发新的 Refresh Token 并作废旧值,即使旧的刷新令牌被截获也无法重放。二是撤销机制:服务端维护已撤销令牌列表,用户修改密码、退出登录或检测到异常时立即吊销刷新令牌,实现真正的“一键登出”。存储上,Refresh Token 绝不能交给 JavaScript,应放在 HttpOnly Cookie 中,且必须走 HTTPS。
续期窗口让游客畅玩一整天,但安保部门又接到新报案:有人在宣传栏贴带毒海报偷看手环,还有小偷趁游客双手拿满东西时从背后推他一把。XSS 与 CSRF 攻击正式登场。
- 有效期分级访问令牌的有效期远小于刷新令牌,但刷新令牌也有上限。
- 刷新调用客户端携带刷新令牌到令牌端点兑换新的访问令牌。
- 旧令失效令牌轮换后,旧的刷新令牌被服务端立即撤销。
攻击防护体系——反窃听与反推搡
乐园遇到的坏人分两类。第一类是“窃听贼”:他往官方宣传栏里塞进一张带毒海报,游客一点开,海报里的脚本就开始偷读手环编号,甚至把手环号发给贼——这就是 XSS(跨站脚本攻击)。第二类是“推手”:他躲在储物柜旁,趁游客双手都拿着东西时突然推他一下,让游客“自己”走向危险项目——这就是 CSRF(跨站请求伪造)。
XSS 的本质是恶意脚本在受害者浏览器里执行,进而读取 Cookie、LocalStorage 中的令牌。防护三板斧:输入转义(把 <script> 变成无害文本)、开启 CSP(内容安全策略,只允许加载白名单脚本)、为 Cookie 设置 HttpOnly(JS 无法读取)。CSRF 的本质是浏览器自动携带 Cookie 的特性被滥用——恶意站点诱导用户向目标站点发请求,Cookie 被自动带上。对策是在 Cookie 上设置 SameSite 属性、在请求中校验来源、增加 CSRF Token。
SameSite 是浏览器级别的防线,有三种取值:Strict 表示跨站完全不携带 Cookie;Lax 表示顶级导航跨站时携带、子请求不携带;None 表示全部携带但必须搭配 Secure。绝大多数场景推荐 Lax——它允许用户从外部链接正常进入站点,又阻止了跨站表单提交。服务端还应校验 Origin 头,对写操作使用自定义 Header 或 CSRF Token 做第二道验证。
防御升级后,乐园内部稳如泰山。但游客开始抱怨:奇幻园、恐龙园、海洋馆三张门票要分别排队购买。三园管理方一合计,推出了“旅行社联票”——这正是 OAuth2 的授权模型。
- 核对来源检查请求头中的 Origin 与站点期望的来源是否一致。
- 限制携带SameSite 为 Lax/Strict 时,跨站子请求不会自动携带 Cookie。
- 双重放行来源一致且 Cookie 未被跨站滥用,两个条件同时满足才放行。
OAuth2授权流程——旅行社联票
三家乐园不再各自售票,而是委托一家旅行社做统一票务。游客去旅行社办联票,拿到一张“三联通票”,票面标明了能进哪几个园、每园玩几天。三家乐园的检票员看到联票即可放行,但不会把游客的完整资料透露给其他园——只传递必要信息,这就是授权与数据最小化的思想。
OAuth2 中有四个角色:资源所有者(游客)、客户端(第三方应用)、授权服务器(旅行社)、资源服务器(三家园)。最常用的是授权码模式:用户访问客户端 → 客户端引导用户到授权服务器 → 用户同意授权 → 授权服务器返回一个一次性授权码 → 客户端用授权码加上自己的密钥换取访问令牌 → 携带令牌访问资源服务器。
授权码模式的关键设计在于授权码只能使用一次、有效期仅几分钟,并且换取令牌时必须同时出示 client_id 和 client_secret,即使授权码在网络中被截获,攻击者也无法在缺少密钥的情况下兑换令牌。Scope 机制用于声明权限范围,比如“只读”还是“读写”,严格遵循最小权限原则。对于纯前端应用,还要搭配 PKCE 扩展(Code Challenge),用动态生成的校验值防止授权码被其他应用拦截。
联票带来了便利,但三家乐园检票时都发现:联票能证明“买了票”,却证明不了“本人是谁”。游客拿着别人的联票也能入园。实名认证的需求,让 OIDC 应运而生。
- 携带授权码客户端把一次性授权码发给授权服务器的令牌端点。
- 客户端认证同时提交 client_id 和 client_secret,证明自己是被信任的第三方应用。
- 校验回调地址redirect_uri 必须与申请时登记的一致,防止授权码被重定向到攻击者站点。
- 兑换令牌校验全部通过后返回访问令牌与刷新令牌。
OIDC身份层——实名认证
旅行社在联票上增加了一页“实名认证信息”:姓名、证件号、照片、签发日期,由官方认证机构盖章。三家乐园只需用官方公布的验章信息核验,就能确认游客身份真实。联票从“入园凭证”升级成了“数字身份证”。
OIDC(OpenID Connect)在 OAuth2 之上增加了身份认证层。OAuth2 只回答“客户端能否访问资源”,OIDC 回答“用户是谁”。其核心产物是 ID Token——一个由授权服务器签发的 JWT,包含标准声明:sub(用户唯一标识)、iss(签发者)、aud(受众)、exp(过期时间)、iat(签发时间)。客户端拿到 ID Token 后,就能安全地确认当前用户的身份。
验证 ID Token 需要经过四道关卡:用授权服务器公布的 JWKS 公钥验签、校验 iss 是否为期望的签发者、校验 aud 是否包含自己的 client_id、校验 exp 是否未过期。OIDC 还提供了发现文档(/.well-known/openid-configuration),客户端访问一个固定 URL 就能拿到授权端点、令牌端点、JWKS 地址等全部配置,实现零配置接入。基于这套标准,企业可以搭建 SSO 单点登录——员工在门户登录一次,就能访问所有接入系统,无需重复输密码。
从储物柜手环、盖章通票、三页通票、续期窗口,到旅行社联票与实名认证,奇幻园的进化史就是 Web 会话安全的发展史。现在,你可以把这些经验带回自己的系统设计中了。
- 验签用授权服务器发布的 JWKS 公钥验证 ID Token 的签名是否真实。
- 校验发行者与受众iss 必须与授权服务器一致,aud 必须包含自己的 client_id,防止令牌被冒用。
- 校验过期时间exp 时间戳必须晚于当前时间,过期令牌直接拒绝。
AI 应用场景
企业级单点登录(SSO)门户
阶段:架构设计 / 统一身份认证
解决的问题:传统方式下每套系统独立登录,员工需要记多套密码,AI 平台(如内部 LLM 网关、模型训练平台)与统一身份体系难以打通。通过 OIDC + SSO,员工在门户登录一次即可访问所有 AI 工具,权限由 ID Token 中的角色声明统一控制。
具体案例:某公司用 Keycloak 做统一认证中心,内部 AI 门户接入 OIDC。员工登录一次,就能访问 JupyterHub、模型注册中心和向量数据库控制台。GPU 集群的调度界面根据 ID Token 里的角色字段判断谁能提交训练任务,安全且高效。
移动端 AI 应用的 Token 自动刷新
阶段:客户端开发 / 网络层优化
解决的问题:移动端网络不稳定,Access Token 过期会导致请求频繁失败,用户被迫反复登录,体验极差。利用“短期 Access Token + 长期 Refresh Token + 令牌轮换”机制,客户端在拦截器中捕获 401,自动换新令牌并重放请求。
具体案例:一款 AI 聊天 App 使用 15 分钟的有效访问令牌和 7 天的刷新令牌。用户在弱网环境下聊天,令牌过期后应用自动用刷新令牌换取新令牌,同时作废旧刷新令牌,全程无感知,且泄露风险始终被压缩在 15 分钟窗口内。
第三方 AI 能力开放平台
阶段:API 网关 / 开放平台
解决的问题:对外提供 AI 接口(如翻译、OCR、语音识别)时,需要让合作方安全调用,又不能暴露主账号密钥。通过 OAuth2 授权码模式,每个合作方获得独立的 client_id/secret,Scope 限制其只能调用特定模型接口,配额独立计费。
具体案例:某云厂商的开放平台采用 OAuth2 授权码模式,合作方申请应用凭证后,通过授权码换取自己的 Access Token。Scope 声明中写明“只能调用通用OCR接口”,即使 Token 泄露,攻击者也无法调用其他高价值模型。
Python 演示
import base64import hashlibimport hmacimport jsonimport time SECRET_KEY = b"your-secret-key" def b64url_encode(raw: bytes) -> str: return base64.urlsafe_b64encode(raw).rstrip(b"=").decode("ascii") def b64url_decode(data: str) -> bytes: padding = "=" * (-len(data) % 4) return base64.urlsafe_b64decode(data + padding) def sign_message(message: str, secret: bytes) -> str: digest = hmac.new(secret, message.encode("utf-8"), hashlib.sha256).digest() return b64url_encode(digest) def create_jwt(user_id: str, expire_minutes: int = 30) -> str: header = {"alg": "HS256", "typ": "JWT"} now = int(time.time()) payload = { "sub": user_id, "iat": now, "exp": now + expire_minutes * 60, "iss": "example-app" } header_b64 = b64url_encode(json.dumps(header).encode("utf-8")) payload_b64 = b64url_encode(json.dumps(payload).encode("utf-8")) message = header_b64 + "." + payload_b64 signature = sign_message(message, SECRET_KEY) return message + "." + signature def verify_jwt(token: str) -> dict: header_b64, payload_b64, sig = token.split(".") message = header_b64 + "." + payload_b64 expected_sig = sign_message(message, SECRET_KEY) if sig != expected_sig: raise ValueError("签名验证失败:令牌被篡改") payload = json.loads(b64url_decode(payload_b64)) if payload["exp"] < int(time.time()): raise ValueError("令牌已过期") return payload if __name__ == "__main__": token = create_jwt("user_1001", expire_minutes=1) print("签发JWT:", token) decoded = verify_jwt(token) print("验证通过,载荷:", decoded)
b64url_encode(raw: bytes) -> str把字节做 Base64Url 编码并去掉尾部等号,这是 JWT 的标准编码方式,保证令牌在 URL 中安全传输。sign_message(message, secret)用 HMAC-SHA256 对“前两段拼接串”计算签名,对应正文中“官方防伪章”的生成。payload = {"sub": ..., "exp": ..., "iss": ...}构造 Payload 标准声明,sub 是用户唯一标识,exp 控制过期时间。signature = sign_message(message, SECRET_KEY)签名只覆盖前两段,Payload 任何改动都会导致验签失败。if sig != expected_sig:验签时重新计算签名并比对,这是 Token 防篡改的核心检查。if payload["exp"] < int(time.time()):检查过期时间,过期令牌直接拒绝,对应正文中“通票过期失效”。
常见误区
误区我一直以为 Session 就是把数据存在 Cookie 里,因为登录后浏览器里能看到 sessionid,就认为 Cookie 就是 Session。
正解Session 是服务器端的存储状态,Cookie 只是浏览器携带 SessionID 的运输工具。好比 Session 是储物柜,Cookie 是手环——手环上只写编号,真正的物品锁在柜子里。手环丢了可以补办,柜子里的数据还在;反过来把柜子拆了,手环就成了一串废塑料。
为何常见:日常调试中开发者最先接触的是 Cookie 里那个 sessionid 字段,把运输载具当成了存储本身。
误区很多教程说 JWT 要放 localStorage,因为 Cookie 会被 CSRF 利用,所以 localStorage 更安全。
正解localStorage 的短板是任何同源脚本都能读,一旦发生 XSS,令牌直接被偷走;而 HttpOnly Cookie 恰恰能防住 XSS 读取。真正的做法是按风险取舍:优先用 HttpOnly Cookie 加 SameSite,或使用短寿命令牌加内存存储。安全不是单点选择,而是分层防御。
为何常见:网上说法以讹传讹,把 CSRF 与 XSS 的防护对象彻底搞混,导致开发者选错存储位置。
误区把刷新令牌有效期设成一年甚至永不过期,用户体验最好,反正 Access Token 短就行。
正解刷新令牌是“长期通行证”,一旦泄露等于把账号永久交给攻击者。正确做法是设定合理有效期(如 7–30 天)、每次刷新轮换旧令牌、服务端实现撤销机制、检测异常立即吊销。便利与安全要平衡,不能无限拉长有效期。
为何常见:产品经理追求零登录体验,开发者在压力下不断放宽有效期,忽视了长令牌泄露的放大风险。
误区网站上了 HTTPS 后数据都加密了,Token 存储和传输细节都不用担心。
正解HTTPS 只保护传输过程,不保护存储端。Token 放在 localStorage 里遇到 XSS 一样被读走;日志、Referer、浏览器历史都可能泄露 URL 中的令牌。安全必须纵深防御:传输用 HTTPS、存储用 HttpOnly、必要时加动态校验与指纹绑定。
为何常见:把网络层安全等同于整体安全,忽略了应用层存储、日志等大量攻击面。
误区后端已经用了 JWT 认证,为什么还要 CSRF Token?JWT 不就能防 CSRF 吗?
正解JWT 解决“你是谁”的认证问题,CSRF Token 解决“这个请求是否用户主动发出”。只要 JWT 放在 Cookie 里被浏览器自动携带,跨站表单就能借用它发起恶意请求,CSRF 依然成立。所以即使有 JWT,也应配合 SameSite 或 CSRF Token 做双重防护。
为何常见:把认证与防伪这两个不同层级的机制混为一谈,以为认证通过就自然防御了所有攻击。
面试题
Q1. Session 与 Token 最核心的区别是什么?
- A.Session服务端存状态,Token带签名← 正确答案
- B.Session走Cookie,Token只能走请求头
- C.Session绝对安全,Token永不安全
- D.Session可注销,Token不可注销
答案:A。Session 把状态存在服务器端储物柜,Token 把签名后的凭证交给客户端携带。B 错误,Token 也能放 Cookie;C、D 都过于绝对,Token 在服务端记录撤销列表时也能注销。
Q2. JWT 中哪一段被篡改后验签必然失败?
- A.Header和Payload内容被改动← 正确答案
- B.Signature字段时间戳失效
- C.只有Payload过期时间异常
- D.Base64Url的填充字符被修改
答案:A。Signature 是对 Header 和 Payload 拼接串的签名,所以 Header 或 Payload 任一改动都会导致签名不匹配。B、D 描述的对象不在验签范围内;C 只是 Payload 改动的一种特例,不全面。
Q3. 设置 HttpOnly 属性的主要目的是什么?
- A.禁止JS脚本读取Cookie内容← 正确答案
- B.阻止Cookie在浏览器持久保存
- C.强制Cookie走HTTPS加密传输
- D.让Cookie可以跨站点自动发送
答案:A。HttpOnly 是给 Cookie 加一道“防读锁”,让 JavaScript 无法读取内容,专门防 XSS 窃取会话。B 是过期时间的作用,C 是 Secure 属性的作用,D 是 SameSite=None 的行为。
Q4. CSRF 攻击成功的关键前提是什么?
- A.浏览器自动携带目标站点的Cookie← 正确答案
- B.攻击者成功窃取了用户明文密码
- C.用户浏览器彻底禁用了JavaScript
- D.目标站点使用Token而非Session
答案:A。CSRF 利用的是浏览器自动携带 Cookie 的特性,目标站点识别成“用户本人”发出的请求。B 是窃取凭证而非伪造请求;C 禁用 JS 反而降低攻击面;D 中 Token 认证同样可能被 CSRF 利用。
Q5. SameSite=Lax 模式下 Cookie 何时会被携带?
- A.顶级导航跨站请求时才会携带← 正确答案
- B.所有跨站子请求都会自动携带
- C.任何请求场景都不会携带它
- D.只有HTTPS安全请求才会携带
答案:A。Lax 是推荐模式:用户点击外部链接进入网站的顶级导航会带 Cookie,而跨站 iframe、AJAX 等子请求不带。B 是 None 的行为;C 是 Strict 的行为;D 与 SameSite 无关,是 Secure 的职责。
Q6. OAuth2 授权码模式下授权码的有效期通常是多久?
- A.几分钟内失效且只能使用一次← 正确答案
- B.可以重复使用直到自己过期
- C.与访问令牌的有效期保持相同
- D.永不过期直到用户主动注销
答案:A。授权码是一次性票据,几分钟内有效,换到令牌后立即作废。这样即使授权码在网络中被截获,攻击者也来不及兑换。B、D 会放大泄露风险;C 混淆了授权码与访问令牌的生命周期。
Q7. 以下哪些属于 JWT Payload 的标准声明?
- A.sub 声明用户的唯一标识← 正确答案
- B.exp 声明令牌的过期时间← 正确答案
- C.alg 声明签名算法类型
- D.aud 声明令牌的受众← 正确答案
答案:ABD。sub、exp、aud 都是 Payload 的标准声明,分别表示主题、过期时间和受众;alg 属于 Header 的声明,描述签名算法,不在 Payload 中。
Q8. 以下哪些措施可以有效防护 XSS 攻击?
- A.对用户输入进行HTML转义← 正确答案
- B.启用CSP内容安全策略← 正确答案
- C.为Cookie设置HttpOnly← 正确答案
- D.用POST替代GET请求方式
答案:ABC。输入转义让恶意脚本失效,CSP 限制脚本加载来源,HttpOnly 让脚本读不到 Cookie,三者都是 XSS 的经典防线。D 是防止敏感数据泄露的通行做法,与 XSS 没有直接关系。
Q9. 以下哪些是刷新令牌的推荐实践?
- A.每次刷新后轮换旧刷新令牌← 正确答案
- B.为刷新令牌设置合理有效期← 正确答案
- C.只在HTTPS通道中传输令牌← 正确答案
- D.把刷新令牌放在URL参数里
答案:ABC。轮换、限时、HTTPS 传输都是刷新令牌的安全基线。D 是典型反模式:URL 会出现在历史记录、日志和 Referer 中,刷新令牌放在 URL 里极易泄露。
Q10. OIDC 相比 OAuth2 核心新增的能力是什么?
- A.签发可验证的身份认证ID令牌← 正确答案
- B.提供资源服务器的访问权限
- C.支持多种授权码模式变体
- D.自动托管用户的账号密码
答案:A。OIDC 在 OAuth2 授权基础上增加身份认证层,核心是签发标准化的 ID Token,让客户端安全确认“用户是谁”。B 是 OAuth2 本来就有的能力;C 是协议流程细节;D 完全不是 OIDC 的职责。