Skip to content
  • About
    • What is Symfony?
    • Community
    • News
    • Contributing
    • Support
  • Documentation
    • Symfony Docs
    • Symfony Book
    • Screencasts
    • Symfony Bundles
    • Symfony Cloud
    • Training
  • Services
    • SensioLabs Professional services to help you with Symfony
    • Platform.sh for Symfony Best platform to deploy Symfony apps
    • SymfonyInsight Automatic quality checks for your apps
    • Symfony Certification Prove your knowledge and boost your career
    • Blackfire Profile and monitor performance of your apps
  • Other
  • Blog
  • Download
sponsored by SensioLabs
  1. Home
  2. Documentation
  3. Cookbook
  4. Security
  5. How to implement your own Voter to blacklist IP Addresses
  • Documentation
  • Book
  • Reference
  • Bundles
  • Cloud

Table of Contents

  • The Voter Interface
  • Creating a Custom Voter
  • Declaring the Voter as a Service
  • Changing the Access Decision Strategy

How to implement your own Voter to blacklist IP Addresses

Edit this page

Warning: You are browsing the documentation for Symfony 2.1, which is no longer maintained.

Read the updated version of this page for Symfony 6.2 (the current stable version).

How to implement your own Voter to blacklist IP Addresses

The Symfony2 security component provides several layers to authorize users. One of the layers is called a `voter`. A voter is a dedicated class that checks if the user has the rights to be connected to the application. For instance, Symfony2 provides a layer that checks if the user is fully authorized or if it has some expected roles.

It is sometimes useful to create a custom voter to handle a specific case not handled by the framework. In this section, you'll learn how to create a voter that will allow you to blacklist users by their IP.

The Voter Interface

A custom voter must implement VoterInterface, which requires the following three methods:

1
2
3
4
5
6
interface VoterInterface
{
    function supportsAttribute($attribute);
    function supportsClass($class);
    function vote(TokenInterface $token, $object, array $attributes);
}

The supportsAttribute() method is used to check if the voter supports the given user attribute (i.e: a role, an acl, etc.).

The supportsClass() method is used to check if the voter supports the current user token class.

The vote() method must implement the business logic that verifies whether or not the user is granted access. This method must return one of the following values:

  • VoterInterface::ACCESS_GRANTED: The user is allowed to access the application
  • VoterInterface::ACCESS_ABSTAIN: The voter cannot decide if the user is granted or not
  • VoterInterface::ACCESS_DENIED: The user is not allowed to access the application

In this example, you'll check if the user's IP address matches against a list of blacklisted addresses. If the user's IP is blacklisted, you'll return VoterInterface::ACCESS_DENIED, otherwise you'll return VoterInterface::ACCESS_ABSTAIN as this voter's purpose is only to deny access, not to grant access.

Creating a Custom Voter

To blacklist a user based on its IP, you can use the request service and compare the IP address against a set of blacklisted IP addresses:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
// src/Acme/DemoBundle/Security/Authorization/Voter/ClientIpVoter.php
namespace Acme\DemoBundle\Security\Authorization\Voter;

use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\Security\Core\Authorization\Voter\VoterInterface;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;

class ClientIpVoter implements VoterInterface
{
    public function __construct(ContainerInterface $container, array $blacklistedIp = array())
    {
        $this->container     = $container;
        $this->blacklistedIp = $blacklistedIp;
    }

    public function supportsAttribute($attribute)
    {
        // you won't check against a user attribute, so return true
        return true;
    }

    public function supportsClass($class)
    {
        // your voter supports all type of token classes, so return true
        return true;
    }

    function vote(TokenInterface $token, $object, array $attributes)
    {
        $request = $this->container->get('request');
        if (in_array($request->getClientIp(), $this->blacklistedIp)) {
            return VoterInterface::ACCESS_DENIED;
        }

        return VoterInterface::ACCESS_ABSTAIN;
    }
}

That's it! The voter is done. The next step is to inject the voter into the security layer. This can be done easily through the service container.

Tip

Your implementation of the methods
supportsAttribute()
and supportsClass()
are not being called internally by the framework. Once you have registered your
voter the vote() method will always be called, regardless of whether or not these two methods return true. Therefore you need to call those methods in your implementation of the vote() method and return ACCESS_ABSTAIN if your voter does not support the class or attribute.

Declaring the Voter as a Service

To inject the voter into the security layer, you must declare it as a service, and tag it as a "security.voter":

  • YAML
  • XML
  • PHP
