Конфигурация SecurityBundle ("security")

Система безопасности - это одна из наиболее мощных частей Symfony, которую можно во многом контролировать через её конфигурацию.

Полная конфигурация по умолчанию

Ниже представлена полная конфигурация по умолчанию для системы безопасности. Каждая часть будет разъяснена отдельно в следующем разделе.

  • YAML
      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
     38
     39
     40
     41
     42
     43
     44
     45
     46
     47
     48
     49
     50
     51
     52
     53
     54
     55
     56
     57
     58
     59
     60
     61
     62
     63
     64
     65
     66
     67
     68
     69
     70
     71
     72
     73
     74
     75
     76
     77
     78
     79
     80
     81
     82
     83
     84
     85
     86
     87
     88
     89
     90
     91
     92
     93
     94
     95
     96
     97
     98
     99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
    228
    229
    230
    231
    232
    233
    234
    235
    236
    237
    238
    239
    240
    241
    242
    243
    244
    245
    246
    247
    248
    249
    250
    251
    252
    253
    254
    255
    256
    257
    258
    259
    260
    261
    262
    263
    264
    265
    266
    267
    268
    269
    270
    271
    272
    273
    274
    275
    276
    277
    278
    279
    # config/packages/security.yaml
    security:
        access_denied_url:    ~ # Example: /foo/error403
    
        # стратегия может быть: ничего не делать, мигрировать, инвалидировать
        session_fixation_strategy:  migrate
        hide_user_not_found:  true
        always_authenticate_before_granting:  false
        erase_credentials:    true
        access_decision_manager:
            strategy:             affirmative # One of affirmative, consensus, unanimous
            allow_if_all_abstain:  false
            allow_if_equal_granted_denied:  true
    
        encoders:
            # Примеры:
            App\Entity\User1: sha512
            App\Entity\User2:
                algorithm:           sha512
                encode_as_base64:    true
                iterations:          5000
    
            # Кодировшик PBKDF2
            # смотрите примечание о PBKDF2 ниже, чтобы узнать детали о безопасности и скорости
            App\Entity\User3:
                algorithm:            pbkdf2
                hash_algorithm:       sha512
                encode_as_base64:     true
                iterations:           1000
                key_length:           40
    
            # Примеры опций/значений того, как может выглядеть пользовательский кодировщик
            App\Entity\User4:
                id:                   App\Security\MyPasswordEncoder
    
            # Кодировщик BCrypt
            # смотрите примечание о bcrypt ниже, чтобы узнать детали о конкретных зависимостях
            App\Entity\User5:
                algorithm:            bcrypt
                cost:                 13
    
            # Кодировщик Plaintext
            # не проводит кодировку
            App\Entity\User6:
                algorithm:            plaintext
                ignore_case:          false
    
            # кодировщик Argon2i
            Acme\DemoBundle\Entity\User6:
                algorithm:            argon2i
    
        providers:            # Required
            # Примеры:
            my_in_memory_provider:
                memory:
                    users:
                        foo:
                            password:           foo
                            roles:              ROLE_USER
                        bar:
                            password:           bar
                            roles:              [ROLE_USER, ROLE_ADMIN]
    
            my_entity_provider:
                entity:
                    class:              App\Entity\User7
                    property:           username
                    # имя менеджера сущностей не по умолчанию
                    manager_name:       ~
    
            my_ldap_provider:
                ldap:
                    service:            ~
                    base_dn:            ~
                    search_dn:          ~
                    search_password:    ~
                    default_roles:      'ROLE_USER'
                    uid_key:            'sAMAccountName'
                    filter:             '({uid_key}={username})'
    
            # Пример пользовательского провайдера
            my_some_custom_provider:
                id:                   ~
    
            # Цепь некоторых провайдеров
            my_chain_provider:
                chain:
                    providers:          [ my_in_memory_provider, my_entity_provider ]
    
        firewalls:            # Required
            # Примеры:
            somename:
                pattern: .*
                # ограничьте брандмауэр для определённого хоста
                host: admin\.example\.com
                # ограничьте брандмауэр для определённых HTTP-методов
                methods: [GET, POST]
                request_matcher: some.service.id
                access_denied_url: /foo/error403
                access_denied_handler: some.service.id
                entry_point: some.service.id
                provider: some_key_from_above
                # управляет тем, где каждый брандмауэр хранит информацию о сессии
                # См "Контекст брандмауэра" ниже, чтобы узнать больше
                context: context_key
                stateless: false
                x509:
                    provider: some_key_from_above
                remote_user:
                    provider: some_key_from_above
                http_basic:
                    provider: some_key_from_above
                http_basic_ldap:
                    provider:     some_key_from_above
                    service:      ldap
                    dn_string:    '{username}'
                    query_string: ~
                http_digest:
                    provider: some_key_from_above
                guard:
                    # Ключ из раздела "провайдеры" вашей конфигурации безопасности, в случае если провайдер вашего пользователя отличается от брандмауеэра
                    provider:             ~
    
                    # id сервиса (одного из ваших аутентификаторов), метод start() которого должен быть вызван, когда анонимный пользователь переходит на страницу, требущую аутентификации
                    entry_point:          null
    
                    # Массив id сервисов для всех ваших "аутентификаторов"
                    authenticators:       []
                form_login:
                    # отправить форму входа здесь
                    check_path: /login_check
    
                    # пользователь перенаправляется сюда, когда ему нужно выполнить вход
                    login_path: /login
    
                    # если "true", то перешлите пользователя на форму входа, вместо перенаправления
                    use_forward: false
    
                    # опции перенаправления удачного входа в систему (читайте подробнее ниже)
                    always_use_default_target_path: false
                    default_target_path:            /
                    target_path_parameter:          _target_path
                    use_referer:                    false
    
                    # опции перенаправления неудачного входа в систему (читайте подробнее ниже)
                    failure_path:    /foo
                    failure_forward: false
                    failure_path_parameter: _failure_path
                    failure_handler: some.service.id
                    success_handler: some.service.id
    
                    # имена полей для имения пользователя и пароля
                    username_parameter: _username
                    password_parameter: _password
    
                    # опции csrf-токенов
                    csrf_parameter:       _csrf_token
                    csrf_token_id:        authenticate
                    csrf_token_generator: my.csrf_token_generator.id
    
                    # по умолчанию, форма входа *должна* быть POST, а не GET
                    post_only:      true
                    remember_me:    false
    
                    # по умолчанию, сессия должна существовать до отправки запроса аутентификации
                    # если это не так, то Request::hasPreviousSession не вызывается во время аутентификации
                    require_previous_session: true
    
                form_login_ldap:
                    # отправьте форму входа здесь
                    check_path: /login_check
    
                    # пользователь перенаправляется сюда, когда ему нужно выполнить вход
                    login_path: /login
    
                    # если "true", перешлите пользователя к форме входа вместо перенаправления
                    use_forward: false
    
                    # опции перенаправления удачного входа в систему (читайте больше ниже)
                    always_use_default_target_path: false
                    default_target_path:            /
                    target_path_parameter:          _target_path
                    use_referer:                    false
    
                    # опции перенаправления неудачного входа в систему (читайте больше ниже)
                    failure_path:    /foo
                    failure_forward: false
                    failure_path_parameter: _failure_path
                    failure_handler: some.service.id
                    success_handler: some.service.id
    
                    # имена полей имени пользователя и пароля
                    username_parameter: _username
                    password_parameter: _password
    
                    # опции csrf-токенов
                    csrf_parameter:       _csrf_token
                    csrf_token_id:        authenticate
                    csrf_token_generator: my.csrf_token_generator.id
    
                    # по умолчанию, форма входа *должна* быть POST, а не GET
                    post_only:      true
                    remember_me:    false
    
                    # по умолчанию, сессия должна существовать до отправки запроса аутентификации
                    # если это не так, то Request::hasPreviousSession не вызывается во время аутентификации
                    require_previous_session: true
    
                    service:      ~
                    dn_string:    '{username}'
                    query_string: ~
    
                remember_me:
                    token_provider: name
                    secret: "%secret%"
                    name: NameOfTheCookie
                    lifetime: 3600 # in seconds
                    path: /foo
                    domain: somedomain.foo
                    secure: false
                    httponly: true
                    always_remember_me: false
                    remember_me_parameter: _remember_me
                logout:
                    path:   /logout
                    target: /
                    invalidate_session: false
                    delete_cookies:
                        a: { path: null, domain: null }
                        b: { path: null, domain: null }
                    handlers: [some.service.id, another.service.id]
                    success_handler: some.service.id
                anonymous: ~
    
            # Значения и опции по умолчанию для любого брандмауэра
            some_firewall_listener:
                pattern:              ~
                security:             true
                request_matcher:      ~
                access_denied_url:    ~
                access_denied_handler:  ~
                entry_point:          ~
                provider:             ~
                stateless:            false
                context:              ~
                logout:
                    csrf_parameter:       _csrf_token
                    csrf_token_generator: ~
                    csrf_token_id:        logout
                    path:                 /logout
                    target:               /
                    success_handler:      ~
                    invalidate_session:   true
                    delete_cookies:
    
                        # Prototype
                        name:
                            path:                 ~
                            domain:               ~
                    handlers:             []
                anonymous:
                    secret:               "%secret%"
                switch_user:
                    provider:             ~
                    parameter:            _switch_user
                    role:                 ROLE_ALLOWED_TO_SWITCH
    
        access_control:
            requires_channel:     ~
    
            # используйте этот дешифрованный формат url
            path:                 ~ # Example: ^/path to resource/
            host:                 ~
            ips:                  []
            methods:              []
            roles:                []
        role_hierarchy:
            ROLE_ADMIN:      [ROLE_ORGANIZER, ROLE_USER]
            ROLE_SUPERADMIN: [ROLE_ADMIN]
    

