为无认证的简易Firebase实时按钮配置数据库安全规则
Firebase安全规则配置:无认证下的WordPress实时按钮防篡改方案
首先,你的核心需求很明确:不需要用户认证,只允许前端修改特定的Firebase路径(也就是你的server和timerState节点),同时防止其他路径被篡改,还要保证按钮的正常点击逻辑。直接设"write": false肯定不行,因为你的代码需要更新这两个节点来控制按钮显示和计时器状态。
第一步:精准配置Firebase实时数据库安全规则
你需要针对用到的两个特定路径做权限限制,同时开放必要的读取权限(因为前端要读取状态来更新UI)。这里的关键是只允许写入合法的内容类型,既满足业务需求,又防止恶意篡改。
把你的Firebase安全规则改成下面这样:
{ "rules": { ".read": true, // 前端需要读取所有用到的节点,所以开放全局读取 ".write": false, // 默认禁止所有写入,只给特定路径开权限 "server": { // 只允许写入数字类型(对应你代码里的时间戳) ".write": "newData.isNumber()" }, "timerState": { // 只允许写入"on"或"off"这两个字符串 ".write": "newData.isString() && (newData.val() === 'on' || newData.val() === 'off')" } } }
规则说明:
.read: true:你的前端需要读取server的时间戳和timerState的状态来更新计时器和按钮显示,所以全局开放读取是安全的(毕竟这些数据都是公开的状态)。- 默认
.write: false:从根源上禁止所有未授权的写入,只有指定路径例外。 server节点的写入规则:只接受数字,正好匹配你点击按钮时写入的时间戳now = new Date().getTime(),非法的字符串或其他类型都会被拒绝。timerState节点的写入规则:只接受"on"或"off",完全对应你代码里的timerState.set('on')和timerState.set('off'),防止写入其他恶意内容。
第二步:验证你的代码和规则的兼容性
你的现有代码完全符合上面的规则要求:
- 点击按钮时,你只更新
server(写入时间戳数字)和timerState(写入"on") - 计时器结束时,你更新
server为0(数字)和timerState为"off"
把规则部署后,你的按钮逻辑会正常工作,同时:
- 任何人都不能修改Firebase里的其他路径(如果有的话)
- 也不能给
server写入非数字内容,或者给timerState写入除了on/off之外的字符串
额外的安全小提示
如果担心有人恶意频繁点击按钮刷写入请求,可以给写入规则加个速率限制,比如限制每个IP每分钟最多写入5次:
"server": { ".write": "newData.isNumber() && request.auth == null && request.rateLimit(5, 60000)" }, "timerState": { ".write": "newData.isString() && (newData.val() === 'on' || newData.val() === 'off') && request.auth == null && request.rateLimit(5, 60000)" }
这里的request.auth == null明确限制无认证的用户(也就是你的场景),rateLimit(5, 60000)表示60秒内最多5次写入请求。
你的完整代码(整理后)
var database = firebase.database(); //firebase queue //************** ///timer fb let timestamp; let now = new Date().getTime(); let endTimeRef = firebase.database().ref("server"); let endTime; let minutes; let secondsDisplay; let distance = endTime - now; let buttonDisplay = document.getElementById("queue"); let timerDisplay = document.getElementById("timer_fb"); let message = document.getElementById("timer_div"); function displayTimer() { minutes = Math.floor((distance % (1000 * 60 * 60)) / (1000 * 60)); secondsDisplay = Math.floor((distance % (1000 * 60)) / 1000); let s; if (minutes >= 1 || secondsDisplay >= 1) { if (minutes >= 1 && secondsDisplay >= 10) { s = "Time Until Next Person: " + minutes + ":" + secondsDisplay; } else if (minutes >= 1) { s = "Time Until Next Person: " + minutes + ":0" + secondsDisplay; } else { s = "Time Until Next Person: " + secondsDisplay + "s"; } } else { s = ""; } document.getElementById("timer_fb").innerHTML = s; } function setTimer() { let interval = setInterval(function() { now = new Date().getTime(); distance = endTime - now; //update timer display displayTimer(); if (distance <= 0) { buttonDisplay.style.display = "block"; timer_div.style.display = "block"; document.getElementById("timer_div").innerHTML = "This session is open!"; clearInterval(interval); timerState.set('off'); endTimeRef.set(0); } }, 1000); } //state machine let timerState = firebase.database().ref("timerState"); let timer; timerState.on("value", function(snapshot) { timer = snapshot.val(); if(timer == "on"){ buttonDisplay.style.display = "none"; message.style.display = "none"; endTimeRef.once("value", function(snap) { let endStamp = snap.val(); console.log("stored button snap value check: ", snap.val()); now = new Date().getTime(); endTime = endStamp + 100000; distance = endTime - now; //show display right after button is pressed displayTimer(); setTimer(); }); } }); //******** //*****on click handler document.getElementById("queue").onclick = function() { //hide button buttonDisplay.style.display = "none"; message.style.display = "none"; //set time button comes back now = new Date().getTime(); endTime = now + 100000; endTimeRef.set(now); //firebase state machine timerState.set('on'); };
内容的提问来源于stack exchange,提问作者mso4491
相关产品推荐
相关产品推荐

