Як працює безпека access_control?
Дата оновлення перекладу 2025-09-09
Як працює безпека access_control?
Для кожного вхідного запиту, Symfony перевіряє кожний запис access_control, щоб знайти
один, який співпадає з поточним запитом. Як тільки вона знаходить запис, що співпадає
access_control, вона зупиняється - лише перший access_control, що співпадає,
використовується для надання доступу.
Кожний access_control має декілька опцій, які конфігурують дві різних речі:
- чи має вхідний запит співпадати з цим записом контролю доступу
- при співпадінні, чи має застосовуватися якесь обмеження доступу :
1. Опції співпадіння
Symfony створює екземпляр класу RequestMatcher
для кожного запису access_control, який визначає, чи має бути використано цей контроль
доступу у цьому запиті. Наступні опції access_control використовуються для співставлення:
path: регулярний вираз (без розмежувачів)ipабоips: маски мережі також підтримуються (може бути рядком, розділеним комою)port: ціле числоhost: регулярний виразmethods: один або декілька методівrequest_matcher: сервіс, що реалізуєRequestMatcherInterfaceattributes: масив, який може бути використано для вказання одного або більше атрибутів запиту , які мають точно співпастиroute: імʼя маршруту
Візьміть наступні записи access_control в якості прикладу:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
# config/packages/security.yaml
parameters:
env(TRUSTED_IPS): '10.0.0.1, 10.0.0.2'
security:
# ...
access_control:
- { path: '^/admin', roles: ROLE_USER_PORT, ip: 127.0.0.1, port: 8080 }
- { path: '^/admin', roles: ROLE_USER_IP, ip: 127.0.0.1 }
- { path: '^/admin', roles: ROLE_USER_HOST, host: symfony\.com$ }
- { path: '^/admin', roles: ROLE_USER_METHOD, methods: [POST, PUT] }
# IP можуть бути розділені комою, що особливо корисно при використанні змінних середовища
- { path: '^/admin', roles: ROLE_USER_IP, ips: '%env(TRUSTED_IPS)%' }
- { path: '^/admin', roles: ROLE_USER_IP, ips: [127.0.0.1, ::1, '%env(TRUSTED_IPS)%'] }
# для користувацьких потреб співставлення, використайте сервіс співставлення запитів
- { roles: ROLE_USER, request_matcher: App\Security\RequestMatcher\MyRequestMatcher }
# вимагати ROLE_ADMIN для маршруту 'admin'. Ви можете використовувати ярлик "route: "xxx", замість "attributes": ["_route": "xxx"]
- { attributes: {'_route': 'admin'}, roles: ROLE_ADMIN }
- { route: 'admin', roles: ROLE_ADMIN }
Для кожного вхідного запиту Symfony буде вирішувати, який access_control використовувати
використовувати на основі URI, IP-адреси клієнта, імені вхідного хоста
та методу запиту. Пам'ятайте, що використовується перше правило, яке збігається, і
якщо ip, port, host або method не вказано для запису, то
access_control буде відповідати будь-якому ip, port, host або method.
Дивіться наступні приклади:
- Приклад #1:
-
- URI
/admin/user - IP:
127.0.0.1, Port:80, Host:example.com, Method:GET - Rule applied: правило #2 (
ROLE_USER_IP) - Why? URI співпадає з
path, а IP співпадає зip.
- URI
- Приклад #2:
-
- URI
/admin/user - IP:
127.0.0.1, Port:80, Host:symfony.com, Method:GET - Rule applied: правило #2 (
ROLE_USER_IP) - Why?
pathтаipвсе ще співпадають. Це також співпадатиме із записомROLE_USER_HOST, але лише перше співпадінняaccess_controlбуде використано.
- URI
- Приклад #3:
-
- URI
/admin/user - IP:
127.0.0.1, Port:8080, Host:symfony.com, Method:GET - Rule applied: правило #1 (
ROLE_USER_PORT) - Why?
path,ipтаportспівпадають.
- URI
- Приклад #4:
-
- URI
/admin/user - IP:
168.0.0.1, Port:80, Host:symfony.com, Method:GET - Rule applied: правило #3 (
ROLE_USER_HOST) - Why?
ipне співпадає ані з першим правилом, ані з другим. - Тому використовується третє правило (яке співпадає).
- URI
- Приклад #5:
-
- URI
/admin/user - IP:
168.0.0.1, Port:80, Host:symfony.com, Method:POST - Rule applied: правило #3 (
ROLE_USER_HOST) - Why? Третє правило все ще співпадає. Це також співпадатиме з четвертим правилом
- (
ROLE_USER_METHOD), але використовується лише перший співпавшийaccess_control.
- URI
- Приклад #6:
-
- URI
/admin/user - IP:
168.0.0.1, Port:80, Host:example.com, Method:POST - Rule applied: правило #4 (
ROLE_USER_METHOD) - Why?
ipтаhostне співпадають з першими трьома записами, але - четвертий -
ROLE_USER_METHOD- співпадає та буде використано.
- URI
- Приклад #7:
-
- URI
/foo - IP:
127.0.0.1, Port:80, Host:symfony.com, Method:POST - Rule applied: не співпадає з жодним записом
- Why? Не співпадає з жодним з правил
access_control, так як його URI - не співпадає з жодним зі значень
path.
- URI
Warning
Співставлення URI відбувається без параметрів $_GET.
Відмовте у доступі в PHP-коді , якщо ви
хочете заборонити доступ, засновуючись на значеннях параметра $_GET.
2. Форсування доступу
Після того, як Symfony вирішила, який запис access_control співпадає (якщо такий є), вона
форсує обмеження доступу, засновані на опціях roles, allow_if і requires_channel:
rolesЯкщо користувач не має заданої ролі, то у доступі буде відмовлено (внутрішньо, викликаєтья AccessDeniedException);allow_ifЯкщо вираз повертає "false", то у доступі буде відмовлено;requires_channelЯкщо канал вхідного запиту (наприклад,http) не співпадає з цим значенням (наприклад,https), користувач буде перенаправлений (наприклад, перенаправлений зhttpнаhttps, або навпаки).
Tip
За лаштунками, значення масиву передається в якості аргументу $attributes
кожному виборцю у додатку з Request
як $subject. Ви можете дізнатися, як використовувати ваші користувацькі
атрибути, прочитавши .
Warning
Якщо ви визначите і roles, і allow_if, і ви використовуєте Стратегію вирішення
доступу за замовчуванням (affirmative), то користувачу буде надано доступ, якщо як
мінімум одна умова валідна. Якщо ця поведінка не підходить під ваші потреби,
змініть Стретегію вирішення доступу .
Tip
Якщо у доступі відмовлено, система спробує аутентифікувати користувача, якщо це ще не було зроблено (наприклад, перенаправити користувача на сторінку входу). Якщо користувач вже виконав виконав вхід, буде відображена сторінка помилка 403 "Доступ заборонено". Дивіться Як налаштувати сторінки помилок, щоб дізнатися більше.
Співставлення access_control за IP
Деякі ситуації можуть виникнути, коли вам потрібен запис access_control,
який співпадає лише із запитами, що виходять з якоїсь IP-адреси або їх спектру.
Наприклад, це мможе бути використано для відмови у доступі до URL-шаблону для
всіх запитів, окрім тих, що виходять з внутрішнього довіреного сервера.
Warning
Як ви прочитаєте у поясненні під прикладом, опція ips не обмежується
конкретною IP-адресою. Замість цього, викристання ключа ips означає, що
запис access_control співпадатиме лише з цією IP-адресою, а користувачі,
які отримують доступ до нього з інших IP-адрес, будуть йти далі за списком
access_control.
Ось приклад того як ви можете сконфігурувати деякий шаблон URL /internal* так,
щоб він був доступний лише за запитами з локального сервера:
1 2 3 4 5 6 7 8
# config/packages/security.yaml
security:
# ...
access_control:
#
# опція 'ips' підтримує IP-адреси та маски підмереж
- { path: '^/internal', roles: IS_AUTHENTICATED_ANONYMOUSLY, ips: [127.0.0.1, ::1, 192.168.0.1/24] }
- { path: '^/internal', roles: ROLE_NO_ACCESS }
Ось, як це працює, коли шлях /internal/something виходить від зовнішньої
IP-адреси 10.0.0.1:
- Перше правило контролю доступу ігнорується, так як
pathспівпадає, але IP-адреси не співпадають з жодним з перерахованих IP; - Включається друге правило контролю доступу (єдине обмеження -
path) і воно співпадає. Якщо ви переконаєтеся в тому, що жоден користувач не маєʼROLE_NO_ACCESS, то у доступі буде відмовлено (ROLE_NO_ACCESSможе бути чим завгодно, що не співпадає з існуючою роллю, воно просто служить споссобом завжди відмовляти у доступі).
Але якщо той же запит поступить від 127.0.0.1 або ::1 (адреса зворотного
звʼязку IPv6):
- Тепер перше правило контролю доступу включається, так як співпадає і
path, іip: доступ дозволено, так як користувач завжди має рольIS_AUTHENTICATED_ANONYMOUSLY. - Друге правило контролю доступу не розгллядається, так як співпало перше.
Убезпечення за виразом
Коли запис access_control співпадає, ви можете відмовити у доступрі через ключ
roles або використати складнішу логіку з виразом у ключі allow_if:
1 2 3 4 5 6 7 8 9 10
# config/packages/security.yaml
security:
# ...
access_control:
-
path: ^/_internal/secure
# опції 'roles' і 'allow_if' працюють як вираз ОС, тому доступ надається,
# якщо вираз - TRUE або якщо користувач має ROLE_ADMIN
roles: 'ROLE_ADMIN'
allow_if: "'127.0.0.1' == request.getClientIp() or request.headers.has('X-Secure-Access')"
У цьому випадку, коли користувач намагається отримати доступ до будь-якого URL,
що починається з /_internal/secure, він його отримає лише якщо IP-адреса
- 127.0.0.1 або якщо він має роль ROLE_ADMIN.
Note
Внутрішньо, allow_if запускає вбудований
ExpressionVoter,
ніби він є частиною атрибутів, визначених в опції roles.
Всередині виразу у вас є доступ до декількох різних змінних та функцій, включно з
request, яка є обʼєктом Symfony Request
(дивіться ).
Щоб побачити список інших функцій та змінних, дивіться функції та змінні .
Tip
Вирази allow_if можуть також містити користувацькі функції, зареєстровані
за допомогою постачальників виразів .
Обмеження за портом
Додайте опцію port до будь-якого запису access_control, щоб вимагати від
короистувачів отримання доступу до цих URL через конкретний порт. Це може бути корисно,
наприклад, для localhost:8080.
1 2 3 4 5
# config/packages/security.yaml
security:
# ...
access_control:
- { path: ^/cart/checkout, roles: PUBLIC_ACCESS, port: 8080 }
Форсування каналу (http, https)
Ви також можете зобовʼязати користувача отримувати доступ до URL через SSL; просто
використайте аргумент requires_channel у будь-яких записах access_control.
Якщо access_control співпаде, і запит використовує канал http, то користувач
буде перенаправлений на https:
1 2 3 4 5
# config/packages/security.yaml
security:
# ...
access_control:
- { path: ^/cart/checkout, roles: PUBLIC_ACCESS, requires_channel: https }