Security

You can configure Symfony to authenticate your users using any method you
want and to load user information from any source. This is a complex topic, but
the Security guide has a lot of information about
this.

Regardless of your needs, authentication is configured in security.yml,
primarily under the firewalls key.

Best Practice

Best Practice

Unless you have two legitimately different authentication systems and
users (e.g. form login for the main site and a token system for your
API only), we recommend having only one firewall entry with the anonymous
key enabled.

Most applications only have one authentication system and one set of users.
For this reason, you only need one firewall entry. There are exceptions
of course, especially if you have separated web and API sections on your
site. But the point is to keep things simple.

Additionally, you should use the anonymous key under your firewall. If
you need to require users to be logged in for different sections of your
site (or maybe nearly all sections), use the access_control area.

Best Practice

Best Practice

Use the bcrypt encoder for encoding your users' passwords.

If your users have a password, then we recommend encoding it using the bcrypt
encoder, instead of the traditional SHA-512 hashing encoder. The main advantages
of bcrypt are the inclusion of a salt value to protect against rainbow
table attacks, and its adaptive nature, which allows to make it slower to
remain resistant to brute-force search attacks.

With this in mind, here is the authentication setup from our application,
which uses a login form to load users from the database:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

# app/config/security.ymlsecurity:encoders:AppBundle\Entity\User:bcryptproviders:database_users:entity:{class:AppBundle:User,property:username}firewalls:secured_area:pattern:^/anonymous:trueform_login:check_path:loginlogin_path:loginlogout:path:security_logouttarget:homepage# ... access_control exists, but is not shown here

Tip

The source code for our project contains comments that explain each part.

Symfony gives you several ways to enforce authorization, including the access_control
configuration in security.yml, the
@Security annotation and using
isGranted on the security.authorization_checker
service directly.

Best Practice

Best Practice

For protecting broad URL patterns, use access_control;

Whenever possible, use the @Security annotation;

Check security directly on the security.authorization_checker service whenever
you have a more complex situation.

There are also different ways to centralize your authorization logic, like
with a custom security voter or with ACL.

Best Practice

Best Practice

For fine-grained restrictions, define a custom security voter;

For restricting access to any object by any user via an admin
interface, use the Symfony ACL.

If your security logic is a little bit more complex, you can use an expression
inside @Security. In the following example, a user can only access the
controller if their email matches the value returned by the getAuthorEmail()
method on the Post object:

Notice that this requires the use of the ParamConverter, which automatically
queries for the Post object and puts it on the $post argument. This
is what makes it possible to use the post variable in the expression.

This has one major drawback: an expression in an annotation cannot easily
be reused in other parts of the application. Imagine that you want to add
a link in a template that will only be seen by authors. Right now you'll
need to repeat the expression code using Twig syntax:

The above example with @Security only works because we're using the
ParamConverter, which gives the expression
access to the post variable. If you don't use this, or have some other
more advanced use-case, you can always do the same security check in PHP:

If your security logic is complex and can't be centralized into a method
like isAuthor(), you should leverage custom voters. These are an order
of magnitude easier than ACLs and will give
you the flexibility you need in almost all cases.

First, create a voter class. The following example shows a voter that implements
the same getAuthorEmail() logic you used above:

namespaceAppBundle\Security;useSymfony\Component\Security\Core\Authentication\Token\TokenInterface;useSymfony\Component\Security\Core\Authorization\AccessDecisionManagerInterface;useSymfony\Component\Security\Core\Authorization\Voter\Voter;useSymfony\Component\Security\Core\User\UserInterface;useAppBundle\Entity\Post;classPostVoterextendsVoter{constCREATE='create';constEDIT='edit';/** * @var AccessDecisionManagerInterface */private$decisionManager;publicfunction__construct(AccessDecisionManagerInterface$decisionManager){$this->decisionManager=$decisionManager;}protectedfunctionsupports($attribute,$subject){if(!in_array($attribute,array(self::CREATE,self::EDIT))){returnfalse;}if(!$subjectinstanceofPost){returnfalse;}returntrue;}protectedfunctionvoteOnAttribute($attribute,$subject,TokenInterface$token){$user=$token->getUser();/** @var Post */$post=$subject;// $subject must be a Post instance, thanks to the supports methodif(!$userinstanceofUserInterface){returnfalse;}switch($attribute){caseself::CREATE:// if the user is an admin, allow them to create new postsif($this->decisionManager->decide($token,array('ROLE_ADMIN'))){returntrue;}break;caseself::EDIT:// if the user is the author of the post, allow them to edit the postsif($user->getEmail()===$post->getAuthorEmail()){returntrue;}break;}returnfalse;}}

To enable the security voter in the application, define a new service:

The FOSUserBundle, developed by the Symfony community, adds support for a
database-backed user system in Symfony. It also handles common tasks like
user registration and forgotten password functionality.

Enable the Remember Me feature to
allow your users to stay logged in for a long period of time.

When providing customer support, sometimes it's necessary to access the application
as some other user so that you can reproduce the problem. Symfony provides
the ability to impersonate users.