安全模型
信任边界
| 边界 | 谁是被信任的 |
|---|---|
| Lumen Worker | 运维人员(部署者) |
| Prism | 身份提供方,完全信任 |
| 配置的 Prism 团队 | owner/co-owner 可管理应用配置与地址 |
| 团队成员 | 可正常收发自己的邮件 |
| 入站邮件发件方 | 不信任——内容会被净化 |
| 邮件中的远程图片服务器 | 不信任——通过代理抓取 |
认证
- 会话存储于 KV,Cookie 为
httpOnly; Secure; SameSite=Lax。 - 会话 TTL 跟随 Prism 令牌;授予了
offline_access时,接近过期时自动用 refresh token 续期。 - 用户在配置团队中的 角色 会在每次登录时从 Prism 重新读取。 在 Prism 中调整角色,会在该成员下次登录后立即生效。
授权
- 所有邮箱接口都按
user_id = session.userId隔离。 - 管理员接口 (
/api/admin/*) 要求role ∈ { owner, co-owner }。 - 初始化接口
POST /api/init/setup:仅在 “尚未暂存配置” 时开放; 一旦暂存,必须由配置团队的 owner 通过 OAuth 回调来翻转init:configured。 - WebSocket 升级会拒绝跨源请求,并重新校验会话 Cookie。
入站 HTML 邮件
每封 HTML 邮件入库时都会被净化:
- 删除
<script>、<style>、<iframe>、<object>、<embed>、<form>、<svg>、<math>、<base>、<meta>、<link>(标签 + 内容)。 - 删除所有
on*事件处理属性。 href/src/action必须匹配^(https?:|mailto:|cid:|tel:|#|/)。- 行内
style一律丢弃(防止expression()、url(javascript:...)、@import等手法)。 <a>自动加target=_blank+rel="noopener noreferrer nofollow"。<img>自动加loading=lazy+referrerpolicy=no-referrer,且 外部http(s)URL 被改写为图片代理。
净化后的 HTML 在浏览器中以 <iframe sandbox="allow-popups allow-popups-to-escape-sandbox" srcdoc=…> 渲染,并通过 <meta http-equiv="Content-Security-Policy"> 进一步约束。 脚本不会运行,插件不会加载,文档把自身视为独立源(无法读取父页 Cookie)。
出站 HTML
发送的 HTML 由 RFC 822 构造器封装为 multipart/alternative;构造器会:
- 从所有头部值中剥离 CR/LF。
- 校验
Message-ID、In-Reply-To、References必须是合法的<…@…>。 - 校验附件文件名与 MIME。
- 拒绝非法地址。
图片代理
参见 存储 中的 SSRF 防护清单。
速率与请求体限额
- 批量 PATCH 接口单次最多 500 个 id。
- 图片代理:最大 10 MB,8 秒超时,最多 3 跳重定向。
- 单用户存储配额(管理员可配置)阻止入站发件人或自循环写入造成的成本失控。
- 写信侧:浏览器侧附件大小限制阻止显然过大的上传。
不在 Lumen 防护范围内
- 垃圾邮件。请使用 Cloudflare Email Routing 规则与 SPF/Spamhaus 等手段在边缘过滤。
- 钓鱼。Lumen 原样展示邮件,不会校验 SPF/DKIM/DMARC 结果。
- 发件人信誉 / DKIM 签名。出站走 Cloudflare Email Sending,共享发件人信誉由 Cloudflare 决定。
报告漏洞
请在 siiway/lumen 仓库提 issue,标题加 [security];或通过 README 中列出的运维联系方式邮件告知。