1
2
3
4
5
6
7
8
# src/Acme/AcmeBundle/Resources/config/services.yml
services:
    security.access.blacklist_voter:
        class:      Acme\DemoBundle\Security\Authorization\Voter\ClientIpVoter
        arguments:  ["@service_container", [123.123.123.123, 171.171.171.171]]
        public:     false
        tags:
            - { name: security.voter }
1
2
3
4
5
6
7
8
9
10
<!-- src/Acme/AcmeBundle/Resources/config/services.xml -->
<service id="security.access.blacklist_voter"
         class="Acme\DemoBundle\Security\Authorization\Voter\ClientIpVoter" public="false">
    <argument type="service" id="service_container" strict="false" />
    <argument type="collection">
        <argument>123.123.123.123</argument>
        <argument>171.171.171.171</argument>
    </argument>
    <tag name="security.voter" />
</service>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// src/Acme/AcmeBundle/Resources/config/services.php
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Reference;

$definition = new Definition(
    'Acme\DemoBundle\Security\Authorization\Voter\ClientIpVoter',
    array(
        new Reference('service_container'),
        array('123.123.123.123', '171.171.171.171'),
    ),
);
$definition->addTag('security.voter');
$definition->setPublic(false);

$container->setDefinition('security.access.blacklist_voter', $definition);

Tip

Be sure to import this configuration file from your main application configuration file (e.g. app/config/config.yml). For more information see Service Container. To read more about defining services in general, see the Service Container chapter.

Changing the Access Decision Strategy

In order for the new voter to take effect, you need to change the default access decision strategy, which, by default, grants access if any voter grants access.

In this case, choose the unanimous strategy. Unlike the affirmative strategy (the default), with the unanimous strategy, if only one voter denies access (e.g. the ClientIpVoter), access is not granted to the end user.

To do that, override the default access_decision_manager section of your application configuration file with the following code.

  • YAML
  • XML
  • PHP
1
2
3
4
5
# app/config/security.yml
security:
    access_decision_manager:
        # strategy can be: affirmative, unanimous or consensus
        strategy: unanimous
1
2
3
4
5
<!-- app/config/security.xml -->
<config>
    <!-- strategy can be: affirmative, unanimous or consensus -->
    <access-decision-manager strategy="unanimous">
</config>
1
2
3
4
5
6
7
// app/config/security.xml
$container->loadFromExtension('security', array(
    // strategy can be: affirmative, unanimous or consensus
    'access_decision_manager' => array(
        'strategy' => 'unanimous',
    ),
));

That's it! Now, when deciding whether or not a user should have access, the new voter will deny access to any user in the list of blacklisted IPs.

This work, including the code samples, is licensed under a Creative Commons BY-SA 3.0 license.
TOC
    Version
    We stand with Ukraine.
    Version:
    Be safe against critical risks to your projects and businesses

    Be safe against critical risks to your projects and businesses

    Measure & Improve Symfony Code Performance

    Measure & Improve Symfony Code Performance

    Symfony footer

    ↓ Our footer now uses the colors of the Ukrainian flag because Symfony stands with the people of Ukraine.

    Avatar of Romaxx, a Symfony contributor

    Thanks Romaxx for being a Symfony contributor

    1 commit • 6 lines changed

    View all contributors that help us make Symfony

    Become a Symfony contributor

    Be an active part of the community and contribute ideas, code and bug fixes. Both experts and newcomers are welcome.

    Learn how to contribute

    Symfony™ is a trademark of Symfony SAS. All rights reserved.

    • What is Symfony?

      • Symfony at a Glance
      • Symfony Components
      • Case Studies
      • Symfony Releases
      • Security Policy
      • Logo & Screenshots
      • Trademark & Licenses
      • symfony1 Legacy
    • Learn Symfony

      • Symfony Docs
      • Symfony Book
      • Reference
      • Bundles
      • Best Practices
      • Training
      • eLearning Platform
      • Certification
    • Screencasts

      • Learn Symfony
      • Learn PHP
      • Learn JavaScript
      • Learn Drupal
      • Learn RESTful APIs
    • Community

      • SymfonyConnect
      • Support
      • How to be Involved
      • Code of Conduct
      • Events & Meetups
      • Projects using Symfony
      • Downloads Stats
      • Contributors
      • Backers
    • Blog

      • Events & Meetups
      • A week of symfony
      • Case studies
      • Cloud
      • Community
      • Conferences
      • Diversity
      • Documentation
      • Living on the edge
      • Releases
      • Security Advisories
      • SymfonyInsight
      • Twig
      • SensioLabs
    • Services

      • SensioLabs services
      • Train developers
      • Manage your project quality
      • Improve your project performance
      • Host Symfony projects

      Deployed on

    Follow Symfony

    Search by Algolia