Автентифікація відповідає на питання «хто ви», авторизація - «що вам дозволено». Більшість реальних витоків даних - не злам паролів, а відсутня чи неповна перевірка доступу: OWASP Top 10 ставить «Broken Access Control» на перше місце.
Заборонено за замовчуванням (deny by default): доступ дозволяється, лише якщо є явне правило, що його дозволяє. Будь-яка непередбачена ситуація - відмова.
// погано: дозволено всім, крім явно заборонених
if ($user->is_banned) {
abort(403);
}
// добре: заборонено всім, крім явно дозволених
if (! $user->can('update', $post)) {
abort(403);
}
Чому це важливо: при «дозволено за замовчуванням» кожна забута перевірка - вразливість. При «заборонено за замовчуванням» забута перевірка - лише непрацююча функція, яку помітять і виправлять.
Перевірки - лише на сервері. Приховати кнопку «Видалити» в інтерфейсі - це UX, а не захист:
- запит можна відправити напряму (
curl, консоль браузера, змінений клієнт); - мобільний застосунок можна декомпілювати й побачити всі ендпойнти;
- у Livewire будь-який публічний метод компонента можна викликати з браузера.
Сервер має перевіряти права на кожну дію, незалежно від того, що показує інтерфейс.
Практичні принципи:
- перевірка на кожен запит, а не лише при вході на сторінку: права могли змінитися, а запит - бути підробленим;
- централізовано: у Laravel - політики й гейти, а не розкидані
if ($user->role === 'admin')по контролерах; - найменші права: користувач і сервіс отримують лише ті можливості, що потрібні для роботи;
- перевіряти конкретний об'єкт, а не лише тип дії: «може редагувати пости» ≠ «може редагувати цей пост»;
- відмова - без зайвих деталей:
403чи404без пояснення, які саме права відсутні.
Тестування: для кожної дії - тест, що користувач без прав отримує відмову. Позитивні сценарії перевіряються природно під час розробки, а відсутність заборони помічають лише тоді, коли її шукають.