Examine the Reassuringly Robust Flexibility and Extensibility of Symfony Security
Tags: symfony, cybersecurity, cyberwar, cyberattacks
Published: 2022-03-22 20:05:13

This post originally appeared on [Symfony Station](https://www.symfonystation.com/Flexibility-Extensibility-Symfony-Security).

**It’s long past time to make your Symfony applications as secure as possible.**

As should be obvious to anyone not living under a rock, cyber security is critical. This has always been the case, but it’s even more important now thanks to Russian war crimes in Ukraine.

The potential for a cyberwar has never been higher. As Russia struggles with illegally occupying Ukraine it may lash out with cyberattacks against anyone supporting the Ukrainian nation.

Russia’s fellow rogue state allies, Belarus, Iran, North Korea, and China, could increase their current efforts. Plus, many cyber-criminal gangs are based in these countries.

In this article, we will examine Symfony’s security system. I am not a Symfony security professional so I will quote Symfony often and paraphrase it even more.

## Non-Symfony Security

But before we look at Symfony, there are many things you should be doing for security, regardless of the platform you use.

Techniques you should implement include:

- [Using HTTPS instead of HTTP](https://www.cloudflare.com/learning/ssl/why-use-https/) for websites
- Using secure hosting if not [SymfonyCloud from Platform.sh](https://symfony.com/cloud/)
- Using [Dependabot](https://github.com/dependabot) and [other security solutions](https://github.com/features/security) from [GitHub](https://github.blog/category/security/) or its competitors
- [Writing more secure code](https://github.blog/2021-12-06-write-more-secure-code-owasp-top-10-proactive-controls/) with the OWASP Top 10 Proactive Controls
- Continually researching and reading cybersecurity information
- [Maxing out the security settings](https://www.jetbrains.com/help/phpstorm/project-security.html) on an IDE like PHP Storm
- [Securing the front-end](https://www.divante.com/blog/5-web-security-tips-for-headless-pwa-jamstack-and-spa) of headless applications
- [Serialization security](https://securityboulevard.com/2021/09/what-are-serialization-attacks-and-how-to-prevent-them/)
- [Using a secure CDN](https://www.cloudflare.com/security/)
- [Input validation](https://github.blog/2021-12-06-write-more-secure-code-owasp-top-10-proactive-controls/)

## How to maximize your Symfony Application’s Security

First of all, after reading this overview, be sure to review the [Symfony security docs](https://symfony.com/doc/current/security.html) and the [SymfonyCasts security course](https://symfonycasts.com/screencast/symfony-security). Bookmark these links, as everything will not be covered in detail in this article. However, we will provide links to the relevant security techniques we explore here.


![SymfonyCasts security course graphic](https://www.symfonystation.com/sites/default/files/inline-images/symfonycasts-security.png)

SymfonyCasts is a paid service well worth the expense. Here’s their description of the security course, “It's security time! Symfony 5.3 comes with a reimagined version of its security system and I ❤️it! Yes, it's still super flexible & dependable. But the "guts" have been streamlined and simplified, making it easier to get your job done *and* giving you readable code if you need to dive into the core.

In this course, we'll go from an introduction into Symfony security into a full-blown application with users, permissions, custom voters and multiple ways to authenticate.”

So, check it out.

Symfony notes that it “provides many tools to secure your application. Some HTTP-related security tools, like [secure session cookies](https://symfony.com/doc/current/session.html) and [CSRF protection](https://symfony.com/doc/current/security/csrf.html), are provided by default. The SecurityBundle provides all the authentication and authorization features needed to secure your application.

A bundle is similar to a plugin in other software but even better. The core features of the Symfony framework are implemented with bundles (FrameworkBundle, SecurityBundle, DebugBundle, etc.)”

There are also third-party security bundles. Below we will look at a 2FA one.

### Symfony Flex

For ease of use with the installation of them, you should [implement Symfony Flex](https://symfony.com/doc/current/setup.html#symfony-flex).

Again, Symfony notes “A common practice when developing Symfony applications is to install packages (Symfony calls them [bundles](https://symfony.com/doc/current/bundles.html)) that provide ready-to-use features. Packages usually require some setup before using them (editing some files to enable the bundle, creating some files to add some initial config, etc.)

This setup can be automated and that's why Symfony includes [Symfony Flex](https://github.com/symfony/flex), a tool to simplify the installation/removal of packages in Symfony applications. Technically speaking, Symfony Flex is a Composer plugin that is installed by default when creating a new Symfony application and which automates the most common tasks of Symfony applications.”

### What are we going to review?

Symfony security includes techniques like:

- [Password Hashing](https://symfony.com/doc/current/security/passwords.html)
- [Two-Factor Authentication Bundle](https://symfony.com/bundles/SchebTwoFactorBundle/current/index.html)
- [Firewall/Authentication](https://symfony.com/doc/current/security.html#the-firewall)
- [Authorization/User Control](https://symfony.com/doc/current/security.html#access-control-authorization)
- [Security Events](https://symfony.com/doc/current/security.html#security-events)
- [Cross-site request forgery prevention](https://symfony.com/doc/current/security/csrf.html)
- [LDAP Server Authentication](https://symfony.com/doc/current/security/ldap.html)

### User Permissions

[Symfony permissions](https://symfony.com/doc/current/security.html#the-user) are linked to a user object. If you need to secure (parts of) your application, you need to create a user class. It will implement [UserInterface](https://github.com/symfony/symfony/blob/6.0/src/Symfony/Component/Security/Core/User/UserInterface.php). While it’s often a Doctrine entity, you can also use a dedicated Security user class.

Symfony notes “Symfony comes with several built-in user providers:
                            

- [Entity User Provider](https://symfony.com/doc/current/security/user_providers.html#security-entity-user-provider) - Loads users from a database using [Doctrine](https://symfony.com/doc/current/doctrine.html);
- [LDAP User Provider](https://symfony.com/doc/current/security/ldap.html#security-ldap-user-provider) - Loads users from an LDAP server;
- [Memory User Provider](https://symfony.com/doc/current/security/user_providers.html#security-memory-user-provider) - Loads users from a configuration file;
- [Chain User Provider](https://symfony.com/doc/current/security/user_providers.html#security-chain-user-provider) - Merges two or more user providers into a new user provider.

The built-in user providers cover the most common needs for applications, but you can also create a [custom user provider](https://symfony.com/doc/current/security/user_providers.html#security-custom-user-provider).”

### Password Hashing

Most applications require their users to log in via a password. If this is the case for you, the Symfony SecurityBundle provides [password hashing and verification functionality](https://symfony.com/doc/current/security.html#registering-the-user-hashing-passwords).

You can [access the details in the documentation](https://symfony.com/doc/current/security/passwords.html).

### SchebTwoFactorBundle

Symfony notes “the bundle hooks into the security layer and listens for authentication events. When a user login appears and the user has two-factor authentication enabled, access and privileges are temporarily withheld, putting the authentication status into an intermediate state. The user is challenged to enter a valid two-factor authentication code. Only when that code is entered correctly, the associated roles are granted.

![https://symfony.com/doc/bundles/SchebTwoFactorBundle/6.x/authentication-process.svg](https://symfony.com/doc/bundles/SchebTwoFactorBundle/6.x/authentication-process.svg)

To represent the state between login and a valid two-factor code being entered, the bundle introduces the role-like attribute `IS_AUTHENTICATED_2FA_IN_PROGRESS`, which can be used in `is_granted()` calls. `IS_AUTHENTICATED_FULLY` is – just like roles – withheld until the two-factor authentication step has been completed successfully.”

The bundle works out of the box with:

- [Google Authenticator](https://symfony.com/bundles/SchebTwoFactorBundle/current/providers/google.html)
- [TOTP Authenticator](https://symfony.com/bundles/SchebTwoFactorBundle/current/providers/totp.html)
- [Code-via-Email authentication](https://symfony.com/bundles/SchebTwoFactorBundle/current/providers/email.html)

### Firewall/Authentication

Again Symfony notes “The `firewalls` section of `config/packages/security.`YAML is the *most*
important section. A "firewall" is your authentication system: the firewall defines which parts of your application are secured and *how* your users will be able to authenticate (e.g. login form, API token, etc).

Only one firewall is active on each request: Symfony uses the `pattern` key to find the first match (you can also [match by the host or other things](https://symfony.com/doc/current/security/firewall_restriction.html)).

The `dev` firewall is a fake firewall: it makes sure that you don't accidentally block Symfony's dev tools - which live under URLs like `/_profiler` and `/_wdt`.

All *real* URLs are handled by the `main` firewall (no `pattern` key means it matches *all* URLs). A  firewall can have many modes of authentication, in other words, it enables many ways to ask the question "Who are you?".”

The SecurityBundle comes with many authenticators:

- [Form Login](https://symfony.com/doc/current/security.html#form-login)
- [JSON Login](https://symfony.com/doc/current/security.html#json-login)
- [HTTP Basic](https://symfony.com/doc/current/security.html#http-basic)
- [Login Link](https://symfony.com/doc/current/security.html#login-link)
- [X.509 Client Certificates](https://symfony.com/doc/current/security.html#x-509-client-certificates)
- [Remote users](https://symfony.com/doc/current/security.html#remote-users)
- [Custom Authenticators](https://symfony.com/doc/current/security/custom_authenticator.html)

### Limiting Login Attempts

Symfony provides basic protection against [brute force login attacks](https://owasp.org/www-community/controls/Blocking_Brute_Force_Attacks) via [enabling the `login_throttling` setting](https://symfony.com/doc/current/security.html#limiting-login-attempts).

### Authorization/Access Control

This security technique is fairly standard. Users log in to your app using your login form. You can deny access and work with the User object. That’s authorization, and it decides if a user can access some resource (a URL, a model object, a method call, etc).

The process has two sides:

1. The user receives a specific role when logging in (e.g. `ROLE_ADMIN`).
2. You add code so that a resource (e.g. URL, controller) requires a specific "attribute" (e.g. a role like `ROLE_ADMIN`) to be accessed.

[Voters are Symfony's most powerful way of managing permissions](https://symfony.com/doc/current/security/voters.html). They allow you to centralize all permission logic, then reuse them in many places.

### Security Events

Symfony notes “During the authentication process, [multiple events](https://symfony.com/doc/current/security.html#security-events) are dispatched that allow you to hook into the process or customize the response sent back to the user. You can do this by creating an [event listener or subscriber](https://symfony.com/doc/current/event_dispatcher.html) for these events.”

![Security event illustration](https://www.symfonystation.com/sites/default/files/inline-images/symfony-authentication-security-event.png)

Authentication events include:

- [CheckPassportEvent](https://github.com/symfony/symfony/blob/6.0/src/Symfony/Component/Security/Http/Event/CheckPassportEvent.php) - Dispatched after the authenticator created the [security passport](https://symfony.com/doc/current/security/custom_authenticator.html#security-passport).
Listeners of this event do the actual authentication checks (such as checking the passport, validating the CSRF token, etc.)
- [AuthenticationTokenCreatedEvent](https://github.com/symfony/symfony/blob/6.0/src/Symfony/Component/Security/Http/Event/AuthenticationTokenCreatedEvent.php) - Dispatched after the passport was validated and the authenticator created the security token (and user). This can be used in advanced use-cases
where you need to modify the created token (e.g. for multi-factor authentication).
- [AuthenticationSuccessEvent](https://github.com/symfony/symfony/blob/6.0/src/Symfony/Component/Security/Core/Event/AuthenticationSuccessEvent.php) - Dispatched when authentication is nearing success. This is the last event that can make an authentication fail by throwing an `AuthenticationException`.
- [LoginSuccessEvent](https://github.com/symfony/symfony/blob/6.0/src/Symfony/Component/Security/Http/Event/LoginSuccessEvent.php) - Dispatched after authentication was fully successful. Listeners to this
the event can modify the response sent back to the user.
- [LoginFailureEvent](https://github.com/symfony/symfony/blob/6.0/src/Symfony/Component/Security/Http/Event/LoginFailureEvent.php) - Dispatched after an `AuthenticationException` was thrown during
authentication. Listeners to this event can modify the error response sent back to the user.

Other Events

- [LogoutEvent](https://github.com/symfony/symfony/blob/6.0/src/Symfony/Component/Security/Http/Event/LogoutEvent.php) - Dispatched just before a user logs out of your application.
- [TokenDeauthenticatedEvent](https://github.com/symfony/symfony/blob/6.0/src/Symfony/Component/Security/Http/Event/TokenDeauthenticatedEvent.php) - Dispatched when a user is deauthenticated, for instance, because the [LogoutEvent](https://github.com/symfony/symfony/blob/6.0/src/Symfony/Component/Security/Http/Event/LogoutEvent.php) password was changed.
- [SwitchUserEvent](https://github.com/symfony/symfony/blob/6.0/src/Symfony/Component/Security/Http/Event/SwitchUserEvent.php) - Dispatched after impersonation is completed.

### How to Implement CSRF Protection

Symfony notes “CSRF - or [Cross-site request forgery](https://en.wikipedia.org/wiki/Cross-site_request_forgery) - is a method by which a malicious user attempts to make your legitimate users unknowingly submit data that they don't intend to submit.

[CSRF protection](https://symfony.com/doc/current/security/csrf.html) works by adding a hidden field to your form that contains a value that only you and your user know. This ensures that the user - not some other entity - is submitting the given data.

### Authenticating against an LDAP server

If you are using an LDAP server, Symfony provides several ways to work with it.

They note “the Security component offers:

- The LDAP [user provider](https://symfony.com/doc/current/security/user_providers.html) uses the [LdapUserProvider](https://github.com/symfony/symfony/blob/6.0/src/Symfony/Component/Ldap/Security/LdapUserProvider.php) class. Like all other user providers, it can be used with any authentication provider.
- The `form_login_ldap` authentication provider, for authenticating against an LDAP server using a login form. Like all other authentication providers, it can be used with any user provider.
- The `http_basic_ldap` authentication provider, for authenticating against an LDAP server using HTTP Basic. Like all other authentication providers, it can be used with any user provider.

This means that the following scenarios will work:

- Checking a user's password and fetching user information against an LDAP server. This can be done using both the LDAP user provider and either the LDAP form login or LDAP HTTP Basic authentication providers.
- Checking a user's password against an LDAP server while fetching user information from another source (database using FOSUserBundle, for example).
- Loading user information from an LDAP server, while using another authentication strategy (token-based pre-authentication, for example).”

## Conclusion

We have now reviewed the basics of Symfony security.

You can see how it is (fortunately) very extensive and flexible. It allows you many options for how you want to implement its best practices. You can customize security based on your server, business logic, security events, authorization roles, authenticators, user providers, plus other practices not covered here.

As noted above explore the [Symfony security documentation](https://symfony.com/doc/current/security.html) in detail. Also, consider subscribing to SymfonyCasts and taking the [security course](https://symfonycasts.com/screencast/symfony-security).

To close please make sure your Symfony application is as secure as possible by implementing these best practices. It’s vital that you keep your users safe from cybercriminals and the enemies of democracy. Security is just as important as speed, usability, accessibility, search optimization, and the other aspects of your app.

Stay safe Symfonistas!

***Reuben Walker***
Founder
Symfony Station