Seguridad en una aplicación Symfony
Descripción general y conceptos de seguridad
1. Los desafíos de la seguridad de las aplicaciones web
A medida que se añaden ciertas funcionalidades cuando se desarrolla un sitio web, como una zona de «miembros» o una de administración, la cuestión de la seguridad se convierte rápidamente en una preocupación importante.
Symfony divide en dos etapas el proceso por el que el acceso a un recurso está restringido a un número limitado de usuarios: autenticación y autorización.
La primera etapa, la autenticación, consiste en identificar a un usuario. Una vez identificado el usuario, la aplicación se encarga de comprobar si el usuario está autorizado para acceder al recurso o para realizar la acción solicitada.
A nivel del protocolo HTTP, estos dos conceptos se ilustran en particular por los estados 401 y 403. El estado 401 significa que se requiere autenticación para acceder al recurso. Por otro lado, el estado 403 indica que el servidor ha identificado correctamente al usuario, pero este no está autorizado para acceder al recurso.
2. Seguridad en Symfony
SecurityBundle gestiona la seguridad. Está integrado en la aplicación de forma predeterminada si ha creado el proyecto a partir del esqueleto de la aplicación web. De lo contrario, es posible añadirlo con la siguiente recipe:
composer require symfony/security-bundle
Las directivas...
Autenticación
1. Cortafuegos
La función del firewall o cortafuegos consiste en definir las zonas de la aplicación sujetas al sistema de seguridad.
Cuando la petición de un usuario coincide con las reglas definidas por un firewall, se habilita la seguridad. Por lo tanto, la petición debe pasar a través de este firewall y satisfacer las restricciones en términos de permisos especificadas en él.
Una petición se asocia a un firewall en función de su ruta (path). Volvamos a la sección firewalls de nuestra configuración básica:
# config/packages/security.yaml
security:
# ...
firewalls:
mi_firewall:
pattern: ^/
anonymous: ~
Aquí definimos un firewall llamado mi_firewall. La directiva pattern contiene la expresión regular que se aplicará a la ruta de la consulta para determinar si debe pasar a través del firewall o no. Dado que todas las rutas de acceso deben comenzar con un slash, la expresión regular anterior hace que todas las peticiones pasen por el firewall mi_firewall.
La directiva anonymous establece que los usuarios anónimos (no autenticados) pueden acceder a este firewall. Por lo tanto, la aplicación no es segura; todas las páginas son de libre acceso.
Firewall para un subdominio
Además del path, el firewall puede analizar el nombre de dominio de la consulta. Esto se hace con la directiva host:
host:
pattern: ^/
host: mi-sección\.ejemplo\.com
http_basic: ~
Aquí, el nombre de dominio mi-sección.ejemplo.com requiere autenticación HTTP. Volveremos sobre esta técnica más adelante en este capítulo.
Firewall para recursos estáticos/de desarrollo
Algunas rutas siempre deben ser accesibles. En particular, este es el caso de los recursos estáticos (imágenes, hojas de estilo, etc.) o las URL utilizadas internamente por Symfony (como la barra de depuración del Profiler).
Para evitar proteger inadvertidamente este tipo de recurso, le recomendamos...
Usuarios y roles
1. El usuario
Con Symfony, el objeto que representa a un usuario debe implementar obligatoriamente la interfaz Symfony\Component\Security\Core\User\UserInterface, que proporciona los siguientes métodos:
-
getUsername(), que devuelve el nombre de usuario.
-
getPassword(), que devuelve la contraseña del usuario.
-
getSalt(), que devuelve el valor del salt de la contraseña (volveremos más adelante en este capítulo sobre este concepto, con los encoders).
-
getRoles(), que devuelve los roles del usuario. Estos roles afectan al nivel de permisos de cada miembro. En la práctica, permiten, por ejemplo, decir si el miembro es un administrador, un moderador o un simple usuario.
-
eraseCredentials(), que el framework utiliza internamente. Este método borra la información confidencial (si la hay) contenida en el objeto usuario. Es útil si, por ejemplo, en un momento dado la contraseña se almacena en formato de texto abierto en el objeto.
Dependiendo del proveedor de usuarios que elija o cree, los objetos que representan a los usuarios pueden implementar una clase más completa que UserInterface: Symfony\Component\Security\Core\User\AdvancedUserInterface. El objetivo de este último es admitir las características de suspensión y expiración de las cuentas de usuario.
a. Recuperar el usuario actual
Para recuperar el usuario autenticado actualmente, dispone del acceso directo getUser() en sus controladores:
<?php
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
class DefaultController extends AbstractController
{
/**
* @Route("/")
*/
public function index()
{
$usuario = $this->getUser();
// ...
}
}
Si el usuario está autenticado, el método devuelve...
Permisos
Hasta ahora, hemos aprendido cómo autenticar a los usuarios a través de diferentes métodos y diferentes proveedores de usuario. Conocemos la representación de un usuario y sabemos que tiene roles. Ahora veremos cómo asignar seguridad a los recursos.
1. Los roles en el centro del proceso
El principio de autorización es sencillo: los usuarios tienen roles y el acceso a ciertos recursos se puede limitar a ciertos roles. Por lo tanto, los roles son elementos fundamentales del proceso de autorización. Antes de continuar, es necesario que le presentemos nuevos roles.
Si un usuario está detrás de uno de los firewalls que ha definido (en config/packages/security.yaml), debe tener, al menos, uno de los roles siguientes:
-
IS_AUTHENTICATED_ANONYMOUSLY: el usuario no está autenticado (está detrás de un firewall que permite usuarios anónimos).
-
IS_AUTHENTICATED_REMEMBERED: el usuario se autentica a través de la función «recordarme».
-
IS_AUTHENTICATED_FULLY: el usuario se ha autenticado en la petición o sesión actual.
No tiene que gestionar estos roles en el método getRoles() de su objeto de usuario porque Symfony los genera automáticamente.
2. Comprobar el rol de usuario
Para asignar seguridad a cualquiera de sus acciones, nunca debe confiar en los roles devueltos por el objeto usuario. Por ejemplo, los roles que acabamos de presentar (IS_AUTHENTICATED_*) no están presentes a nivel del objeto usuario porque Symfony utiliza internamente un token para verificar una autorización.
A continuación, se indica cómo verificar que se ha concedido un rol determinado al usuario actual:
<?php
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
class DefaultController extends AbstractController
{
/**
* @Route("/")
*/...