Як працює безпека access_control?
Дата оновлення перекладу 2024-05-29
Як працює безпека 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
: сервіс, що реалізуєRequestMatcherInterface
attributes
: масив, який може бути використано для вказання одного або більше атрибутів запиту , які мають точно співпастиroute
: імʼя маршруту
Візьміть наступні записи access_control
в якості прикладу:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
# 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 }
Для кожного вхідного запиту 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
Caution
Співставлення 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
. Ви можете дізнатися, як використовувати ваші користувацькі
атрибути, прочитавши .
Caution
Якщо ви визначите і roles
, і allow_if
, і ви використовуєте Стратегію вирішення
доступу за замовчуванням (affirmative
), то користувачу буде надано доступ, якщо як
мінімум одна умова валідна. Якщо ця поведінка не підходить під ваші потреби,
змініть Стретегію вирішення доступу .
Tip
Якщо у доступі відмовлено, система спробує аутентифікувати користувача, якщо це ще не було зроблено (наприклад, перенаправити користувача на сторінку входу). Якщо користувач вже виконав виконав вхід, буде відображена сторінка помилка 403 "Доступ заборонено". Дивіться Як налаштувати сторінки помилок, щоб дізнатися більше.
Співставлення access_control за IP
Деякі ситуації можуть виникнути, коли вам потрібен запис access_control
,
який співпадає лише із запитами, що виходять з якоїсь IP-адреси або їх спектру.
Наприклад, це мможе бути використано для відмови у доступі до URL-шаблону для
всіх запитів, окрім тих, що виходять з внутрішнього довіреного сервера.
Caution
Як ви прочитаєте у поясненні під прикладом, опція 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 }