[SymfonyCon Paris] Dig in security

34ade09dd3d11004ca8ee4174fd3d6a2?s=47 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.

34ade09dd3d11004ca8ee4174fd3d6a2?s=128

Sarah KHALIL

December 09, 2015
Tweet

Transcript

  1. 2.

    WHO AM I? • Head of • Trainer & Developer

    • Enjoying sharer Sarah Khalil @saro0h
  2. 10.
  3. 12.
  4. 13.
  5. 17.
  6. 18.
  7. 21.

    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
  8. 22.

    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
  9. 23.

    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
  10. 24.
  11. 25.
  12. 31.

    Your application all routes begin with /admin all routes beginning

    with /shop all routes beginning with /blog all other routes…
  13. 32.

    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
  14. 33.

    WHAT DO WE HAVE TO WRITE? security: firewalls: admin: provider:

    in_memory pattern: /^admin form_login: login_path: /login check_path: /login_check
  15. 34.

    WHAT DO WE HAVE TO WRITE? security: firewalls: admin: provider:

    in_memory pattern: /^admin form_login: login_path: /login check_path: /login_check
  16. 35.

    WHAT DO WE HAVE TO WRITE? security: firewalls: admin: provider:

    in_memory pattern: /^admin form_login: login_path: /login check_path: /login_check
  17. 36.

    WHAT DO WE HAVE TO WRITE? security: firewalls: admin: provider:

    in_memory pattern: /^admin form_login: login_path: /login check_path: /login_check
  18. 55.

    AUTHENTICATION FLOW Firewall backend _username = ‘sarah.khalil’ _password = ‘mypwd’

    Provider authenticated 401: Unauthorized /admin/myprofile or
  19. 56.

    AUTHENTICATION FLOW Firewall backend _username = ‘sarah.khalil’ _password = ‘mypwd’

    Provider $provider->loadUserByUsername('sarah.khalil'); authenticated 401: Unauthorized /admin/myprofile or
  20. 57.

    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
  21. 62.

    AUTHENTICATION FLOW Firewall backend username = ‘sarah.khalil’ password = ‘mypwd’

    authenticated Provider $provider->loadUserByUsername('sarah.khalil'); $user = new User(‘sarah.khalil’); /admin/myprofile
  22. 63.

    AUTHENTICATION FLOW Firewall backend username = ‘sarah.khalil’ password = ‘mypwd’

    Encoder Factory authenticated Provider $provider->loadUserByUsername('sarah.khalil'); $user = new User(‘sarah.khalil’); /admin/myprofile
  23. 64.

    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
  24. 65.

    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
  25. 66.

    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
  26. 67.

    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
  27. 68.

    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
  28. 69.

    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
  29. 70.

    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); }
  30. 72.

    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
  31. 79.

    AUTHENTICATION FLOW Firewall backend Provider $provider->loadUserByUsername('sarah.khalil'); $user = new User(‘sarah.khalil’);

    Encoder Factory _username = ‘sarah.khalil’ _password = ‘mypwd’ /admin/myprofile
  32. 80.

    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
  33. 81.

    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
  34. 82.

    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
  35. 83.

    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
  36. 84.

    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
  37. 85.

    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
  38. 86.

    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
  39. 87.

    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
  40. 89.

    $decision = $this ->get(‘security.authorization_checker') ->isGranted('ROLE_ADMIN'); if (false === $decision) {

    throw $this ->createAccessDeniedException(‘Access denied.’); } What you have usually in a controller
  41. 90.

    $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!');
  42. 93.
  43. 94.
  44. 96.

    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
  45. 97.

    Symfony\Component\Security\Core\Authorization \AccessDecisionManager The object needs: • voters • a decision

    strategy • $allowIfAllAbstainDecisions • $allowIfEqualGrantedDeniedDecisions
  46. 99.

    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)
  47. 103.

    Symfony\Component\Security\Core\Authorization \AccessDecisionManager The object needs: • voters • a decision

    strategy • $allowIfAllAbstainDecisions • $allowIfEqualGrantedDeniedDecisions
  48. 104.

    //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
  49. 106.

    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
  50. 108.

    If all voters say ACCESS_ABSTAIN the configuration takes the decision.

    security.access_decision_manager.allow_if_all_abstain default = false
  51. 109.

    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)
  52. 112.

    Voters must be light in case of unanimous or consensus

    strategy. (because all voters are executed)
  53. 117.

    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
  54. 118.

    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
  55. 119.

    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
  56. 120.

    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
  57. 124.
  58. 127.
  59. 128.
  60. 134.
  61. 135.