Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Symfony Security Demystified

Sponsored · Your Podcast. Everywhere. Effortlessly. Share. Educate. Inspire. Entertain. You do you. We'll handle the rest.

Symfony Security Demystified

Avatar for Marco Petersen

Marco Petersen

July 31, 2019
Tweet

More Decks by Marco Petersen

Other Decks in Programming

Transcript

  1. The Game Plan → authentication concepts → authorization concepts →

    how they're used in a framework project Example Project https://github.com/ocrampete16/symfony-security- demystified-talk
  2. User Providers load a user from a source $userProvider =

    new InMemoryUserProvider([ 'marco' => [ 'password' => 'p4$$w0rd', 'roles' => ['ROLE_USER'], ], ]); $user = $userProvider->loadUserByUsername('marco');
  3. User Providers load a user from a source $userProvider =

    new InMemoryUserProvider([ 'marco' => [ 'password' => 'p4$$w0rd', 'roles' => ['ROLE_USER'], ], ]); $user = $userProvider->loadUserByUsername('marco');
  4. The User Checker ensures that the user meets certain requirements

    $userChecker = new UserChecker(); $userChecker->checkPreAuth($user); // authenticate user here $userChecker->checkPostAuth($user);
  5. The User Checker ensures that the user meets certain requirements

    $userChecker = new UserChecker(); $userChecker->checkPreAuth($user); // authenticate user here $userChecker->checkPostAuth($user);
  6. The User Checker ensures that the user meets certain requirements

    $userChecker = new UserChecker(); $userChecker->checkPreAuth($user); // authenticate user here $userChecker->checkPostAuth($user);
  7. The User Checker ensures that the user meets certain requirements

    $userChecker = new UserChecker(); $userChecker->checkPreAuth($user); // authenticate user here $userChecker->checkPostAuth($user);
  8. What does the default User Checker do? The User Checker

    does not do anything by default. Implement your own to check for things like: → disabled accounts → accounts past the trial period → expired credentials
  9. Password Encoders encode passwords $encoderFactory = new EncoderFactory([ User::class =>

    new PlaintextPasswordEncoder(), ]); $encoder = $encoderFactory->getEncoder($user); $hash = $encoder->encodePassword($plaintext, $salt);
  10. Password Encoders encode passwords $encoderFactory = new EncoderFactory([ User::class =>

    new PlaintextPasswordEncoder(), ]); $encoder = $encoderFactory->getEncoder($user); $hash = $encoder->encodePassword($plaintext, $salt);
  11. const PROVIDER_KEY = 'default'; $unauthenticatedToken = new UsernamePasswordToken('marco', 'p4$$w0rd', PROVIDER_KEY);

    $authenticationManager = new AuthenticationProviderManager([ new DaoAuthenticationProvider( $userProvider, $userChecker, PROVIDER_KEY, $encoderFactory ), ]); $authenticatedToken = $authenticationManager->authenticate($unauthenticatedToken);
  12. const PROVIDER_KEY = 'default'; $unauthenticatedToken = new UsernamePasswordToken('marco', 'p4$$w0rd', PROVIDER_KEY);

    $authenticationManager = new AuthenticationProviderManager([ new DaoAuthenticationProvider( $userProvider, $userChecker, PROVIDER_KEY, $encoderFactory ), ]); $authenticatedToken = $authenticationManager->authenticate($unauthenticatedToken);
  13. const PROVIDER_KEY = 'default'; $unauthenticatedToken = new UsernamePasswordToken('marco', 'p4$$w0rd', PROVIDER_KEY);

    $authenticationManager = new AuthenticationProviderManager([ new DaoAuthenticationProvider( $userProvider, $userChecker, PROVIDER_KEY, $encoderFactory ), ]); $authenticatedToken = $authenticationManager->authenticate($unauthenticatedToken);
  14. const PROVIDER_KEY = 'default'; $unauthenticatedToken = new UsernamePasswordToken('marco', 'p4$$w0rd', PROVIDER_KEY);

    $authenticationManager = new AuthenticationProviderManager([ new DaoAuthenticationProvider( $userProvider, $userChecker, PROVIDER_KEY, $encoderFactory ), ]); $authenticatedToken = $authenticationManager->authenticate($unauthenticatedToken);
  15. const PROVIDER_KEY = 'default'; $unauthenticatedToken = new UsernamePasswordToken('marco', 'p4$$w0rd', PROVIDER_KEY);

    $authenticationManager = new AuthenticationProviderManager([ new DaoAuthenticationProvider( $userProvider, $userChecker, PROVIDER_KEY, $encoderFactory ), ]); $authenticatedToken = $authenticationManager->authenticate($unauthenticatedToken);
  16. Symfony uses the same class for authenticated and unauthenticated tokens

    // UserPasswordToken.php public function __construct( $user, $credentials, string $providerKey, array $roles = [] ) { // ... parent::setAuthenticated(\count($roles) > 0); }
  17. Symfony uses the same class for authenticated and unauthenticated tokens

    // UserPasswordToken.php public function __construct( $user, $credentials, string $providerKey, array $roles = [] ) { // ... parent::setAuthenticated(\count($roles) > 0); }
  18. $userProvider = new InMemoryUserProvider([ 'marco' => [ 'password' => 'p4$$w0rd',

    'roles' => [], ], ]); // ... // $token is unauthenticated! $token = $authenticationManager->authenticate($unauthenticatedToken);
  19. $userProvider = new InMemoryUserProvider([ 'marco' => [ 'password' => 'p4$$w0rd',

    'roles' => [], ], ]); // ... // $token is unauthenticated! $token = $authenticationManager->authenticate($unauthenticatedToken);
  20. $userProvider = new InMemoryUserProvider([ 'marco' => [ 'password' => 'p4$$w0rd',

    'roles' => [], ], ]); // ... // $token is unauthenticated! $token = $authenticationManager->authenticate($unauthenticatedToken);
  21. Security voters vote whether an authenticated user can perform an

    action on a subject $voter = new BlogPostVoter(); // returns VoterInterface::ACCESS_GRANTED, // VoterInterface::ACCESS_ABSTAIN or VoterInterface::ACCESS_DENIED $vote = $voter->vote($token, $blogPost, ['edit']);
  22. Security voters vote whether an authenticated user can perform an

    action on a subject $voter = new BlogPostVoter(); // returns VoterInterface::ACCESS_GRANTED, // VoterInterface::ACCESS_ABSTAIN or VoterInterface::ACCESS_DENIED $vote = $voter->vote($token, $blogPost, ['edit']);
  23. The framework also uses Security Voters to check for roles

    and authentication states $roleVoter = new RoleVoter('ROLE_'); $authenticatedVoter = new AuthenticatedVoter( $authenticationTrustResolver ); // Does the user have the role `ROLE_USER`? $roleVoter->vote($token, null, ['ROLE_USER']); // Is the user logged in? $authenticatedVoter->vote($token, null, ['IS_AUTHENTICATED_FULLY']);
  24. The framework also uses Security Voters to check for roles

    and authentication states $roleVoter = new RoleVoter('ROLE_'); $authenticatedVoter = new AuthenticatedVoter( $authenticationTrustResolver ); // Does the user have the role `ROLE_USER`? $roleVoter->vote($token, null, ['ROLE_USER']); // Is the user logged in? $authenticatedVoter->vote($token, null, ['IS_AUTHENTICATED_FULLY']);
  25. The framework also uses Security Voters to check for roles

    and authentication states $roleVoter = new RoleVoter('ROLE_'); $authenticatedVoter = new AuthenticatedVoter( $authenticationTrustResolver ); // Does the user have the role `ROLE_USER`? $roleVoter->vote($token, null, ['ROLE_USER']); // Is the user logged in? $authenticatedVoter->vote($token, null, ['IS_AUTHENTICATED_FULLY']);
  26. Putting it all together $accessDecisionManager = new AccessDecisionManager([ new RoleVoter(),

    new BlogPostVoter(), ]); $hasRole = $accessDecisionManager->decide( $authenticatedToken, ['ROLE_USER'] ); $canEditBlogPost = $accessDecisionManager->decide( $authenticatedToken, ['edit'], $blogPost );
  27. Putting it all together $accessDecisionManager = new AccessDecisionManager([ new RoleVoter(),

    new BlogPostVoter(), ]); $hasRole = $accessDecisionManager->decide( $authenticatedToken, ['ROLE_USER'] ); $canEditBlogPost = $accessDecisionManager->decide( $authenticatedToken, ['edit'], $blogPost );
  28. Putting it all together $accessDecisionManager = new AccessDecisionManager([ new RoleVoter(),

    new BlogPostVoter(), ]); $hasRole = $accessDecisionManager->decide( $authenticatedToken, ['ROLE_USER'] ); $canEditBlogPost = $accessDecisionManager->decide( $authenticatedToken, ['edit'], $blogPost );
  29. Putting it all together $accessDecisionManager = new AccessDecisionManager([ new RoleVoter(),

    new BlogPostVoter(), ]); $hasRole = $accessDecisionManager->decide( $authenticatedToken, ['ROLE_USER'] ); $canEditBlogPost = $accessDecisionManager->decide( $authenticatedToken, ['edit'], $blogPost );
  30. A simple security configuration security: providers: database_users: entity: { class:

    App\Entity\User, property: username } encoders: App\Entity\User: 'bcrypt' firewalls: main: anonymous: true form_login: true access_control: - { path: ^/profile, roles: ROLE_USER }
  31. framework providers: database_users: entity: { class: App\Entity\User, property: username }

    standalone $userProvider = new EntityUserProvider( $registry, App\Entity\User::class, 'username' );
  32. framework firewalls: main: anonymous: true form_login: true standalone $authenticationManager =

    new AuthenticationProviderManager([ new AnonymousAuthenticationProvider($secret), new DaoAuthenticationProvider( $userProvider, $userChecker, PROVIDER_KEY, $encoderFactory ), ]);
  33. framework access_control: - { path: ^/profile, roles: ROLE_USER } standalone

    $accessDecisionManager = new AccessDecisionManager([ new RoleVoter(), ]); // if request matches ^/profile $granted = $accessDecisionManager->decide( $authenticatedToken, ['ROLE_USER'] );
  34. The security workflow is comprised of two phases. Authentication asks

    "Who are you?" Authorization asks "Are you allowed to do that?"
  35. User providers load users from a source. User checkers check

    loaded users. Password encoders encode passwords.
  36. Security voters vote to grant or deny access (or abstain).

    Symfony uses Security Voters to check roles and authentication states.