# ephron.ren 登录重定向失败问题分析报告 > **严重等级**: 🔴 Critical > **影响范围**: 所有需要登录的页面(Home / Blog / Canvas / Prompt / Auth) > **发现日期**: 2026-05-05 > **状态**: 已确认复现 --- ## 1. 问题概述 在 ephron.ren 全站所有页面中,未登录用户点击「登录」后跳转至 `auth.ephron.ren/login?redirect=`,填写账号密码点击登录按钮后,**无法重定向回原始页面**,始终停留在登录页。 **影响的所有入口**: | 来源页面 | 登录页 redirect 参数 | 结果 | |----------|---------------------|------| | https://www.ephron.ren/ | `aHR0cHM6Ly93d3cuZXBocm9uLnJlbi8=` | ❌ 失败 | | https://blog.ephron.ren/ | `aHR0cHM6Ly9ibG9nLmVwaHJvbi5yZW4v` | ❌ 失败 | | https://canvas.ephron.ren/ | `aHR0cHM6Ly9jYW52YXMuZXBocm9uLnJlbi8=` | ❌ 失败 | | https://prompt.ephron.ren/ | `aHR0cHM6Ly9wcm9tcHQuZXBocm9uLnJlbi8=` | ❌ 失败 | | auth.ephron.ren/admin(同源) | `aHR0cHM6Ly9hdXRoLmVwaHJvbi5yZW4vYWRtaW4=` | ✅ 成功 | | 无 redirect 参数 | N/A → `/login-success` | ✅ 成功 | **关键发现**: 当 redirect 目标与 auth.ephron.ren **同源**时登录正常,**跨源**(不同子域)时失败。 --- ## 2. 根因分析 ### 2.1 问题根因 **CSP `form-action 'self'` 策略阻止了登录接口的 303 跨源重定向。** 完整流程分析: ``` ① 用户在 www.ephron.ren 点击「登录」 → 跳转到 https://auth.ephron.ren/login?redirect=aHR0cHM6Ly93d3cuZXBocm9uLnJlbi8= ② 用户填写账号密码,点击「登录」按钮 → 浏览器 POST 到 https://auth.ephron.ren/api/login(同源 ✅ 允许) ③ 服务端验证成功,返回 HTTP 303 重定向 → Location: https://www.ephron.ren/(跨源 ❌) → 响应头包含 Content-Security-Policy: ... form-action 'self' ④ 浏览器检查 CSP form-action 策略 → 重定向目标 https://www.ephron.ren/ ≠ 'self'(https://auth.ephron.ren) → 浏览器阻止重定向,停留在登录页 ⑤ 结果:Cookie 已设置(ephron_auth),但页面未跳转 ``` ### 2.2 技术细节 #### CSP 配置(auth.ephron.ren 所有响应) ``` Content-Security-Policy: default-src 'self'; script-src 'self' 'unsafe-inline'; script-src-elem 'self' 'unsafe-inline' https://cdn.jsdelivr.net; style-src 'self' 'unsafe-inline'; style-src-elem 'self' 'unsafe-inline' https://fonts.googleapis.com https://cdn.jsdelivr.net https://maxcdn.bootstrapcdn.com; img-src 'self' data: https:; font-src 'self' data: https://fonts.gstatic.com https:; connect-src 'self'; frame-ancestors 'none'; base-uri 'self'; form-action 'self' ``` 关键指令: **`form-action 'self'`** #### CSP `form-action` 行为说明 根据 CSP Level 2/3 规范,`form-action` 指令限制的是「表单提交的目标 URL」。在 Chromium 的实现中,表单提交触发的 **整个重定向链** 都受此策略约束: 1. 表单 POST 到 `/api/login`(`https://auth.ephron.ren`)→ ✅ 同源,允许 2. 服务端返回 303 到 `https://www.ephron.ren/` → ❌ 跨源,被阻止 #### 登录接口响应(curl 验证) ```http HTTP/2 303 location: https://www.ephron.ren/ set-cookie: ephron_auth=eyJ2...; Domain=.ephron.ren; HttpOnly; Max-Age=604800; Path=/; SameSite=lax; Secure content-security-policy: ... form-action 'self' ``` 服务端逻辑完全正确:验证凭证 → 签发 token → 设置 Cookie → 303 重定向。但浏览器因 CSP 策略拒绝执行重定向。 #### 源码定位 - **CSP 配置**: `shared/security_headers.py` 第 8-20 行,`_CSP_POLICY` 常量 - **登录接口**: `auth/src/routes/api.py` 第 174-241 行,`POST /api/login` - **重定向校验**: `auth/src/utils/redirect.py` 第 16-75 行,`validate_redirect()` - **登录页面模板**: `auth/templates/login.html` 第 140 行,表单 action --- ## 3. 浏览器复现证据 ### 3.1 控制台错误 ``` [ERROR] Sending form data to 'https://auth.ephron.ren/api/login' violates the following Content Security Policy directive: "form-action 'self'". ``` ### 3.2 网络请求分析 | 请求 | 状态 | 说明 | |------|------|------| | `GET /login?redirect=...` | 200 ✅ | 登录页正常加载 | | `POST /api/login` | 已发送 ✅ | 浏览器确实发出了 POST 请求 | | 303 重定向响应 | ❌ 未跟随 | 浏览器收到 303 但未执行跳转 | ### 3.3 Cookie 状态 登录后 `ephron_auth` Cookie 已正确设置(`Domain=.ephron.ren; HttpOnly; Secure; SameSite=Lax`),说明服务端逻辑完全正常。问题纯粹在客户端 CSP 策略。 ### 3.4 对照实验 | 测试场景 | 结果 | CSP 错误 | |----------|------|----------| | 无 redirect 参数登录 | ✅ 跳转到 `/login-success` | 无 | | redirect = `auth.ephron.ren/admin`(同源) | ✅ 跳转到 admin | 无 | | redirect = `www.ephron.ren`(跨源) | ❌ 停留登录页 | `form-action 'self'` | | redirect = `blog.ephron.ren`(跨源) | ❌ 停留 API URL | `form-action 'self'` | | redirect = `canvas.ephron.ren`(跨源) | ❌ 停留 API URL | `form-action 'self'` | | redirect = `prompt.ephron.ren`(跨源) | ❌ 停留 API URL | `form-action 'self'` | --- ## 4. 修复方案 ### 方案 A(推荐): 从 303 响应中移除 CSP 头 在 `shared/security_headers.py` 的中间件中,对 303 重定向响应不添加 CSP 头: ```python @app.middleware("http") async def _security_headers(request: Request, call_next): response = await call_next(request) response.headers.setdefault("X-Content-Type-Options", "nosniff") response.headers.setdefault("X-Frame-Options", "DENY") response.headers.setdefault("Referrer-Policy", "strict-origin-when-cross-origin") # 仅对非重定向响应添加 CSP(避免 form-action 阻止跨源重定向) if response.status_code not in (301, 302, 303, 307, 308): response.headers.setdefault("Content-Security-Policy", _CSP_POLICY) # ... Cache-Control 逻辑不变 ``` **优点**: 最小改动,不影响其他页面的 CSP 保护 **缺点**: 重定向响应失去 CSP 保护(但重定向响应通常无 HTML 内容,CSP 保护意义不大) ### 方案 B: 修改 form-action 策略 将 `form-action 'self'` 改为允许所有 ephron.ren 子域: ``` form-action 'self' https://*.ephron.ren ``` 或更精确地列出所有子域: ``` form-action 'self' https://www.ephron.ren https://blog.ephron.ren https://canvas.ephron.ren https://prompt.ephron.ren ``` **优点**: 明确允许列表,安全性可审计 **缺点**: 新增子域需同步更新 CSP;通配符 `*` 可能过于宽松 ### 方案 C: 改用 JavaScript 重定向 修改登录接口,返回 200 + JSON,由前端 JS 执行 `window.location.href` 跳转: ```javascript // 前端 const res = await fetch('/api/login', { method: 'POST', body: formData }); const data = await res.json(); if (data.success) { window.location.href = data.redirect_url; } ``` **优点**: 完全绕开 CSP form-action 限制 **缺点**: 需要修改前后端逻辑;JS 禁用时无法登录(渐进增强降级) ### 方案 D: 使用中间页面中转 登录成功后先重定向到 `auth.ephron.ren/redirect?url=`(同源),再由该页面通过 meta refresh 或 JS 跳转到目标: ```html ``` **优点**: 保持 form-action 'self' 不变 **缺点**: 多一次跳转,增加延迟;需要额外的路由和页面 --- ## 5. 推荐修复路径 **推荐方案 A**,理由: 1. **改动最小**: 仅修改 `security_headers.py` 中间件的 1 行逻辑 2. **安全性可接受**: 303 重定向响应体为空(`content-length: 0`),CSP 对其无实际保护作用 3. **不影响其他安全头**: `X-Content-Type-Options`、`X-Frame-Options`、`Referrer-Policy` 仍正常添加 4. **无需修改前端**: 登录流程保持 HTML 表单提交,不依赖 JavaScript --- ## 6. 附录 ### 6.1 受影响的完整页面列表 所有跳转到 `auth.ephron.ren/login?redirect=...` 的页面均受影响: - `www.ephron.ren` — 首页 - `blog.ephron.ren` — 博客(含文章详情页、管理后台) - `canvas.ephron.ren` — 画布 - `prompt.ephron.ren` — 提示词(含详情页、管理后台) - `auth.ephron.ren` — 注册成功后跳转、管理后台 ### 6.2 注册表单同样受影响 注册页面 (`auth.ephron.ren/register`) 使用相同的 CSP 策略和表单提交模式,注册成功后的 303 重定向也可能受 `form-action 'self'` 影响。需一并验证和修复。 ### 6.3 测试环境 - 浏览器: Chromium (Playwright headless) - 测试账号: `Elaina_user` / `Elaina_owner` - 测试时间: 2026-05-05 22:00-22:30 CST