1. Seguridad
Realizada por:
Christian Aquino |@cj_aquino
Diego Ramirez |@thedarsideofit
Gonzalo Alonso |@GonzaloAlonsoD
Diego Barros |@Inmzombie
Para: Hydras C&S |@hydras_cs
Basada en Libro Symfony 2 en español Nacho Pacheco y The Book
5. Autenticación HTTP
● Hay dos usuarios en el sistema (ryan y admin);
● Los usuarios se autentican a través de la autenticación
HTTP básica del sistema;
● Cualquier URL que coincida con /admin/* está
protegida, y sólo el usuario admin puede acceder a
ellas;
● Todas las URL que no coincidan con /admin/* son
accesibles para todos los usuarios (y nunca se pide al
usuario que se registre).
7. Cortafuegos (Autenticación)
El trabajo del cortafuegos es determinar si el usuario necesita
estar autenticado, y si lo hace, enviar una respuesta al usuario
para iniciar el proceso de autenticación.
En este ejemplo el patrón (^/) concordará con cada petición
entrante.
Cualquier usuario puede acceder a /foo sin que se le pida se
autentique.
Puesto que no hay rol especial necesario para acceder a /foo
(bajo la sección access_control), la petición se puede llevar a
cabo sin solicitar al usuario se autentique
Si eliminas la clave anonymous, el cortafuegos siempre hará
que un usuario se autentique inmediatamente.
9. Control de acceso (Autorización)
Si un usuario solicita /admin/foo, la configuración
access_control dice que cualquier URL coincidente con el
patrón de la expresión regular ^/admin (/admin o /admin/*)
requiere el rol ROLE_ADMIN.
Los roles son la base para la mayor parte de la
autorización: solo puede acceder a /admin/foo si cuenta
con el rol ROLE_ADMIN.
11. Control de acceso (Autorización)
El usuario ryan se autentica correctamente con el
cortafuegos. Pero como ryan no cuenta con el rol
ROLE_ADMIN, se le sigue negando el acceso a
/admin/foo. En última instancia, el usuario debe ver algún
tipo de mensaje indicándole que se le ha denegado el
acceso.
Cuando Symfony niega el acceso al usuario, él verá una
pantalla de error y recibe un código de estado HTTP 403
(Prohibido).
13. Control de acceso (Autorización)
Si admin solicita /admin/foo, después de haberse
autenticado, la capa de control de acceso le permitirá
pasar a través de la petición.
El flujo de la petición cuando un usuario solicita un recurso
protegido es sencillo. Independientemente del método de
autenticación, siempre es el mismo:
1. Un usuario accede a un recurso protegido
2. Se redirige al usuario al formulario de acceso
3. El usuario presenta sus credenciales
(usuario/contraseña)
4. El cortafuegos autentica al usuario
5. El nuevo usuario autenticado intenta de nuevo la
petición original.
14. Usando un formulario de acceso
tradicional
activa el formulario de acceso en el cortafuegos:
# app/config/security.yml
security:
firewalls:
secured_area:
pattern: ^/
anonymous: ~
form_login:
login_path: login
check_path: login_check
15. Usando un formulario de acceso
tradicional
Si no necesitas personalizar tus valores login_path o
check_path (los valores utilizados aquí son los
valores predeterminados), puedes acortar tu
configuración:
form_login: ~
18. Controlador
// obtiene el error de inicio de sesión si lo hay
if ($request->attributes->has(
SecurityContext::AUTHENTICATION_ERROR)) {
$error = $request->attributes->get(
SecurityContext::AUTHENTICATION_ERROR
);
} else {
$error = $session->get(
SecurityContext::AUTHENTICATION_ERROR);
$session->remove(
SecurityContext::AUTHENTICATION_ERROR);
20. Controlador
El trabajo es mostrar el formulario al usuario y los errores
de ingreso que puedan haber ocurrido, el propio sistema de
seguridad se encarga de verificar el nombre de usuario y
contraseña y la autenticación del usuario.
22. Plantilla
Requisitos:
Presentando el formulario a /login_check (a través de la ruta
login_check), el sistema de seguridad debe interceptar el envío
del formulario y procesarlo automáticamente.
El sistema de seguridad espera que los campos presentados se
llamen _username y _password.
Proceso:
1. Intenta acceder
2. El cortafuegos inicia la autenticación (/login)
3. /login reproduce el formulario
4. El usuario envía el formulario de acceso a /login_check
5. El sistema de seguridad intercepta la petición, comprueba
las credenciales, autentica al usuario si todo está correcto, y
si no, envía al usuario de nuevo al formulario de acceso.
23. Evitando errores comunes
1. Crea las rutas correctas
2. Asegúrate de que la página de inicio de sesión no
esté protegida
3. Asegúrate de que /login_check está detrás de un
cortafuegos
4. Múltiples cortafuegos no comparten el contexto de
seguridad
24. Autorizando
Una vez que el usuario se ha autenticado, comienza la
autorización. La autorización proporciona una forma
estándar y potente para decidir si un usuario puede
acceder a algún recurso (una URL, un modelo de objetos,
una llamada a un método, ...). Esto funciona asignando
roles específicos a cada usuario y, a continuación,
requiriendo diferentes roles para diferentes recursos.
26. Entendiendo como trabaja
access_control
Cada access_control tiene varias opciones que configuran
dos diferentes cosas: (a) la petición entrante emparejada
debe tener esta entrada de control de acceso y (b) una vez
emparejada, debe tener algún tipo de restricción de acceso
aplicable:
# app/config/security.yml
security:
# ...
access_control:
- { path: ^/admin/users, roles: ROLE_SUPER_ADMIN }
- { path: ^/admin, roles: ROLE_ADMIN }
27. Emparejando opciones
Symfony2 crea una instancia de
SymfonyComponentHttpFoundationRequestMatcher para
cada entrada access_control.
Las siguientes opciones de access_control se utilizan para
emparejar:
● path
● ip
● host
● methods
32. Forzando el acceso
Al decidir que entrada access_control concuerda (si la hay),
aplica las restricciones de acceso basándose en las opciones
roles y requires_channel:
● role Si el usuario no tiene determinado rol o roles, entonces
el acceso es denegado (internamente, se lanza una
SymfonyComponentSecurityCoreExceptionAccessDenied
Exception);
● requires_channel Si el canal de la petición entrante (p. ej.
http) no concuerda con este valor (p. ej. https), el usuario
será redirigido (p. ej. redirigido de http a https, o viceversa).
Si el acceso es denegado, el sistema intentará autenticar al
usuario si aún no lo está. Si el usuario ya inició sesión, se
mostrará la página del error 403 «acceso denegado».
35. Protegiendo un controlador
// ...
use SymfonyComponentSecurityCoreExceptionAccessDeniedException;
public function helloAction($name)
{
if (false === $this->get('security.context')
->isGranted('ROLE_ADMIN')) {
throw new AccessDeniedException();
}
// ...
}
36. Protegiendo un controlador usando
anotaciones
// ...
use JMSSecurityExtraBundleAnnotationSecure;
/**
* @Secure(roles="ROLE_ADMIN")
*/
public function helloAction($name)
{
// ...
}
37. Usuarios
De donde provienen los usuarios? (Proveedores de usuarios)
El usuario envía un conjunto de credenciales (username y
password). El trabajo del sistema de autenticación es
concordar esas credenciales contra una piscina de
usuarios.
Los usuarios pueden venir de cualquier parte. Todo lo que
proporcione usuarios a la autenticación se llama
«proveedor de usuario».
Proveedores de usuario más comunes:
Carga usuarios de un archivo de configuración.
Carga usuarios de una tabla de la base de datos.
38. Especificando usuarios en un
archivo de configuración (en memoria)
# app/config/security.yml
security:
# ...
providers:
default_provider:
memory:
users:
ryan: {
password: ryanpass, roles: 'ROLE_USER' }
admin: {
password: kitten, roles: 'ROLE_ADMIN' }
39. Especificando usuarios en un
archivo de configuración (en memoria)
prudencia nombre de usuario utilizar la sintaxis alterna
users:
- { name: 77, password: pass, roles: 'ROLE_USER' }
- { name: user-name, password: pass, roles:
'ROLE_USER' }
40. Cargando usuarios de la base de
datos
Si deseas cargar tus usuarios a través del ORM de
Doctrine, lo puedes hacer creando una clase User y
configurando el proveedor entity.
43. Creando la entidad usuario
$ php app/console doctrine:generate:entity
The Entity shortcut name: UsuarioBundle:Usuario
Configuration format (yml, xml, php, or annotation) [annotation]:
New field name (): nombre
Field type [string]: <Enter>
New field name (): apellidos
Field type [string]: <Enter>
New field name (): email
Field type [string]: <Enter>
44. New field name (): password
Field type [string]: <Enter>
New field name (): salt
Field type [string]: <Enter>
New field name (): <Enter>
Do you want to generate an empty repository class [no]? no
Do you confirm generation [yes]? yes
45. Creando el proveedor de usuarios
// src/Clase/UsuarioBundle/Entity/Usuario.php
namespace ClaseUsuarioBundleEntity;
use SymfonyComponentSecurityCoreUserUserInterface;
use DoctrineORMMapping as ORM;
/**
* CuponUsuarioBundleEntityUsuario
*
* @ORMEntity
*/
class Usuario implements UserInterface
{
...
}
47. Implementar UserInterface
• eraseCredentials(), se invoca cuando la aplicación
necesita borrar la información más sensible del usuario,
como por ejemplo su contraseña.
• getPassword(), se invoca cada vez que la aplicación
necesita obtener la contraseña del usuario.
• getRoles(), cuando se autentica a un usuario, se invoca
este método para obtener un array con todos los roles que
posee.
• getSalt(), devuelve el valor que se utilizó para aleatorizar
la contraseña cuando se creó el usuario.
• getUsername(), se invoca para obtener el login o nombre
de usuario que se utiliza para autenticar a los usuarios.
48. Añadiendo el formulario de login
usuario_login:
pattern: /usuario/login
defaults: { _controller: UsuarioBundle:Default:login }
usuario_login_check:
pattern: /usuario/login_check
usuario_logout:
pattern: /usuario/logout
49. Añadiendo el formulario de login
• /login, se utiliza para mostrar el formulario de login.
• /login_check, es la acción que comprueba que el usuario
y contraseña introducidos son correctos.
• /logout, se emplea para desconectar al usuario logueado.
58. Formulario registro
// src/Clase/UsuarioBundle/Form/Frontend/UsuarioType.php
namespace ClaseUsuarioBundleFormFrontend;
use SymfonyComponentFormAbstractType;
use SymfonyComponentFormFormBuilderInterface;
use SymfonyComponentOptionsResolverOptionsResolverInterface;
class UsuarioType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder,
array $options)
{
61. Controlador registro procesar form
public function registroAction()
{
$peticion = $this->getRequest();
$usuario = new Usuario();
$formulario = $this->createForm(new UsuarioType(),
$usuario);
if ($peticion->getMethod() == 'POST') {
$formulario->bind($peticion);
if ($formulario->isValid()) {
$encoder = $this->get('security.encoder_factory')
->getEncoder($usuario);