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

[SymfonyCon Paris] Dig in security

Sarah KHALIL
December 09, 2015

[SymfonyCon Paris] Dig in security

Security in Symfony is often misunderstood. Let's have a look at what is really happening behind the opaque configuration of the Security Bundle. Then we will see how it is possible to implement any way to authenticate a user in Symfony without third party library.

Sarah KHALIL

December 09, 2015
Tweet

More Decks by Sarah KHALIL

Other Decks in Technology

Transcript

  1. WHO AM I? • Head of • Trainer & Developer

    • Enjoying sharer Sarah Khalil @saro0h
  2. WHAT DO WE HAVE TO WRITE? security: encoders: Symfony\Component\Security\Core\User\User: bcrypt

    providers: in_memory: memory: users: sarah: password: $2a$12$LCY0M… roles: 'ROLE_USER' firewalls: admin: provider: in_memory pattern: /^admin form_login: login_path: /login check_path: /login_check
  3. AUTHENTICATION FLOW Firewall backend _username = ‘sarah.khalil’ _password = ‘mypwd’

    Provider $provider->loadUserByUsername('sarah.khalil'); $user = new User(‘sarah.khalil’); Encoder Factory $factory->getEncoder($user); $encoder $encoder->isPasswordValid('mypwd'); Encoder true authenticated false | 401: Unauthorized /admin/myprofile or
  4. AUTHENTICATION FLOW Firewall backend _username = ‘sarah.khalil’ _password = ‘mypwd’

    Provider $provider->loadUserByUsername('sarah.khalil'); $user = new User(‘sarah.khalil’); Encoder Factory $factory->getEncoder($user); $encoder $encoder->isPasswordValid('mypwd'); Encoder true authenticated false | 401: Unauthorized /admin/myprofile or
  5. Your application all routes begin with /admin all routes beginning

    with /shop all routes beginning with /blog all other routes…
  6. Your application all routes begin with /admin all routes beginning

    with /shop all routes beginning with /blog all other routes… The user needs to be authenticated to access this part of the app
  7. WHAT DO WE HAVE TO WRITE? security: firewalls: admin: provider:

    in_memory pattern: /^admin form_login: login_path: /login check_path: /login_check
  8. WHAT DO WE HAVE TO WRITE? security: firewalls: admin: provider:

    in_memory pattern: /^admin form_login: login_path: /login check_path: /login_check
  9. WHAT DO WE HAVE TO WRITE? security: firewalls: admin: provider:

    in_memory pattern: /^admin form_login: login_path: /login check_path: /login_check
  10. WHAT DO WE HAVE TO WRITE? security: firewalls: admin: provider:

    in_memory pattern: /^admin form_login: login_path: /login check_path: /login_check
  11. AUTHENTICATION FLOW Firewall backend _username = ‘sarah.khalil’ _password = ‘mypwd’

    Provider authenticated 401: Unauthorized /admin/myprofile or
  12. AUTHENTICATION FLOW Firewall backend _username = ‘sarah.khalil’ _password = ‘mypwd’

    Provider $provider->loadUserByUsername('sarah.khalil'); authenticated 401: Unauthorized /admin/myprofile or
  13. AUTHENTICATION FLOW Firewall backend _username = ‘sarah.khalil’ _password = ‘mypwd’

    Provider $provider->loadUserByUsername('sarah.khalil'); $user = new User(‘sarah.khalil’); authenticated 401: Unauthorized /admin/myprofile or
  14. AUTHENTICATION FLOW Firewall backend username = ‘sarah.khalil’ password = ‘mypwd’

    authenticated Provider $provider->loadUserByUsername('sarah.khalil'); $user = new User(‘sarah.khalil’); /admin/myprofile
  15. AUTHENTICATION FLOW Firewall backend username = ‘sarah.khalil’ password = ‘mypwd’

    Encoder Factory authenticated Provider $provider->loadUserByUsername('sarah.khalil'); $user = new User(‘sarah.khalil’); /admin/myprofile
  16. AUTHENTICATION FLOW Firewall backend username = ‘sarah.khalil’ password = ‘mypwd’

    Encoder Factory $factory->getEncoder($user); authenticated Provider $provider->loadUserByUsername('sarah.khalil'); $user = new User(‘sarah.khalil’); /admin/myprofile
  17. AUTHENTICATION FLOW Firewall backend username = ‘sarah.khalil’ password = ‘mypwd’

    Encoder Factory $factory->getEncoder($user); $encoder authenticated Provider $provider->loadUserByUsername('sarah.khalil'); $user = new User(‘sarah.khalil’); /admin/myprofile
  18. AUTHENTICATION FLOW Firewall backend username = ‘sarah.khalil’ password = ‘mypwd’

    Encoder Factory $factory->getEncoder($user); $encoder Encoder authenticated Provider $provider->loadUserByUsername('sarah.khalil'); $user = new User(‘sarah.khalil’); /admin/myprofile
  19. AUTHENTICATION FLOW Firewall backend username = ‘sarah.khalil’ password = ‘mypwd’

    Encoder Factory $factory->getEncoder($user); $encoder Encoder $encoder->isPasswordValid('mypwd'); authenticated Provider $provider->loadUserByUsername('sarah.khalil'); $user = new User(‘sarah.khalil’); /admin/myprofile
  20. AUTHENTICATION FLOW Firewall backend username = ‘sarah.khalil’ password = ‘mypwd’

    Encoder Factory $factory->getEncoder($user); $encoder Encoder $encoder->isPasswordValid('mypwd'); authenticated true Provider $provider->loadUserByUsername('sarah.khalil'); $user = new User(‘sarah.khalil’); /admin/myprofile
  21. AUTHENTICATION FLOW Firewall backend username = ‘sarah.khalil’ password = ‘mypwd’

    Encoder Factory $factory->getEncoder($user); $encoder Encoder $encoder->isPasswordValid('mypwd'); authenticated true Provider $provider->loadUserByUsername('sarah.khalil'); $user = new User(‘sarah.khalil’); /admin/myprofile false | 401: Unauthorized or
  22. interface PasswordEncoderInterface { /** * Encodes the raw password. */

    public function encodePassword($raw, $salt); /** * Checks a raw password against an encoded password. */ public function isPasswordValid($encoded, $raw, $salt); }
  23. security: encoders: Symfony\Component\Security\Core\User\User: bcrypt providers: in_memory: memory: users: sarah: password:

    $2a$12$LCY0M… roles: 'ROLE_USER' firewalls: admin: provider: in_memory pattern: /^admin form_login: login_path: /login check_path: /login_check
  24. AUTHENTICATION FLOW Firewall backend Provider $provider->loadUserByUsername('sarah.khalil'); $user = new User(‘sarah.khalil’);

    Encoder Factory _username = ‘sarah.khalil’ _password = ‘mypwd’ /admin/myprofile
  25. AUTHENTICATION FLOW Firewall backend Provider $provider->loadUserByUsername('sarah.khalil'); $user = new User(‘sarah.khalil’);

    Encoder Factory $factory->getEncoder($user); _username = ‘sarah.khalil’ _password = ‘mypwd’ /admin/myprofile
  26. AUTHENTICATION FLOW Firewall backend Provider $provider->loadUserByUsername('sarah.khalil'); $user = new User(‘sarah.khalil’);

    Encoder Factory $factory->getEncoder($user); $encoder _username = ‘sarah.khalil’ _password = ‘mypwd’ /admin/myprofile
  27. AUTHENTICATION FLOW Firewall backend Provider $provider->loadUserByUsername('sarah.khalil'); $user = new User(‘sarah.khalil’);

    Encoder Factory $factory->getEncoder($user); $encoder Encoder _username = ‘sarah.khalil’ _password = ‘mypwd’ /admin/myprofile
  28. AUTHENTICATION FLOW Firewall backend Provider $provider->loadUserByUsername('sarah.khalil'); $user = new User(‘sarah.khalil’);

    Encoder Factory $factory->getEncoder($user); $encoder Encoder $encoder->isPasswordValid('mypwd'); _username = ‘sarah.khalil’ _password = ‘mypwd’ /admin/myprofile
  29. AUTHENTICATION FLOW Firewall backend Provider $provider->loadUserByUsername('sarah.khalil'); $user = new User(‘sarah.khalil’);

    Encoder Factory $factory->getEncoder($user); $encoder Encoder $encoder->isPasswordValid('mypwd'); true _username = ‘sarah.khalil’ _password = ‘mypwd’ /admin/myprofile
  30. AUTHENTICATION FLOW Firewall backend Provider $provider->loadUserByUsername('sarah.khalil'); $user = new User(‘sarah.khalil’);

    Encoder Factory $factory->getEncoder($user); $encoder Encoder $encoder->isPasswordValid('mypwd'); true authenticated _username = ‘sarah.khalil’ _password = ‘mypwd’ /admin/myprofile
  31. AUTHENTICATION FLOW Firewall backend Provider $provider->loadUserByUsername('sarah.khalil'); $user = new User(‘sarah.khalil’);

    Encoder Factory $factory->getEncoder($user); $encoder Encoder $encoder->isPasswordValid('mypwd'); true authenticated _username = ‘sarah.khalil’ _password = ‘mypwd’ /admin/myprofile or
  32. AUTHENTICATION FLOW Firewall backend Provider $provider->loadUserByUsername('sarah.khalil'); $user = new User(‘sarah.khalil’);

    Encoder Factory $factory->getEncoder($user); $encoder Encoder $encoder->isPasswordValid('mypwd'); true authenticated false | 401: Unauthorized _username = ‘sarah.khalil’ _password = ‘mypwd’ /admin/myprofile or
  33. $decision = $this ->get(‘security.authorization_checker') ->isGranted('ROLE_ADMIN'); if (false === $decision) {

    throw $this ->createAccessDeniedException(‘Access denied.’); } What you have usually in a controller
  34. $decision = $this ->get(‘security.authorization_checker') ->isGranted('ROLE_ADMIN'); if (false === $decision) {

    throw $this ->createAccessDeniedException(‘Access denied.’); } What you have usually in a controller $this ->denyAccessUnlessGranted('ROLE_ADMIN', null, ‘Access denied!');
  35. final public function isGranted($attributes, $object = null) { // Checks

    if there is someone authenticated (if there is a token) // Makes sure that the attributes in an array // returns true or false return $this->accessDecisionManager->decide($token, $attributes, $object); } Symfony\Component\Security\Core\Authorization\AccessDecisionManager Any string that represent a permission anything that can help to make a decision
  36. Symfony\Component\Security\Core\Authorization \AccessDecisionManager The object needs: • voters • a decision

    strategy • $allowIfAllAbstainDecisions • $allowIfEqualGrantedDeniedDecisions
  37. VOTERS Voters are sorted by the attribute priority (the highest

    is the last to be executed). If you don’t put any priority, => 0 (first to be executed)
  38. Symfony\Component\Security\Core\Authorization \AccessDecisionManager The object needs: • voters • a decision

    strategy • $allowIfAllAbstainDecisions • $allowIfEqualGrantedDeniedDecisions
  39. //Symfony\Component\Security\Core\Authorization\AccessDecisionManager public function decide(TokenInterface $token, array $attributes, $object = null)

    { return $this->{$this->strategy} ($token, $attributes, $object); } decideAffirmative decideConsensus decideUnanimous
  40. decideConsensus • Gives access if the majority says ACCESS_GRANTED (without

    abstainer) • If voters saying ACCESS_GRANTED === voters saying ACCESS_DENIED => the configuration takes the decision security.access_decision_manager.allow_if_equal_granted_denied Majority says yes. default = true
  41. If all voters say ACCESS_ABSTAIN the configuration takes the decision.

    security.access_decision_manager.allow_if_all_abstain default = false
  42. decideXxxx //Symfony\Component\Security\Core\Authorization\AccessDecisionManager //… foreach ($this->voters as $voter) { $result[] =

    $voter->vote($token, $object, $attributes); } // logic to return true or false 200 or 403 (forbidden)
  43. Voters must be light in case of unanimous or consensus

    strategy. (because all voters are executed)
  44. Controller or template or service from AuthorizationChecker::isGranted() Strategy Voters Decision

    maker if all can’t make a decision Decision maker if all abstain needs AccessDecisionManager::decide() calls
  45. Controller or template or service from AuthorizationChecker::isGranted() Strategy Voters Decision

    maker if all can’t make a decision Decision maker if all abstain needs AccessDecisionManager::decide() calls AccessDecisionManager::decideAffirmative() or AccessDecisionManager::decideConsensus() or AccessDecisionManager::decideUnanimous() calls
  46. Controller or template or service from VoterInterface::vote() … VoterInterface::vote() calls

    AuthorizationChecker::isGranted() Strategy Voters Decision maker if all can’t make a decision Decision maker if all abstain needs AccessDecisionManager::decide() calls AccessDecisionManager::decideAffirmative() or AccessDecisionManager::decideConsensus() or AccessDecisionManager::decideUnanimous() calls
  47. Controller or template or service from VoterInterface::vote() … VoterInterface::vote() calls

    AuthorizationChecker::isGranted() false true returns Strategy Voters Decision maker if all can’t make a decision Decision maker if all abstain needs AccessDecisionManager::decide() calls AccessDecisionManager::decideAffirmative() or AccessDecisionManager::decideConsensus() or AccessDecisionManager::decideUnanimous() calls