Vue+Node+Firestore TodoList应用:Admin SDK操作Firestore安全问询
首先得给你提个醒:仅验证accessToken是否存在确实有严重的安全隐患,尤其是你用了Firebase Admin SDK的情况下——因为Admin SDK默认是完全绕过Firestore安全规则的,这一点很多开发者容易忽略!
先拆解下核心风险:如果后端只检查token存在,不验证它的有效性(比如是否过期、签名是否合法),也不关联到具体的用户身份,那恶意攻击者只要拿到任何一个有效的Firebase ID Token(哪怕是别人的),就能通过你的后端操作不属于他的Firestore数据。更糟的是,如果token是伪造的,后端没验证签名,攻击者甚至能冒充任意用户执行操作。
接下来给你梳理生产环境下必须做的安全加固措施,都是实战中验证过的:
1. 严格验证ID Token的合法性,而非仅检查存在
你后端拿到前端传来的token后,必须用Firebase Admin SDK的admin.auth().verifyIdToken(idToken)方法做完整验证:
- 验证token的签名是否由Firebase官方签发,防止伪造
- 检查token的过期时间(
exp字段),拒绝过期token - 验证token的发行者(
iss字段)是否为你的Firebase项目域名,避免跨项目的token滥用 - 从验证结果中提取用户UID(
uid字段),后续所有Firestore操作都要基于这个UID做数据过滤
示例代码:
// 后端验证token并获取用户UID的中间件 async function verifyUserToken(req, res, next) { const idToken = req.headers.authorization?.split('Bearer ')[1]; if (!idToken) { return res.status(401).send('Unauthorized: No token provided'); } try { const decodedToken = await admin.auth().verifyIdToken(idToken); req.userUid = decodedToken.uid; next(); } catch (error) { return res.status(403).send('Forbidden: Invalid or expired token'); } }
2. 后端操作Firestore时强制基于用户UID过滤数据
因为Admin SDK绕过安全规则,所以不能依赖Firestore安全规则来限制用户数据访问,必须在后端代码里主动过滤:
- 查询用户的todo列表时,加上
where('userId', '==', req.userUid) - 创建todo时,自动把
userId字段设为当前用户的UID,不允许前端传入 - 更新/删除todo时,先查询该todo的
userId是否等于当前用户UID,再执行操作
比如创建todo的后端逻辑:
app.post('/api/todos', verifyUserToken, async (req, res) => { const { title, completed } = req.body; // 自动注入用户UID,禁止前端指定 const todoData = { title, completed: completed || false, userId: req.userUid, createdAt: admin.firestore.FieldValue.serverTimestamp() }; const docRef = await admin.firestore().collection('todos').add(todoData); res.status(201).send({ id: docRef.id, ...todoData }); });
3. 最小化Admin SDK服务账号的权限
不要用默认的Firebase Admin服务账号(通常带有Editor权限),而是创建一个自定义服务账号,只授予它必要的权限:
- 前往GCP控制台的IAM页面,找到你的Firebase项目
- 创建新的服务账号,比如命名为
todo-backend-service - 给这个账号分配Firestore Datastore User权限(而非Owner/Editor),优先保证后端代码过滤的前提下,进一步缩小权限范围
- 用这个自定义服务账号的密钥文件初始化Admin SDK,而非默认的密钥
4. 前端层面的token安全管理
- 你提到用Vuex存储accessToken,这里要注意:Firebase密码认证返回的是ID Token(短期有效,默认1小时)和Refresh Token(长期有效),Vuex存储ID Token是没问题的(内存存储,刷新页面就失效),但不要把ID Token存在
localStorage或sessionStorage——容易被XSS攻击窃取 - 前端要监听ID Token的过期事件,自动调用
firebase.auth().currentUser.getIdToken(true)刷新token,避免用过期token请求后端 - 请求后端时,用
Authorization: Bearer <idToken>的方式携带token,不要放在URL参数里(容易被日志记录或泄露)
5. 增加日志与监控
- 在后端记录所有Firestore操作的日志,包括用户UID、操作类型(增/删/改/查)、数据ID等,方便事后追踪异常行为
- 启用GCP的Cloud Monitoring,设置告警规则:比如异常的403/401请求量、高频的Firestore写入操作等,及时发现攻击
6. 封装API,避免直接暴露Firestore操作
不要让前端直接指定Firestore的集合路径或查询条件,而是封装成业务相关的API:
- 比如用
GET /api/todos获取当前用户的所有todo,而非让前端请求/api/firestore/collections/todos?where=userId:... - 这样可以避免攻击者构造恶意查询,也能让后端更好地控制数据访问逻辑
总结一下:你的Firestore安全规则对Admin SDK无效,所以后端必须承担起身份验证和数据过滤的责任,同时最小化服务账号权限,才能确保生产环境的安全。
内容的提问来源于stack exchange,提问作者m.lapeyre

