init: consolidate all ephron.ren PRDs and docs
This commit is contained in:
104
fixes/login-redirect-fix.md
Normal file
104
fixes/login-redirect-fix.md
Normal file
@@ -0,0 +1,104 @@
|
||||
# ephron.ren 登录重定向失效修复方案
|
||||
|
||||
## 问题描述
|
||||
|
||||
在主页(www.ephron.ren)未登录状态下,点击右上角「未登录」跳转至登录页,输入账号密码点击登录后,页面不会自动重定向回主页,而是停留在登录页。
|
||||
|
||||
## 影响范围
|
||||
|
||||
所有子服务(Home / Blog / Canvas / Prompt)的登录后重定向均受影响。只要登录页 URL 中带有 `redirect` 参数指向其他子域名,登录后都无法跳转。
|
||||
|
||||
## 根因分析
|
||||
|
||||
### 直接原因
|
||||
|
||||
Chrome 浏览器的 CSP(Content Security Policy)引擎将登录表单的 POST 请求拦截了。
|
||||
|
||||
浏览器控制台报错:
|
||||
|
||||
```
|
||||
Sending form data to 'https://auth.ephron.ren/api/login?redirect=https%3A//www.ephron.ren/'
|
||||
violates the following Content Security Policy directive: "form-action 'self'".
|
||||
The request has been blocked.
|
||||
```
|
||||
|
||||
### 技术细节
|
||||
|
||||
站点响应头中设置了 CSP:
|
||||
|
||||
```
|
||||
form-action 'self'
|
||||
```
|
||||
|
||||
该指令限制表单只能提交到同源 URL。
|
||||
|
||||
**问题在于**:登录表单将 `redirect` 参数拼接在 form action 的 query string 中:
|
||||
|
||||
```html
|
||||
<form method="POST" action="/api/login?redirect=https%3A//www.ephron.ren/">
|
||||
```
|
||||
|
||||
Chrome 的 CSP 引擎在解析 form action URL 时,会将 query string 中的 `%3A` 解码为 `:`,使得 `://` 看起来像协议标识符。引擎误判该 URL 包含跨域目标(`https://www.ephron.ren/`),从而拦截了表单提交。
|
||||
|
||||
### 验证证据
|
||||
|
||||
| 场景 | form action | 结果 |
|
||||
|------|------------|------|
|
||||
| 不带 redirect 参数 | `/api/login` | ✅ 登录成功,跳转到 `/login-success` |
|
||||
| 带 redirect 到自身域名 | `/api/login?redirect=https%3A//auth.ephron.ren/admin` | ✅ 正常 |
|
||||
| 带 redirect 到其他子域名 | `/api/login?redirect=https%3A//www.ephron.ren/` | ❌ CSP 拦截 |
|
||||
|
||||
## 修复方案
|
||||
|
||||
将 `redirect` 参数从 URL query string 改为表单 hidden field,使 form action 保持干净,不含任何可能触发 CSP 误判的字符。
|
||||
|
||||
### 修改文件 1:`auth/templates/login.html`
|
||||
|
||||
```diff
|
||||
- <form class="login-form" method="POST" action="/api/login{% if redirect %}?redirect={{ redirect | urlencode }}{% endif %}">
|
||||
+ <form class="login-form" method="POST" action="/api/login">
|
||||
|
||||
{% if error %}
|
||||
<div class="alert alert-error">
|
||||
{{ error }}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
+ {% if redirect %}
|
||||
+ <input type="hidden" name="redirect" value="{{ redirect }}">
|
||||
+ {% endif %}
|
||||
+
|
||||
<div class="form-group">
|
||||
<label for="username">用户名</label>
|
||||
```
|
||||
|
||||
### 修改文件 2:`auth/src/routes/api.py`
|
||||
|
||||
```diff
|
||||
async def login(
|
||||
request: Request,
|
||||
username: str = Form(...),
|
||||
password: str = Form(...),
|
||||
- redirect: str | None = Query(default=None),
|
||||
- return_url: str | None = Query(default=None, alias="return_url"),
|
||||
- next_url: str | None = Query(default=None, alias="next"),
|
||||
+ redirect: str | None = Form(default=None),
|
||||
+ return_url: str | None = Form(default=None),
|
||||
+ next_url: str | None = Form(default=None),
|
||||
):
|
||||
```
|
||||
|
||||
### 修复原理
|
||||
|
||||
| | 修改前 | 修改后 |
|
||||
|--|--------|--------|
|
||||
| form action | `/api/login?redirect=https%3A//www.ephron.ren/` | `/api/login` |
|
||||
| redirect 传递方式 | URL query string | hidden form field(body) |
|
||||
| CSP 检查 | action URL 含 `//` → 误判为跨域 → 拦截 | action URL 为纯路径 → 同源 → 放行 |
|
||||
|
||||
### 不受影响的部分
|
||||
|
||||
- 登录页 GET 请求传递 redirect 参数(query string)不受影响,CSP `form-action` 只拦截 POST
|
||||
- 登录失败时重定向回登录页(携带 redirect 参数)不受影响,因为那是 303 跳转,不是表单提交
|
||||
- `validate_redirect()` 安全校验逻辑无需修改
|
||||
- cookie 设置逻辑无需修改
|
||||
Reference in New Issue
Block a user