Конфигурация формы входа

При использовании слушателя аутентификатора form_login под брандмауэром, существует несколько общих опций для конфигурации опыта "формы входа".

Чтобы узнать ещё больше деталей, см. How to Customize Redirect After Form Login.

Процесс и форма входа в систему

login_path

тип: string по умолчанию: /login

Это маршрут или путь, по которому будет перенаправлен пользователь (кроме случаев, когда use_forward установлен, как true), когда он будет пытаться получить доступ к защищённому источнику, но при этом не пройдя полную аутентификацию.

Этот путь должен быть доступен обычному, неаутентифицированному пользователю, иначе вы можете создать петлю перенаправления. Чтобы узнать больше, см. "Избежание распространённых ошибок".

check_path

тип: string по умолчанию: /login_check

Это маршрут или путь, по которму должна отправляться ваша форма входа. Брандмауэр будет принимать любые запросы (по умолчанию, только запросы POST) по этому URL, и обрабатывать отправленные учётные данные входа.

Убедитесь в том, что этот URL охватывается вашим основным брандмауэром (т.е. не создавайте отдельный брандмауэр просто для URL check_path).

use_forward

тип: boolean по умолчанию: false

Если вы хотите, чтобы пользователь был переслан к форме входа вместо того, чтобы быть перенаправленным, установите эту опцию как true.

