Clase 13 seguridad

426 views
323 views

Published on

www.hydrascs.com

0 Comments
0 Likes
Statistics
Notes
  • Be the first to comment

  • Be the first to like this

No Downloads
Views
Total views
426
On SlideShare
0
From Embeds
0
Number of Embeds
0
Actions
Shares
0
Downloads
5
Comments
0
Likes
0
Embeds 0
No embeds

No notes for slide

Clase 13 seguridad

  1. 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
  2. 2. Seguridad etapas
  3. 3. Autenticación HTTP security: firewalls: secured_area: pattern: ^/ anonymous: ~ http_basic: realm: "Secured Demo Area" access_control: - { path: ^/admin, roles: ROLE_ADMIN }
  4. 4. Autenticación HTTP providers: in_memory: memory: users: ryan: { password: ryanpass, roles: 'ROLE_USER' } admin: { password: kitten, roles: 'ROLE_ADMIN' } encoders: SymfonyComponentSecurityCoreUserUser: plaintext
  5. 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).
  6. 6. Cortafuegos (Autenticación)
  7. 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.
  8. 8. Control de acceso (Autorización)
  9. 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.
  10. 10. Control de acceso (Autorización)
  11. 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).
  12. 12. Control de acceso (Autorización)
  13. 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. 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. 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: ~
  16. 16. Configurar rutas # app/config/routing.yml login: pattern: /login defaults: { _controller: AcmeSecurityBundle: Security:login } login_check: pattern: /login_check
  17. 17. Controlador // src/Acme/SecurityBundle/Controller/SecurityController.php; namespace AcmeSecurityBundleController; use SymfonyBundleFrameworkBundleControllerController; use SymfonyComponentSecurityCoreSecurityContext; class SecurityController extends Controller { public function loginAction() { $request = $this->getRequest(); $session = $request->getSession();
  18. 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);
  19. 19. Controlador } return $this->render( 'AcmeSecurityBundle:Security:login.html.twig', array( // último nombre de usuario ingresado 'last_username' => $session->get( SecurityContext::LAST_USERNAME), 'error' => $error, ) ); } }
  20. 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.
  21. 21. Plantilla {% if error %} <div>{{ error.message }}</div> {% endif %} <form action="{{ path('login_check') }}" method="post"> <label for="username">Username:</label> <input type="text" id="username" name="_username" value="{{ last_username }}" /> <label for="password">Password:</label> <input type="password" id="password" name="_password" /> <button type="submit">login</button> </form>
  22. 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. 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. 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.
  25. 25. Protegiendo patrones de URL # app/config/security.yml security: # ... access_control: - { path: ^/admin/users, roles: ROLE_SUPER_ADMIN } - { path: ^/admin, roles: ROLE_ADMIN } ^/admin/users /admin/users /users/admin
  26. 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. 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
  28. 28. Emparejando opciones # app/config/security.yml security: # ... access_control: - { path: ^/admin, roles: ROLE_USER_IP, ip: 127.0.0.1 } - { path: ^/admin, roles: ROLE_USER_HOST, host: symfony.com } - { path: ^/admin, roles: ROLE_USER_METHOD, methods: [POST, PUT] } - { path: ^/admin, roles: ROLE_USER }
  29. 29. 1. { path: ^/admin, roles: ROLE_USER_IP, ip: 127.0.0.1 } 2. { path: ^/admin, roles: ROLE_USER_HOST, host: symfony.com } 3. { path: ^/admin, roles: ROLE_USER_METHOD, methods: [POST, PUT] } 4. { path: ^/admin, roles: ROLE_USER }
  30. 30. 1. { path: ^/admin, roles: ROLE_USER_IP, ip: 127.0.0.1 } 2. { path: ^/admin, roles: ROLE_USER_HOST, host: symfony.com } 3. { path: ^/admin, roles: ROLE_USER_METHOD, methods: [POST, PUT] } 4. { path: ^/admin, roles: ROLE_USER }
  31. 31. 1. { path: ^/admin, roles: ROLE_USER_IP, ip: 127.0.0.1 } 2. { path: ^/admin, roles: ROLE_USER_HOST, host: symfony.com } 3. { path: ^/admin, roles: ROLE_USER_METHOD, methods: [POST, PUT] } 4. { path: ^/admin, roles: ROLE_USER }
  32. 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».
  33. 33. Protegiendo por IP # app/config/security.yml security: # ... access_control: - { path: ^/esi, roles: IS_AUTHENTICATED_ANONYMOUSLY, ip: 127.0.0.1 } - { path: ^/esi, roles: ROLE_NO_ACCESS }
  34. 34. Protegiendo por canal # app/config/security.yml security: # ... access_control: - { path: ^/cart/checkout, roles: IS_AUTHENTICATED_ANONYMOUSLY, requires_channel: https }
  35. 35. Protegiendo un controlador // ... use SymfonyComponentSecurityCoreExceptionAccessDeniedException; public function helloAction($name) { if (false === $this->get('security.context') ->isGranted('ROLE_ADMIN')) { throw new AccessDeniedException(); } // ... }
  36. 36. Protegiendo un controlador usando anotaciones // ... use JMSSecurityExtraBundleAnnotationSecure; /** * @Secure(roles="ROLE_ADMIN") */ public function helloAction($name) { // ... }
  37. 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. 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. 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. 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.
  41. 41. Restringiendo el acceso # app/config/security.yml security: firewalls: frontend: pattern: ^/* anonymous: ~ form_login: ~ access_control: - { path: ^/usuario/login, roles: IS_AUTHENTICATED_ANONYMOUSLY }
  42. 42. Restringiendo el acceso - { path: ^/usuario/registro, roles: IS_AUTHENTICATED_ANONYMOUSLY } - { path: ^/usuario/*, roles: ROLE_USUARIO } providers: usuarios: entity: { class: ClaseUsuarioBundleEntityUsuario, property: email } encoders: ClaseUsuarioBundleEntityUsuario: { algorithm: sha512, iterations: 10 }
  43. 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. 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. 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 { ... }
  46. 46. // src/Clase/UsuarioBundle/Entity/Usuario.php class Usuario implements UserInterface { function eraseCredentials() {} function getRoles() { return array('ROLE_USUARIO'); } function getUsername() { return $this->getEmail(); } // ... }
  47. 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. 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. 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.
  50. 50. Actualizar ruta form_login # app/config/security.yml security: firewalls: frontend: pattern: ^/* anonymous: ~ form_login: login_path: /usuario/login check_path: /usuario/login_check login_path: usuario_login check_path: usuario_login_check
  51. 51. El controlador //src/Clase/UsuarioBundle/Controller/DefaultController.php namespace ClaseUsuarioBundleController; use SymfonyBundleFrameworkBundleControllerController; use SymfonyComponentSecurityCoreSecurityContext; class DefaultController extends Controller { public function loginAction() { $peticion = $this->getRequest(); $sesion = $peticion->getSession(); $error = $peticion->attributes->get(
  52. 52. El controlador SecurityContext::AUTHENTICATION_ERROR, $sesion->get(SecurityContext::AUTHENTICATION_ ERROR) ); return $this->render('UsuarioBundle:Default:login.html. twig', array( 'last_username' => $sesion->get(SecurityContext::LAST_USERNAME), 'error' => $error )); }}
  53. 53. La vista del formulario {# src/Clase/UsuarioBundle/Resources/views/Default/login.html.twig #} {% if error %} <div>{{ error.message }}</div> {% endif %} <form action="{{ path('usuario_login_check') }}" method="post"> <label for="username">Usuario:</label> <input type="text" id="username" name="_username" value="{{ last_username }}" /> <label for="password">Contraseña:</label> <input type="password" id="password" name="_password" /> <input type="submit" name="login" value="Acceder" /> </form>
  54. 54. Formulario de registro #routing.yml usuario_registro: pattern: /registro defaults: { _controller: UsuarioBundle:Default:registro }
  55. 55. Controlador registro // src/Clase/UsuarioBundle/Controller/DefaultController.php namespace ClaseUsuarioBundleController; use SymfonyBundleFrameworkBundleControllerController; use ClaseUsuarioBundleEntityUsuario; use ClaseUsuarioBundleFormFrontendUsuarioType; class DefaultController extends Controller { // ...
  56. 56. Controlador registro public function registroAction() { $usuario = new Usuario(); $formulario = $this->createForm(new UsuarioType(), $usuario); return $this->render( 'UsuarioBundle:Default:registro.html.twig', array('formulario' => $formulario->createView()) ); } }
  57. 57. Plantilla registro {# src/Clase/UsuarioBundle/Resources/views/Default/registro.html.twig #} <form action="{{ path('usuario_registro') }}" method="post" {{ form_enctype(formulario) }}> {{ form_widget(formulario) }} <input class="boton" type="submit" value="Registrarme" /> </form>
  58. 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) {
  59. 59. $builder ->add('nombre') ->add('apellidos') ->add('email', 'email') ->add('password', 'repeated', array( 'type' => 'password', 'invalid_message' => 'Las dos contraseñas deben coincidir', 'options' => array('label' => 'Contraseña') )) ; }
  60. 60. public function setDefaultOptions (OptionsResolverInterface $resolver) { $resolver->setDefaults(array( 'data_class' => 'ClaseUsuarioBundleEntityUsuario' )); } public function getName() { return 'clase_usuariobundle_usuariotype'; } }
  61. 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);
  62. 62. $usuario->setSalt(md5(time())); $passwordCodificado = $encoder->encodePassword( $usuario->getPassword(), $usuario->getSalt() ); $usuario->setPassword($passwordCodificado); $em = $this->getDoctrine()->getEntityManager(); $em->persist($usuario); $em->flush();
  63. 63. $token = new UsernamePasswordToken( $usuario, $usuario->getPassword(), 'usuarios', $usuario->getRoles() ); $this->container ->get('security.context')->setToken($token); return $this->redirect($this->generateUrl('url')); }
  64. 64. } return $this->render( 'UsuarioBundle:Default:registro.html.twig', array('formulario' => $formulario->createView()) ); } }

×