Security and Voters in Symfony 3.4

So the other day I was talking to one of our interns on the topic why it’s important to think about security and permissions when writing a backend app, and so I thought, why not write a blog post talking about it.

If you’ve coded a little bit with Symfony, you probably already know that the security component is very useful to secure your app in many ways. There’s different concepts we need to understand before we can talk about voters.

Authentication

The first part to think of when coding a backend app is authentication.

Wether it’s through a login page or a cookie header within the request, the user will have to be authenticated to access the secured part of your application. This way you will prevent anonymous users from accessing it.

How to do this is up to you, but there’s several bundles that help you get started. The most used one is FOSUserBundle. Alternatively, you can code your own custom authentication system and User entities, implementing Symfony’s UserInterface, so you can handle all the roles, etc. More info on implementing a custom authentication system here.

One way or the other, you’ll have to define roles in your application, specifically in role_hierarchy and access_control keys in your security.yml. Let’s look into these.

So for instance, let’s imagine a case where we have an art application, where artists upload their art.

Our security.yml role hierarchy could look something like this:

Once you have your authentication firewall up and running, it’s all about the authorization.

Authorization

This is when an already authenticated user is allowed or not into a specific part of an app.

For example, our ACL could look something like this:

This means that all URIs under /admin will need some type of authentitication.

Let’s imagine we defined a CRUD page with all artists on it, with the following uri in our controller: /admin/artist

So, right now, a user that has ROLE_USER might be able to see a CRUD page with all the users in it! This is wrong! Only a user with ROLE_ADMIN should be able to see that page right? To achieve this we can call the AuthenticateCheker service from Symfony’s security component, like so:

Or with Symfony’s base Controller’s wrapper function

You can even write the annotation, and the controller will be even thinner!

Ok, so far so good. We now have a page which is only allowed for admins to access, great!

Let’s talk about an artist being able to edit it’s own profile page.

So we’ve just updated the role and that should do right? Wrong!

What about users who have the same roles, but are not allowed to see each others’ information!!

So for instance, the artist with id 34 should be able to access the URI the /admin/artist/edit/34 but should not be able to access /admin/artist/edit/35, /admin/artist/edit/36,… etc.

Well, that’s where voters come in. This is such a key part of the application and it’s important you take it into account. Do not just secure your actions with roles, secure it with voters. The last thing you want is to have security holes in your application!

So in our previous example, artist with id 34 should only be able to access /admin/artist/edit/34 right? Let’s see how it can be done.

Security Voters

Voters are the services that decide which user can see or access what page. Conceptually, they are called voters because they vote, just like a Parliament or the Government, if a user can access certain information. They vote in favour (ACCESS_GRANTED), against (ACCESS_DENIED) or they abstain themselves (ACCESS_ABSTAIN) when they do not have enough information.

How to build a Voter

A Voter has to implement the VoterInterface. You can implement it yourself, or you can extend the abstract class Voter, which already has some logic into it.

This is what an Artist Voter would look like in our example.

So the code is pretty self explanatory. There are two functions which we must implement when extending the Voter,

and

.

The other two functions are private functions that are called in the above two. The private functions contain the logic. In our case, it’ll return true if an user is the owner of the artist entity, and false if anything else. Also, if the user has an admin role, it’ll also return true.

The supports function is telling that this Artist Voter will just vote about Artist entities, nothing else. And it’ll also vote on ‘edit’ and ‘view’ attributes, nothing else. If subject and attributes match, it’ll return the result of the vote, which will be a true or false on this case, as it’s what we need in the if section of the

function of the abstract Voter.

Now let’s use this voter in our artist edit action:

That would now protect our URI from users who are trying to edit someone else’s page.

Let’s look a bit more into the the wrapper function

from Symfony’s Controller, which uses Symfony’s ControlTrait.

So basically, the second parameter is the subject, AKA, the object we’re trying to edit.

So calling the above function

is the same as calling

like so:

So as you can see, we’re calling the same function as at the beggining when we wanted to secure the indexAction with the function

except this time, we’re seding a second parameter, the subject.

What this piece of code will do is trigger the hook of all voters that implement VoterInterface in your application and try to vote.

And if we look a bit deeper what the vote function does, you can find this code in the abstract class Voter, which we just extended to create our ArtistVoter.

In our example, if it supports the attribute ‘edit’ and the subject, in this case, an Artist entity, it’ll try to grant or deny the access. If it doesn’t support this attribute OR this subject, it’ll abstain itself and move on to the next voter.

The great thing about this is you can implement different voters with one same subject but different logic, and in the end, all votes will be counted and the application will decide if that user is granted or not the access, much like a democratic parliament :)

Hope this info is useful to understand a bit more what goes under the hood in Symfony’s security component, and how to make sure your app is always secure.

Happy coding!! :)

Authentication here.
Authorization here.
Security component here.
Security configuration here.
Voters documentation here.

Originally published at Joey’s blog.

Coder, Entrepreneur, Co-founder at SlowCode

Coder, Entrepreneur, Co-founder at SlowCode