Ce diaporama a bien été signalé.
Nous utilisons votre profil LinkedIn et vos données d’activité pour vous proposer des publicités personnalisées et pertinentes. Vous pouvez changer vos préférences de publicités à tout moment.

JWT - Sécurisez vos APIs

1 469 vues

Publié le

Les web-services sont aujourd'hui au centre des SI de nos entreprises. Ils permettent de transmettre l'information entre des systèmes hétérogènes, à la fois au sein de l’entreprise mais également à l'extérieur, notamment et de plus en plus vers des systèmes mobiles. Or exposer cette information, potentiellement critique, pose la question de la sécurisation de ces échanges. Cette présentation sera l'occasion de présenter JWT, d'expliquer son fonctionnement et son implémentation au sein d'un projet Symfony 3.

  • Soyez le premier à commenter

  • Soyez le premier à aimer ceci

JWT - Sécurisez vos APIs

  1. 1. Architecte technique chez depuis 2011 +5 ans d’expérience sur une quinzaine de projets Symfony2 de tous types
  2. 2. 1
  3. 3. Une API Web :  expose de l’information potentiellement critique  permet de manipuler cette information
  4. 4. Une API Web :  expose de l’information potentiellement critique  permet de manipuler cette information Edition Ajout Suppression
  5. 5. CRUCIAL de veiller à une sécurité accrue de chaque API
  6. 6. Une API Web :  expose de l’information potentiellement critique  permet de manipuler cette information  est normalement stateless  Pas de session  Appel isolé
  7. 7. Une API Web :  expose de l’information potentiellement critique  permet de manipuler cette information  est normalement stateless  Pas de session  Appel isolé
  8. 8. Une API Web :  expose de l’information potentiellement critique  permet de manipuler cette information  est normalement stateless  Pas de session  Appel isolé  Authentification à chaque appel
  9. 9. Une API Web :  expose de l’information potentiellement critique  permet de manipuler cette information  est normalement stateless  doit être utilisée en HTTPS
  10. 10.  Authentification basée sur la session
  11. 11. Inconvénients  CORS (Cross-origin resource sharing)  Évolutivité
  12. 12.  Authentification basée sur les clefs d’API Pas de session
  13. 13.  Authentification basée sur les clefs d’API Pas de session Gestion des clefs en bdd 1 andre … z654df84sSdDLfs3 2 amine … Ohg2v5x6df2fFspoa1fdffds8 3 antoine … khHp5se8w2xf1t9823tz3
  14. 14.  Authentification basée sur les clefs d’API Pas de session Gestion des clefs en bdd Pas de mécanisme d’expiration
  15. 15.  Authentification basée sur les clefs d’API Pas de session Gestion des clefs en bdd Pas de mécanisme d’expiration Token non exploitable
  16. 16. Solution idéale :  Stateless  Gestion de l’expiration  Auto-porteuse et sécurisée
  17. 17. 2
  18. 18.  Standard industriel qui repose sur une RFC (7519)  Permet de fournir un mécanisme d’authentification fiable  Repose sur un token qui va contenir les données  Token sécurisé o JWS (RFC 7515) o JWE (RFC 7516)  Fournit un système d’expiration
  19. 19.  Liste des propriétés réservées : Nom: sub Description: Subject Nom: exp Description: Expiration Time Nom: nbf Description: Not Before Nom: aud Description: Audience Nom: iss Description: Issuer Nom: iat Description: Issued At Nom: jti Description: JWT ID
  20. 20. JOSE : Javascript Object Signing and Encryption HMAC + SHA RSA + SHA ECDSA + SHA
  21. 21.  Implémentation disponible pour la grande majorité des langages de développement
  22. 22. Etape 1 :  L’utilisateur va s’authentifier sur l’API  En cas d’authentification réussie, le serveur génère et renvoie un token JWT à l’application
  23. 23. Etape 2 à N :  L’application transmet le token JWT pour chaque transaction suivante en header des requêtes
  24. 24. Quelle durée choisir ?  Pas de durée type  En moyenne : entre 5 min et 1 heure  Délai expiré :
  25. 25. Utilisation de Refresh token
  26. 26. 3
  27. 27. namespace SymfonyComponentSecurityGuard; abstract class AbstractGuardAuthenticator { public function createAuthenticatedToken(UserInterface $user, $providerKey); }
  28. 28. namespace SymfonyComponentSecurityGuard; abstract class AbstractGuardAuthenticator implements GuardAuthenticatorInterface { public function createAuthenticatedToken(UserInterface $user, $providerKey); }
  29. 29. namespace SymfonyComponentSecurityGuard; interface GuardAuthenticatorInterface { public function getCredentials(Request $request); public function getUser($credentials, UserProviderInterface $userProvider); public function checkCredentials($credentials, UserInterface $user); public function createAuthenticatedToken(UserInterface $user, $providerKey); public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey); public function onAuthenticationFailure(Request $request, AuthenticationException $exception); public function supportsRememberMe(); }
  30. 30. namespace SymfonyComponentSecurityGuard; interface GuardAuthenticatorInterface extends AuthenticationEntryPointInterface { public function getCredentials(Request $request); public function getUser($credentials, UserProviderInterface $userProvider); public function checkCredentials($credentials, UserInterface $user); public function createAuthenticatedToken(UserInterface $user, $providerKey); public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey); public function onAuthenticationFailure(Request $request, AuthenticationException $exception); public function supportsRememberMe(); }
  31. 31. namespace SymfonyComponentSecurityHttpEntryPoint; interface AuthenticationEntryPointInterface { public function start(Request $request, AuthenticationException $authException = null); }
  32. 32. #app/config/security.yml security: encoders: SymfonyComponentSecurityCoreUserUserInterface: plaintext providers: in_memory: memory: users: andre: password: I_<3_Webnet roles: ROLE_ADMIN
  33. 33. #app/config/security.yml security: firewalls: login: pattern: ^/api/login stateless: true anonymous: true provider: in_memory form_login: check_path: /api/login_check success_handler: webnet_authentication.handler.authentication_success failure_handler: webnet_authentication.handler.authentication_failure require_previous_session: false use_referer: true access_control: - { path: ^/api/login, roles: IS_AUTHENTICATED_ANONYMOUSLY }
  34. 34. # app/config/service.yml services: webnet_authentication.handler.authentication_success: class: AppBundleSecurityAuthenticationSuccessHandler arguments: [] webnet_authentication.handler.authentication_failure: class: AppBundleSecurityAuthenticationFailureHandler arguments: []
  35. 35. /** * Class AuthenticationFailureHandler * * @package AppBundleSecurity */ class AuthenticationFailureHandler implements AuthenticationFailureHandlerInterface { /** * {@inheritdoc} */ public function onAuthenticationFailure(Request $request, AuthenticationException $exception) { $data = array( 'message' => strtr($exception->getMessageKey(), $exception->getMessageData()) ); return new JsonResponse($data, Response::HTTP_FORBIDDEN); } }
  36. 36. /** * Class AuthenticationSuccessHandler * * @package AppBundleSecurity */ class AuthenticationSuccessHandler implements AuthenticationSuccessHandlerInterface { /** * @inheritdoc */ public function onAuthenticationSuccess(Request $request, TokenInterface $token) { return $this->handleAuthenticationSuccess($token->getUser()); } }
  37. 37. /** * Class AuthenticationSuccessHandler * @package AppBundleSecurity */ class AuthenticationSuccessHandler implements AuthenticationSuccessHandlerInterface { const SSL_KEY_PASSPHRASE = 'tests'; public function onAuthenticationSuccess(Request $request, TokenInterface $token) { return $this->handleAuthenticationSuccess($token->getUser()); } public function handleAuthenticationSuccess(UserInterface $user) { $jws = new SimpleJWS(array('alg' => 'RS256')); $jws->setPayload(array('sub' => $user->getUsername(), 'exp' => time() + 3600)); $privateKey = openssl_pkey_get_private("file://path_to_private.key", self::SSL_KEY_PASSPHRASE); $jws->sign($privateKey); return new JsonResponse(array('token' => $jws->getTokenString())); } }
  38. 38. # app/config/services.yml services: app.token_authenticator: class: AppBundleSecurityWebnetTokenAuthenticator
  39. 39. /** * Class WebnetAuthenticator * * @package AppBundleSecurity */ class WebnetAuthenticator extends AbstractGuardAuthenticator { }
  40. 40. /** * Class WebnetAuthenticator * * @package AppBundleSecurity */ class WebnetAuthenticator extends AbstractGuardAuthenticator { /** * @inheritdoc */ public function getCredentials(Request $request) { if (!$tokenValue = $request->headers->get('Authorization')) { // no token? Return null and no other methods will be called return; } $token = explode(' ', $tokenValue); try { return ['token' => SimpleJWS::load($token[1])]; } catch (Exception $e) { return; } } }
  41. 41. /** * Class WebnetAuthenticator * * @package AppBundleSecurity */ class WebnetAuthenticator extends AbstractGuardAuthenticator { /** * @inheritdoc */ public function start(Request $request, AuthenticationException $authException = null) { $data = array('message' => 'Authentication Required'); return new JsonResponse($data, Response::HTTP_UNAUTHORIZED); } }
  42. 42. /** * Class WebnetAuthenticator * * @package AppBundleSecurity */ class WebnetAuthenticator extends AbstractGuardAuthenticator { /** * @inheritdoc */ public function getCredentials(Request $request) { if (!$tokenValue = $request->headers->get('Authorization')) { // no token? Return null and no other methods will be called return; } $token = explode(' ', $tokenValue); try { return ['token' => SimpleJWS::load($token[1])]; } catch (Exception $e) { return; } } }
  43. 43. /** * Class WebnetAuthenticator * * @package AppBundleSecurity */ class WebnetAuthenticator extends AbstractGuardAuthenticator { /** * @inheritdoc */ public function getUser($credentials, UserProviderInterface $userProvider) { $payload = $credentials['token']->getPayload(); if (!isset($payload['sub']) || !$payload['sub']) { return; } return $userProvider->loadUserByUsername($payload['sub']); } }
  44. 44. /** * Class WebnetAuthenticator * * @package AppBundleSecurity */ class WebnetAuthenticator extends AbstractGuardAuthenticator { /** * @inheritdoc */ public function checkCredentials($credentials, UserInterface $user) { $publicKey = openssl_pkey_get_public("file://path_to_public.key"); // verify that the token is valid (exp) and had the same values return $credentials['token']->isValid($publicKey, 'RS256'); } }
  45. 45. /** * Class WebnetAuthenticator * * @package AppBundleSecurity */ class WebnetAuthenticator extends AbstractGuardAuthenticator { /** * @inheritdoc */ public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey) { // on success, let the request continue return null; } /** * @inheritdoc */ public function onAuthenticationFailure(Request $request, AuthenticationException $exception) { $data = array( 'message' => strtr($exception->getMessageKey(), $exception->getMessageData()) ); return new JsonResponse($data, Response::HTTP_FORBIDDEN); } }
  46. 46. /** * Class WebnetAuthenticator * * @package AppBundleSecurity */ class WebnetAuthenticator extends AbstractGuardAuthenticator { public function supportsRememberMe() { return false; } }
  47. 47. « There’s a bundle for that ! » o lexik/LexikJWTAuthenticationBundle o gesdinet/JWTRefreshTokenBundle (refresh token)
  48. 48.

×