如何配置Firebase规则阻止用户删除他人数据并允许新用户注册?
问题
我在游戏开发中使用Firebase,当前配置的Firebase规则如下:
{ "rules": { ".read": true, ".write": "auth != null", "users": { ".indexOn": ["nickname", "attackPoints"], "$uid": { "nickname": { ".validate": "newData.val().matches(/^[a-zA-Z0-9_]+$/)" , ".read": true, ".write": "$uid === auth.uid" }, "gold": { ".read": "$uid === auth.uid", ".write": "$uid === auth.uid && newData.isNumber() && ((newData.val() >= 0 && newData.val() <= (data.val() + 1000)) || (data.exists() === false && newData.val() <= 1000))" }, "experience": { ".read": "auth != null", ".write": "$uid === auth.uid && newData.isNumber() && ((newData.val() >= 0 && newData.val() <= (data.val() + 1000)) || (data.exists() === false && newData.val() <= 1000))" }, "attackPoints": { ".read": "auth != null", ".write": "$uid === auth.uid && newData.isNumber() && ((newData.val() >= 0 && newData.val() <= (data.val() + 1000)) || (data.exists() === false && newData.val() <= 1000))" }, "characterImageName": { ".read": "auth != null", ".write": "$uid === auth.uid" }, "work": { ".read": "$uid === auth.uid", ".write": "$uid === auth.uid" }, "inventory": { ".read": "auth != null", ".write": "$uid === auth.uid" }, "attacks": { "$attackId": { ".validate": "newData.child('attacker').val() === auth.uid || newData.child('target').val() === auth.uid || newData.child('target').val() === $uid", ".read": "$uid === auth.uid || data.child('attacker').val() === auth.uid", ".write": "$uid === auth.uid || newData.child('attacker').val() === auth.uid || newData.child('target').val() === auth.uid" } } } } } }
现在遇到两个问题:
- 用户X可以删除其他用户(如userY)的
gold等数据 - 尝试移除根节点的
".write": "auth != null",规则后,新用户注册时触发权限拒绝错误,注册代码如下:
val nicknameRef = database.child("users").orderByChild("nickname").equalTo(nick) nicknameRef.addListenerForSingleValueEvent(object : ValueEventListener { override fun onDataChange(snapshot: DataSnapshot) { if (snapshot.exists()) { // Username is already taken showErrorDialog(getString(R.string.username_is_already_taken)) } else { // Username is available, proceed with registration auth.createUserWithEmailAndPassword(email, password) .addOnCompleteListener(this@Register) { task -> if (task.isSuccessful) { val user = auth.currentUser val userId = user?.uid if (userId != null) { val userData = HashMap<String, Any>() userData["nickname"] = nick userData["experience"] = 0 userData["gold"] = 200 userData["attackPoints"] = 35 userData["characterImageName"] = profile database.child("users").child(userId).setValue(userData) .addOnCompleteListener { dbTask -> if (dbTask.isSuccessful) { // Registration and data storage successful navigateToHome() } else { showErrorDialog( getString( R.string.error_while_saving_user_data_in_database, dbTask.exception?.message )) } } } } else { showErrorDialog( getString( R.string.registration_error, task.exception?.message )) } } } } })
请问如何调整Firebase规则,既阻止用户删除他人数据,又能让新用户正常完成注册?
解决方案
问题根源
- 根节点的
.write: auth != null是全局写权限,只要用户登录就能修改/删除任何路径的数据,包括其他用户的节点,这是导致用户能删除他人数据的核心原因。 - 移除全局写权限后,注册时创建
users/$uid节点没有对应的权限,所以触发拒绝错误。
调整后的规则
{ "rules": { ".read": true, "users": { ".indexOn": ["nickname", "attackPoints"], "$uid": { // 允许用户创建自己的节点,或修改自己的节点,但禁止删除整个用户节点 ".write": "$uid === auth.uid && newData.exists()", "nickname": { ".validate": "newData.val().matches(/^[a-zA-Z0-9_]+$/)", ".read": true, // 禁止删除nickname,要求新数据必须存在且符合格式 ".write": "$uid === auth.uid && newData.exists()" }, "gold": { ".read": "$uid === auth.uid", // 禁止删除gold,同时保留原有的数值校验规则 ".write": "$uid === auth.uid && newData.exists() && newData.isNumber() && ((newData.val() >= 0 && newData.val() <= (data.val() + 1000)) || (data.exists() === false && newData.val() <= 1000))" }, "experience": { ".read": "auth != null", // 禁止删除experience,保留数值校验 ".write": "$uid === auth.uid && newData.exists() && newData.isNumber() && ((newData.val() >= 0 && newData.val() <= (data.val() + 1000)) || (data.exists() === false && newData.val() <= 1000))" }, "attackPoints": { ".read": "auth != null", // 禁止删除attackPoints,保留数值校验 ".write": "$uid === auth.uid && newData.exists() && newData.isNumber() && ((newData.val() >= 0 && newData.val() <= (data.val() + 1000)) || (data.exists() === false && newData.val() <= 1000))" }, "characterImageName": { ".read": "auth != null", // 禁止删除characterImageName ".write": "$uid === auth.uid && newData.exists()" }, "work": { ".read": "$uid === auth.uid", // 禁止删除work ".write": "$uid === auth.uid && newData.exists()" }, "inventory": { ".read": "auth != null", // 禁止删除inventory ".write": "$uid === auth.uid && newData.exists()" }, "attacks": { "$attackId": { ".validate": "newData.child('attacker').val() === auth.uid || newData.child('target').val() === auth.uid || newData.child('target').val() === $uid", ".read": "$uid === auth.uid || data.child('attacker').val() === auth.uid", // 允许创建/修改攻击记录,但禁止删除 ".write": "newData.exists() && ($uid === auth.uid || newData.child('attacker').val() === auth.uid || newData.child('target').val() === auth.uid)" } } } } } }
关键修改说明
- 移除根节点全局写权限:彻底避免任何登录用户随意修改其他用户数据的可能。
- 给
users/$uid层级添加写规则:$uid === auth.uid:确保只有用户自己能操作自己的节点newData.exists():禁止删除整个用户节点(删除操作时newData不存在)
- 给所有子节点补充
newData.exists():禁止用户删除自己的gold、nickname等子节点,如果业务需要允许删除特定子节点,可以单独移除该节点的newData.exists()限制。 - 注册流程适配:注册时调用
setValue(userData)是创建自己的users/$uid节点,此时$uid等于当前用户的auth.uid,且节点原本不存在,符合$uid === auth.uid && newData.exists()的规则,所以能正常写入。
内容的提问来源于stack exchange,提问作者RGS
相关产品推荐
相关产品推荐

