如何为与MV3 Chrome扩展交互的Firebase后端做安全防护(含AppCheck、Auth)
Chrome扩展对接Firebase时的恶意请求防护方案
一、绕过Manifest V3 CSP限制,实现AppCheck验证
由于reCAPTCHA需要加载外部脚本,完全不符合Manifest V3的内容安全策略(CSP),这里可以改用自定义AppCheck提供者,利用Chrome扩展的唯一标识(扩展ID)和用户认证信息生成验证令牌:
扩展端实现:
在扩展的弹窗或后台脚本中,生成包含扩展ID、用户UID(来自Firebase Auth)的自定义JWT令牌(注意MV3中要使用ES模块版本的JWT库,比如jwt-esm):// 扩展端代码(后台/弹窗) import jwt from 'jwt-esm'; const generateAppCheckToken = async (user) => { const payload = { iss: chrome.runtime.id, // 你的扩展唯一ID sub: user.uid, // 当前登录用户的UID exp: Math.floor(Date.now() / 1000) + 3600 // 令牌有效期设为1小时 }; // 用你自己生成的密钥签名,注意密钥要保密,别打包到扩展里 return jwt.sign(payload, 'YOUR_CUSTOM_APPCHECK_SECRET', { algorithm: 'HS256' }); };调用Callable函数时,把这个自定义令牌作为参数传递给云端,或者用自定义HTTP头传递(如果改用HTTP触发器的话)。
Cloud Functions端验证:
在Cloud Functions中配置AppCheck的自定义验证逻辑,校验令牌的合法性:// Cloud Functions端代码 const { initializeAppCheck, CustomProvider } = require('firebase-admin/app-check'); const jwt = require('jsonwebtoken'); initializeAppCheck(app, { provider: new CustomProvider({ getToken: async (request) => { // 从Callable请求的参数里拿到自定义令牌 const appCheckToken = request.rawRequest.body.data.appCheckToken; try { // 验证签名和内容 const decoded = jwt.verify(appCheckToken, 'YOUR_CUSTOM_APPCHECK_SECRET'); // 校验扩展ID是否和你的官方扩展一致 if (decoded.iss !== 'YOUR_OFFICIAL_EXTENSION_ID') { throw new Error('非法扩展来源'); } return { token: appCheckToken, valid: true }; } catch (err) { return { token: appCheckToken, valid: false }; } } }), isTokenAutoRefreshEnabled: false });这样既避开了MV3的CSP限制,又能确保请求来自你的合法扩展。
二、提前拦截未认证/非法请求,减少函数触发次数
1. 扩展端前置拦截
在调用云端函数前,先强制检查用户是否已登录,未登录直接阻断请求,根本不往云端发:
// 扩展端代码 const callProtectedFunction = async () => { const user = firebase.auth().currentUser; if (!user) { alert('请先登录账号'); return; } // 生成自定义AppCheck令牌 const appCheckToken = await generateAppCheckToken(user); // 调用Callable函数 const result = await firebase.functions().httpsCallable('yourTargetFunction')({ // 业务参数 appCheckToken: appCheckToken }); };
2. Cloud Functions端快速拦截
在Callable函数的最开头,先检查用户认证状态和AppCheck令牌,不合法直接抛出错误,避免执行后续业务逻辑:
// Cloud Functions端Callable函数 exports.yourTargetFunction = functions.https.onCall(async (data, context) => { // 第一步:检查用户是否已认证 if (!context.auth) { throw new functions.https.HttpsError('unauthenticated', '用户未登录'); } // 第二步:验证自定义AppCheck令牌 const appCheckToken = data.appCheckToken; try { const decoded = jwt.verify(appCheckToken, 'YOUR_CUSTOM_APPCHECK_SECRET'); // 确保令牌中的用户UID和当前认证用户一致,且来源是你的扩展 if (decoded.iss !== 'YOUR_OFFICIAL_EXTENSION_ID' || decoded.sub !== context.auth.uid) { throw new Error('非法请求'); } } catch (err) { throw new functions.https.HttpsError('failed-precondition', '请求不合法'); } // 第三步:执行正常业务逻辑 // ... });
3. 改用HTTP触发器替代Callable函数
如果需要更前置的拦截(比如连函数初始化都不想触发),可以改用HTTP触发器,在请求到达时直接验证ID Token和AppCheck令牌,不合法就返回401:
// Cloud Functions端HTTP触发器 exports.yourHttpFunction = functions.https.onRequest(async (req, res) => { // 从请求头获取Firebase Auth的ID Token const idToken = req.headers.authorization?.split('Bearer ')[1]; if (!idToken) { return res.status(401).send('缺少身份令牌'); } try { // 验证ID Token const decodedToken = await admin.auth().verifyIdToken(idToken); // 获取自定义AppCheck令牌 const appCheckToken = req.headers['x-appcheck-token']; const decodedAppCheck = jwt.verify(appCheckToken, 'YOUR_CUSTOM_APPCHECK_SECRET'); // 校验扩展ID和用户UID匹配 if (decodedAppCheck.iss !== 'YOUR_OFFICIAL_EXTENSION_ID' || decodedAppCheck.sub !== decodedToken.uid) { return res.status(403).send('请求非法'); } // 执行业务逻辑 res.status(200).send('请求成功'); } catch (err) { return res.status(401).send('令牌验证失败'); } });
三、额外防护措施,降低计费风险
- 设置Cloud Functions配额:在Google Cloud控制台给每个函数设置每日调用次数、CPU使用时长的限额,超过后自动拒绝请求。
- 启用Firebase计费告警:设置账单阈值,当费用接近阈值时触发邮件提醒,及时发现异常流量。
- 绑定API密钥到扩展ID:在Google Cloud控制台的API密钥管理中,将你的Firebase API密钥限制为仅允许你的Chrome扩展ID使用,防止第三方盗用密钥调用API。
- 启用VPC服务控制:将Cloud Functions加入VPC服务边界,限制只能从合法来源访问。
内容的提问来源于stack exchange,提问作者luigisuncorner
相关产品推荐
相关产品推荐

