mirror of https://github.com/tiangolo/fastapi.git
108 lines
7.4 KiB
Markdown
108 lines
7.4 KiB
Markdown
# HTTP Basic Auth { #http-basic-auth }
|
||
|
||
Для самых простых случаев можно использовать HTTP Basic Auth.
|
||
|
||
При HTTP Basic Auth приложение ожидает HTTP-заголовок, который содержит имя пользователя и пароль.
|
||
|
||
Если его нет, возвращается ошибка HTTP 401 «Unauthorized».
|
||
|
||
Также возвращается заголовок `WWW-Authenticate` со значением `Basic` и необязательным параметром `realm`.
|
||
|
||
Это говорит браузеру показать встроенное окно запроса имени пользователя и пароля.
|
||
|
||
Затем, когда вы вводите эти данные, браузер автоматически отправляет их в заголовке.
|
||
|
||
## Простой HTTP Basic Auth { #simple-http-basic-auth }
|
||
|
||
* Импортируйте `HTTPBasic` и `HTTPBasicCredentials`.
|
||
* Создайте «схему» `security` с помощью `HTTPBasic`.
|
||
* Используйте эту `security` как зависимость в вашей *операции пути*.
|
||
* Она возвращает объект типа `HTTPBasicCredentials`:
|
||
* Он содержит отправленные `username` и `password`.
|
||
|
||
{* ../../docs_src/security/tutorial006_an_py39.py hl[4,8,12] *}
|
||
|
||
Когда вы впервые откроете URL (или нажмёте кнопку «Execute» в документации), браузер попросит ввести имя пользователя и пароль:
|
||
|
||
<img src="/img/tutorial/security/image12.png">
|
||
|
||
## Проверка имени пользователя { #check-the-username }
|
||
|
||
Вот более полный пример.
|
||
|
||
Используйте зависимость, чтобы проверить, корректны ли имя пользователя и пароль.
|
||
|
||
Для этого используйте стандартный модуль Python <a href="https://docs.python.org/3/library/secrets.html" class="external-link" target="_blank">`secrets`</a> для проверки имени пользователя и пароля.
|
||
|
||
`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_py39.py hl[1,12:24] *}
|
||
|
||
Это было бы похоже на:
|
||
|
||
```Python
|
||
if not (credentials.username == "stanleyjobson") or not (credentials.password == "swordfish"):
|
||
# Вернуть ошибку
|
||
...
|
||
```
|
||
|
||
Но используя `secrets.compare_digest()`, вы защитите код от атак типа «тайминговая атака» (атака по времени).
|
||
|
||
### Тайминговые атаки { #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 (тем же, что и при отсутствии учётных данных) и добавьте HTTP-заголовок `WWW-Authenticate`, чтобы браузер снова показал окно входа:
|
||
|
||
{* ../../docs_src/security/tutorial007_an_py39.py hl[26:30] *}
|