username_parameter

тип: string по умолчанию: _username

Это имя поля, которое вам нужно дать полю имени пользователя вашей формы входа. Когда вы отправляете форму в check_path, то система безопасности будет искать параметр POST с этим именем.

password_parameter

тип: string по умолчанию: _password

Это имя поля, которое вам нужно дать полю пароле вашей формы входа. Когда вы отправляете форму в check_path, то система безопасности будет искать параметр POST с этим именем.

post_only

тип: boolean по умолчанию: true

По умолчанию, вы должны отправить вашу форму входа по URL check_path в виде запроса POST. Установив эту опцию, как false, вы можете отправить запрос GET по URL check_path.

Перенаправление после входа в систему

always_use_default_target_path

тип: boolean по умолчанию: false

Если true, то пользователи всегда перенаправляются по целевому пути по умолчанию, несмотря на предыдущий URL, сохранённый в сессии.

default_target_path

тип: string по умолчанию: /

Страница, на которую перенаправляются пользователи, когда предыдущая страница, сохранённая в сессии, отсутствует (например, когда пользователи напрямую заходят на страницу входа в систему).

target_path_parameter

тип: string по умолчанию: _target_path

При использовании формы входа, если вы включаете HTML-элемент для установки целевого пути, эта опция позволяется вам изменить имя самого HTML-элемента.

use_referer

тип: boolean по умолчанию: false

Если true, то пользователь перенаправляется в значению, сохранённому в заголовке HTTP_REFERER, когда в сессии не был сохранён предыдущий URL. Если реферальный URL такой же, как и сгенерированный с маршрутом login_path, то пользователь перенаправляется по default_target_path, чтобы избежать замкнутого перенаправления.

Note

По историческим причинам, и чтобы соответствовать ошибке HTTP стандарта, опция называется use_referer вместо use_referrer.

Конфигурация выхода из системы

invalidate_session

тип: boolean по умолчанию: true

