
本文详解在静态网站(如 github pages、vercel)中安全使用 fauna 实现用户注册与登录,重点解决前端直连导致的权限泄露风险,推荐采用 udf + 凭据(credentials)+ 令牌(token)的标准化认证模式。
在静态托管站点中直接将 Fauna 秘钥(secret)硬编码于前端 JavaScript 中——如你当前代码所示——存在严重安全隐患:该密钥一旦泄露,攻击者即可完全控制你的数据库,执行任意读写操作。更危险的是,你当前的 login() 函数直接通过 q.Get(q.Match(...)) 获取完整用户文档,并用明文比对 result.password,而实际数据结构中密码位于 result.data.password(因 Fauna 文档的业务数据始终嵌套在 data 字段下)。这不仅逻辑错误,更暴露了原始密码字段,违背最小权限与安全存储原则。
✅ 正确做法是:禁用前端直连管理密钥,改用 Fauna 原生 Credentials 机制 + 自定义函数(UDF)封装认证逻辑。以下是关键改进步骤:
1. 启用 Credentials 并创建安全索引
在 Fauna Dashboard 中为 users 集合启用凭据支持(无需手动存 password 字段):
// 在 Shell 中执行(需管理员密钥)
CreateCollection({ name: "users" })
// 启用 Credentials(自动哈希密码,仅存 salted hash)
CreateCredentials({
collection: Collection("users")
})
// 创建唯一邮箱索引(用于查找用户)
CreateIndex({
name: "users_by_email",
source: Collection("users"),
terms: [{ field: ["data", "email"] }],
unique: true
})2. 编写安全 UDF(Signup & Login)
在 Fauna 中创建两个函数,仅允许公共密钥调用:
// Function: signup
Query(
Lambda(
["email", "password"],
Let(
{
user: Create(Collection("users"), {
credentials: { password: Var("password") },
data: { email: Var("email") }
})
},
Var("user")
)
)
)// Function: login
Query(
Lambda(
["email", "password"],
Let(
{
userRef: Select(
["ref"],
Get(Match(Index("users_by_email"), Var("email")))
),
loginResult: Login(Var("userRef"), { password: Var("password") })
},
Var("loginResult")
)
)
)3. 前端调用 UDF(使用 Public Key)
生成一个 Public Key(权限仅限调用上述两个函数),替换前端密钥:
⚠️ 关键注意事项
- 永不暴露管理密钥(Admin Secret)到前端:它应只用于服务端或 CI/CD 初始化。
- Credentials 是必须的:Fauna 的 Login() 函数依赖凭据集合,明文存密码是反模式。
- Token 权限可控:Login() 返回的 secret 可绑定 Role,限制用户仅能访问自身数据(例如 Read(Collection("posts")) 且 Where("author", Identity()))。
- HTML ID 冲突修复:你的表单中两个 input#email 和 input#password ID 重复,会导致 document.getElementById 获取错误元素。请为登录/注册表单分别使用唯一 ID(如 id="signup-email" / id="login-email")。
遵循此方案,你的静态网站即可获得企业级认证能力:密码零明文传输、权限严格隔离、密钥永不泄露。立即参考 Fauna 官方认证教程 实践部署。










