mirror of https://github.com/tiangolo/fastapi.git
108 lines
7.6 KiB
Markdown
108 lines
7.6 KiB
Markdown
# HTTP Basic Auth { #http-basic-auth }
|
||
|
||
Для найпростіших випадків ви можете використовувати HTTP Basic Auth.
|
||
|
||
У HTTP Basic Auth застосунок очікує заголовок, що містить ім'я користувача та пароль.
|
||
|
||
Якщо він його не отримує, повертається помилка HTTP 401 «Unauthorized».
|
||
|
||
І повертається заголовок `WWW-Authenticate` зі значенням `Basic` та необов'язковим параметром `realm`.
|
||
|
||
Це каже браузеру показати вбудовану підсказку для введення імені користувача та пароля.
|
||
|
||
Потім, коли ви введете це ім'я користувача та пароль, браузер автоматично надішле їх у заголовку.
|
||
|
||
## Простий HTTP Basic Auth { #simple-http-basic-auth }
|
||
|
||
- Імпортуйте `HTTPBasic` і `HTTPBasicCredentials`.
|
||
- Створіть «`security` scheme» за допомогою `HTTPBasic`.
|
||
- Використайте цей `security` як залежність у вашій операції шляху.
|
||
- Він повертає об'єкт типу `HTTPBasicCredentials`:
|
||
- Він містить надіслані `username` і `password`.
|
||
|
||
{* ../../docs_src/security/tutorial006_an_py310.py hl[4,8,12] *}
|
||
|
||
Коли ви спробуєте відкрити URL вперше (або натиснете кнопку «Execute» у документації), браузер попросить вас ввести ім'я користувача та пароль:
|
||
|
||
<img src="/img/tutorial/security/image12.png">
|
||
|
||
## Перевірте ім'я користувача { #check-the-username }
|
||
|
||
Ось більш повний приклад.
|
||
|
||
Використайте залежність, щоб перевірити, чи правильні ім'я користувача та пароль.
|
||
|
||
Для цього використайте стандартний модуль Python [`secrets`](https://docs.python.org/3/library/secrets.html), щоб перевірити ім'я користувача та пароль.
|
||
|
||
`secrets.compare_digest()` повинен отримувати `bytes` або `str`, що містить лише ASCII-символи (англійські), це означає, що він не працюватиме з символами на кшталт `á`, як у `Sebastián`.
|
||
|
||
Щоб це обійти, ми спочатку перетворюємо `username` і `password` у `bytes`, кодувавши їх у UTF-8.
|
||
|
||
Потім ми можемо використати `secrets.compare_digest()`, щоб упевнитися, що `credentials.username` дорівнює `"stanleyjobson"`, а `credentials.password` дорівнює `"swordfish"`.
|
||
|
||
{* ../../docs_src/security/tutorial007_an_py310.py hl[1,12:24] *}
|
||
|
||
Це було б подібно до:
|
||
|
||
```Python
|
||
if not (credentials.username == "stanleyjobson") or not (credentials.password == "swordfish"):
|
||
# Поверніть якусь помилку
|
||
...
|
||
```
|
||
|
||
Але використовуючи `secrets.compare_digest()`, це буде захищено від типу атак, що називаються «атаки за часом» (timing attacks).
|
||
|
||
### Атаки за часом { #timing-attacks }
|
||
|
||
Що таке «атака за часом»?
|
||
|
||
Уявімо, що зловмисники намагаються вгадати ім'я користувача та пароль.
|
||
|
||
Вони надсилають запит з ім'ям користувача `johndoe` та паролем `love123`.
|
||
|
||
Тоді Python-код у вашому застосунку буде еквівалентний чомусь на кшталт:
|
||
|
||
```Python
|
||
if "johndoe" == "stanleyjobson" and "love123" == "swordfish":
|
||
...
|
||
```
|
||
|
||
Але в той момент, коли Python порівнює першу `j` у `johndoe` з першою `s` у `stanleyjobson`, він поверне `False`, тому що вже знає, що ці дві строки не однакові, «немає сенсу витрачати обчислення на порівняння решти літер». І ваш застосунок скаже «Невірні ім'я користувача або пароль».
|
||
|
||
А потім зловмисники спробують з ім'ям користувача `stanleyjobsox` і паролем `love123`.
|
||
|
||
І ваш код застосунку зробить щось на кшталт:
|
||
|
||
```Python
|
||
if "stanleyjobsox" == "stanleyjobson" and "love123" == "swordfish":
|
||
...
|
||
```
|
||
|
||
Python доведеться порівняти весь `stanleyjobso` у обох `stanleyjobsox` і `stanleyjobson`, перш ніж зрозуміти, що строки різні. Тому відповідь «Невірні ім'я користувача або пароль» займе на кілька мікросекунд довше.
|
||
|
||
#### Час відповіді допомагає зловмисникам { #the-time-to-answer-helps-the-attackers }
|
||
|
||
У цей момент, помітивши, що сервер витратив на кілька мікросекунд більше, щоб надіслати відповідь «Невірні ім'я користувача або пароль», зловмисники знатимуть, що вони щось вгадали, деякі початкові літери правильні.
|
||
|
||
І тоді вони можуть спробувати знову, знаючи, що це, ймовірно, щось більш схоже на `stanleyjobsox`, ніж на `johndoe`.
|
||
|
||
#### «Професійна» атака { #a-professional-attack }
|
||
|
||
Звісно, зловмисники не робитимуть усе це вручну, вони напишуть програму, можливо з тисячами або мільйонами перевірок за секунду. І вони отримуватимуть лише по одній правильній літері за раз.
|
||
|
||
Але так за кілька хвилин або годин зловмисники вгадають правильні ім'я користувача та пароль, «за допомогою» нашого застосунку, просто використовуючи час, потрібний для відповіді.
|
||
|
||
#### Виправте за допомогою `secrets.compare_digest()` { #fix-it-with-secrets-compare-digest }
|
||
|
||
Але в нашому коді ми насправді використовуємо `secrets.compare_digest()`.
|
||
|
||
Коротко, він витрачає однаковий час на порівняння `stanleyjobsox` зі `stanleyjobson`, як і на порівняння `johndoe` зі `stanleyjobson`. І так само для пароля.
|
||
|
||
Таким чином, використовуючи `secrets.compare_digest()` у коді вашого застосунку, він буде захищений від усього цього класу атак безпеки.
|
||
|
||
### Поверніть помилку { #return-the-error }
|
||
|
||
Після виявлення, що облікові дані неправильні, поверніть `HTTPException` з кодом статусу 401 (такий самий повертається, коли облікові дані не надані) і додайте заголовок `WWW-Authenticate`, щоб браузер знову показав підсказку входу:
|
||
|
||
{* ../../docs_src/security/tutorial007_an_py310.py hl[26:30] *}
|