Reglas de seguridad en Firebase Realtime Database 🔥

En este artículo te explicaré cómo usar las reglas de seguridad de Firebase Realtime Database para bastionar tu base de datos

14.05.2022 a las 12:51

Reglas de seguridad en Firebase Realtime Database 🔥

En este artículo te explicaré cómo usar las reglas de seguridad de Firebase Realtime Database para bastionar tu base de datos


Esta semana he comenzado a recibir avisos en mi consola de Firebase de que una de mis bases de datos de Realtime Database no tenía reglas seguras.


Las reglas que tenía implementadas eran las típicas de permitir acceso de lectura y escritura a cualquiera que estuviera logueado en mi aplicación:


        
  {
    "rules": {
      ".read": "auth.uid!=null",
      ".write": "auth.uid!=null",
    }
  }
  
    

Pero parece que eso ha empezado a no ser suficiente para Firebase como seguridad para la base de datos, ya que la advertencia decía que cualquiera que estuviera logueado podía acceder a toda la información de la base de datos y eso no era seguro.

Como es cierto que no es lo mejor, me puse a estudiar cómo mejorar la seguridad en el acceso de los datos en RealTime Database y como me ocurre casi siempre, los mejores recursos para aprender algo sobre Firebase los encuentro en su documentación oficial.


Estos han sido mis aprendizajes y conclusiones:

Crear una nueva entrada de usuarios para cada "cliente (tienda)"

Imagina que tienes varios clientes (por ejemplo varias tiendas) en tu base de datos, pues he añadido para cada tienda una lista de usuarios donde iré almacenando los UID de los usuarios que se registren para esa tienda.

De esta forma, cada usuario sólo podrá leer y escribir sobre los datos de la tienda en la que esté registrado.


        
  {
    "rules": {     
      "$storeId":{
      ".read": "data.child('users').hasChildren([auth.uid])"",
      ".write": "data.child('users').hasChildren([auth.uid])",  
      }    
    }
  }
  
    

Tipos de reglas

    Podrás escribir reglas para:
  • Leer
    
                ".read"
            
  • Escribir
    
                ".write"
            
  • Validar
    
                ".validate"
            
    ; controlará el formato correcto de los datos en cuanto a atributos y tipo
  • Obtener índice
    
                ".indexOn"
            
    ; indicará el hijo en donde se hará la consulta (ordenar...)

Variables por defecto que puedes usar en las reglas

  • auth: esta variables tiene todos los datos relacionados con la autenticación del usuario como el UID el proveedor de autenticación... Un valor de null indica que el usuario no está autenticado.
  • now: son los milisegundos desde EPOCH (1 de enero de 1970)
  • data: hace referencia a los datos relacionados con la petición de lectura o escritura. Suministra un *RuleDataSnapshot con diferentes métodos para encontrar el contenido de un dato en concreto.
  • newData: es un *RuleDataSnapshot creado después de que una petición de escritura sea aprobada.
  • root:*RuleDataSnapshot con el árbol de la base de datos

Métodos del *RuleDataSnapshot

  • child(); devuelve un RuleDataSnapshot de una ruta especificada
  • parent(); devuelve el nodo padre del actual nodo
  • hasChild(childpath); este método devolverá true o false dependiendo si existe el hijo especificado
  • hasChildren([children]); true o false, dependiendo si existe el array de hijos enviados como argumento
  • exists(); true o false si el RuleDataSnapShot tiene data
  • getPriority(); devuelve la prioridad de la data del snapshor
  • isNumber(); true o false dependiendo de si el snapshot es un valor numérico
  • isString(); true o false dependiendo de si el snapshot es un string
  • isBoolean(); true o false dependiendo de si el snapshot es un boleano
  • val(); es un método del método child() y extrae el valor asociado al nodo hijo.

El siguiente ejemplo permitirá añadir datos a la base de datos siempre y cuando esos datos contengan un email, un userId y un Name


        ".write":"data.child('fjmartinez').child('name').child('userid').child('email').exists()"
    

Ejemplos de reglas comunes

Sin seguridad

Comienzo todos mis proyectos así y poco a poco voy añadiendo reglas


        
  {
    "rules": {
    ".read": true,
    ".write": true
    }
  }
  
    

Sin acceso a los datos

Si encontraras abusos a tu base de datos puedes escribir estas reglas para que nadie pueda leer ni escribir hasta que solventes el problema.


        
  {
    "rules": {
    ".read": false,
    ".write": false
    }
  }
  
    

Acceso sólo para usuarios autenticados


        
  {
    "rules": {
      ".read": "auth.uid!=null",
      ".write": "auth.uid!=null",
    }
  }
  
    

Acceso sólo para usuarios autenticados de un dominio


        
  {
    "rules": {
    ".read": “auth.token.email.endsWith(‘@domain.com’)”,
    ".write": “auth.token.email.endsWith(‘@domain.com’)”
    }
   }
  
    

Acceso a datos de usuario sólo por el propio usuario

Acceso a los datos del usuario que estarán dentro del nodo con su uid y a su vez dentro del nodo users


        
  {
    "rules": {
      "users": {
        "$uid": {
          ".read": "$uid === auth.uid",
          ".write": "$uid === auth.uid"
        }
      }
    }
  }
  
    

Escritura sólo permitida para el rol admin


        
  {
    "rules": {
    "posts": {
    "$uid": {
    ".write": "root.child('users').child('admin').val() === true"
    }
    }
    }
   }
  
    

Validar datos


        
  {
    "rules": {
    "posts": {
    "$uid": {
    ".validate": "newData.isString() 
    && newData.val().length > 0
    && newData.val().length <= 140"
    }
    }
    }
   }
  
    

Validar si existe atributos


        
  {
    "rules": {
    "posts": {
    "$uid": {
    ".validate": "newData.hasChildren(['username', 'timestamp'])"
    }
    }
    }
   }
  
    

Validad que la estampa de tiempo no tenga un valor futuro


        
  {
    "rules": {
    "posts": {
    "$uid": {
    "timestamp": { 
    ".validate": "newData.val() <= now"
    }
    }
    }
    }
   }
  
    

Evita que se pueda eliminar o actualizar


        
  {
    "rules": {
    "posts": {
    "$uid": {
    ".write": "!data.exists()"
    }
    }
    }
   }
  
    

Evita que se pueda eliminar


        
  {
    "rules": {
    "posts": {
    "$uid": {
    ".write": "newData.exists()"
    }
    }
    }
   }
  
    

        
{
  "rules": {
  "posts": {
  "$uid": {
  ".write": "!data.exists() || !newData.exists()"
  }
  }
  }
 }

    

Evita crear y borrar


        
{
  "rules": {
  "posts": {
  "$uid": {
  ".write": "data.exists() && newData.exists()"
  }
  }
  }
 }

    

Saludos 👋

Servicios

Software

IoT

Digitalización

Aplicaciones móviles

Consultoría

fjmduran.com v0.1.2