По умолчанию, когда пользователи выходят из любого брандмауэра, их сессии инвалидируются. Это означает, что выход из одного брандмауэра автоматически запускает выходы из всех других.

Опция invalidate_session позволяет вам переопределить это поведение. Установите эту опцию, как false в каждом брандмауэре, и пользователь будет выполнять выход только из текущего брандмауэра, но не остальных.

Функциональность LDAP

Существует несколько опций для соединения с LDAP-сервером, используя поставщиков аутентификации form_login_ldap и http_basic_ldap, или поставщика пользователей ldap.

Чтобы узнать ещё больше, см. Authenticating against an LDAP server.

Аутентификация

Вы можете аутентифицироваться на LDAP-сервере используя LDAP-варианты поставщиков аутентификации form_login и http_basic. Просто используйте form_login_ldap и http_basic_ldap, которые попробуют bind с LDAP-сервером, вместо использования сравнения паролей.

Оба поставщика аутентификации имеют одинаковые аргументы в качестве обычных составляющих, с дополнением в виде двух ключей конфигурации:

service

тип: string по умолчанию: ldap

Это имя вашего сконфигурированного LDAP-клиента.

dn_string

тип: string по умолчанию: {username}

Это строка, которая будет использована как связующее отличительное имя. Заполнитель {username} будет заменён значением, предоставленным пользователем (его логином). В зависимости от конфигурации вашего LDAP-сервера, вам может быть нужно переопределить это значение.

query_string

тип: string по умолчанию: null

Это строка, которая будет использованадля запросов отличительного имени. Заполнитель {username} будет заменён значением, предоставленным пользователем (его логином). В зависимости от конфигурации вашего LDAP-сервера, вам может быть нужно переопределить это значение. Эта настройка необходима только если отличительное имя пользователя не может быть выведено статистично, используя опцию конфигурации dn_string.

Поставщик пользователей

Пользователи будут извлекаться из сконфигурированного поставщика пользователей. Если вы хотите получаь ваших пользователей из LDAP-сервера, то вам нужно будет использовать поставщика пользователей ldap, в дополнение к одному из двух поставщиков аутентификации (form_login_ldap или http_basic_ldap).

  • YAML
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    # config/packages/security.yaml
    security:
        # ...
    
        providers:
            my_ldap_users:
                ldap:
                    service: ldap
                    base_dn: 'dc=symfony,dc=com'
                    search_dn: '%ldap.search_dn%'
                    search_password: '%ldap.search_password%'
                    default_roles: ''
                    uid_key: 'uid'
                    filter: '(&({uid_key}={username})(objectclass=person)(ou=Users))'
    

Использование кодировщика PBKDF2: Безопасность и скорость

Кодировщик PBKDF2 предоставляет высокий уровень Шифрованной безопасности, как рекомендовано Национальным Институтом Стандартов и Технологий (NIST).

Вы можете увидеть пример кодировщика pbkdf2 в разделе YAML на этой странице.

Но использование PBKDF2 также имеет предостережние: его использование (с большим количеством итераций) замедляет процесс. Поэтому, PBKDF2 стоит использовать бережно и с осторожностью.

Хорошая конфигурация содержит какминимум 1000 итераций и sha512 для алгоритма хеширования.

Использование кодировщика паролей BCrypt

  • YAML
    1
    2
    3
    4
    5
    6
    7
    8
    # config/packages/security.yaml
    security:
        # ...
    
        encoders:
            Symfony\Component\Security\Core\User\User:
                algorithm: bcrypt
                cost:      15
    
  • XML
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    <!-- config/packages/security.xml -->
    <?xml version="1.0" charset="UTF-8" ?>
    <srv:container xmlns="http://symfony.com/schema/dic/security"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:srv="http://symfony.com/schema/dic/services"
        xsi:schemaLocation="http://symfony.com/schema/dic/services
            http://symfony.com/schema/dic/services/services-1.0.xsd">
    
        <config>
            <!-- ... -->
            <encoder
                class="Symfony\Component\Security\Core\User\User"
                algorithm="bcrypt"
                cost="15"
            />
        </config>
    </srv:container>
    
  • PHP
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    // app/config/security.php
    use Symfony\Component\Security\Core\User\User;
    
    $container->loadFromExtension('security', array(
        // ...
        'encoders' => array(
            User::class => array(
                'algorithm' => 'bcrypt',
                'cost'      => 15,
            ),
        ),
    ));
    

cost может быть в диапазоне 4-31 и определяет длину кодирования пароля. Каждый отрезок cost удваивает время кодирования пароля.

Если вы не предоставите опцию cost, то используется значение по умолчанию - 13.

Note

Вы можете изменить значение в любой момент - даже если у вас уже есть зашифрованные пароли с другим значением. Новые пароли будут зашифрованы с использованием нового значения, в то время, как зашифрованные ранее будут влаидированы, используя значение, использованное во время шифрования.

Соль для каждого нового пароля генерируется автоматически и не должна быть сохранена. Так как зашифрованный пароль содержит соль, использованную для его шифрования, сохранения только зашифрованного пароля достаточно.

Note

Зашифрованные пароли BCrypt составляют 60 символов, так что убедитесь в том, что вы выделили им достаточно места для хранения.

Tip

Простая техника для ускорения тестов при использовании BCrypt - установить стоимость как 4, что является минимальным позволенным значением, в конфигурации окружения test.

Использование Argon2i Кодировщик пароля

Caution

Чтобы использовать этот кодировщик, вам нужно либо использовать PHP версии 7.2, или установить расширение libsodium.

  • YAML
    1
    2
    3
    4
    5
    6
    7
    # app/config/security.yml
    security:
        # ...
    
        encoders:
            Symfony\Component\Security\Core\User\User:
                algorithm: argon2i
    
  • XML
    1
    2
    3
    4
    5
    6
    7
    8
    <!-- app/config/security.xml -->
    <config>
        <!-- ... -->
        <encoder
            class="Symfony\Component\Security\Core\User\User"
            algorithm="argon2i"
        />
    </config>
    
  • PHP
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    // app/config/security.php
    use Symfony\Component\Security\Core\User\User;
    
    $container->loadFromExtension('security', array(
        // ...
        'encoders' => array(
            User::class => array(
                'algorithm' => 'argon2i',
            ),
        ),
    ));
    

Соль для каждого нового пароля генерируется автоматически и не требует сохранения. Так как зашифрованный пароль содержит соль, использованную для его шифрования, сохранения самого зашифрованного пароля достаточно.

Note

Зашифрованные с помощью Argon2i пароли имеют 96 символов, но в связи с требованиями хеширования, сохранёнными в результирующем хеше, это может измениться в будущем.

Контекст брандмауэра

Большинству приложений понадобится только один брандмауэр. Но если вашему приложению нужно несколько, то вы заметите, что если вы аутентифицированы в одном брандмауэре, автоматически в других вы не аутентифицируетесь. Другими словами, системы не пользуются общщим "контекстом": каждый брандмауэр действует, как отдельная система безопасности.

Однако, каждый брандмауэр имеет необязательный ключ context (который по умолчанию имеет имя бандмауэра), который используется при хранении и извлечении данных безопасности из и в сессию. Если бы этот ключ устанавливал одинаковое значение в нескольких брандмауэрах, то "контекст" мог бы быть общим:

  • YAML
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    # config/packages/security.yaml
    security:
        # ...
    
        firewalls:
            somename:
                # ...
                context: my_context
            othername:
                # ...
                context: my_context
    
  • XML
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    <!-- config/packages/security.xml -->
    <?xml version="1.0" charset="UTF-8" ?>
    <srv:container xmlns="http://symfony.com/schema/dic/security"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:srv="http://symfony.com/schema/dic/services"
        xsi:schemaLocation="http://symfony.com/schema/dic/services
            http://symfony.com/schema/dic/services/services-1.0.xsd">
    
        <config>
            <firewall name="somename" context="my_context">
                <!-- ... -->
            </firewall>
            <firewall name="othername" context="my_context">
                <!-- ... -->
            </firewall>
        </config>
    </srv:container>
    
  • PHP
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    // config/packages/security.php
    $container->loadFromExtension('security', array(
        'firewalls' => array(
            'somename' => array(
                // ...
                'context' => 'my_context',
            ),
            'othername' => array(
                // ...
                'context' => 'my_context',
            ),
        ),
    ));
    

Note

Ключ контекста брандмауэра хранится в сессии, так что каждый брандмауэр, использующий его, должен установить свою опцию stateless, как false. В обратном случае, контекст игнорируется и вы не сможете аутентифицироваться в нескольких брандмауэрах одновременно.

Эта документация является переводом официальной документации Symfony и предоставляется по свободной лицензии CC BY-SA 3.0.