10.12. CORS и CSRF#

CORS и CSRF – это два механизма защиты на стороне браузера, которые нужны камере, открытой в интернет, наряду с HTTPS и авторизацией. Каждый настраивается несколькими строками. Разделы ниже определяют термин и показывают интеграцию с microdot.

10.12.1. Что делает CORS#

Cross-Origin Resource Sharing (CORS) – это механизм браузера, позволяющий серверу явно разрешить определённым другим источникам читать его ответы. Стандартная политика одного источника браузера блокирует такое чтение: JavaScript на https://example.com не может читать ответы от https://yard-cam.example.com, потому что другой хост считается другим источником. CORS – это способ на стороне сервера предоставлять исключения для выбранных партнёров.

Если панель управления раздаётся самой камерой, каждый запрос имеет тот же источник, и CORS ничего не делает. Эта настройка важна, когда панель управления находится где-то ещё – публичный URL вроде https://app.example.com, который общается с камерой по адресу https://yard-cam.example.com:

from microdot.cors import CORS

cors = CORS(
    app,
    allowed_origins=['https://app.example.com'],
    allow_credentials=True,
    max_age=86400,
)

allowed_origins – это список источников, которым разрешено читать ответы камеры. Источник панели управления и только он – не * – чтобы сторонний сайт не мог случайно прочитать ответы камеры.

allow_credentials=True позволяет межсайтовым запросам включать cookie сессии, что нужно панели управления, чтобы оставаться авторизованной через границу источников.

max_age=86400 сообщает браузеру, что он может кешировать результат предзапроса в течение суток. Браузеры отправляют дополнительный запрос OPTIONS перед любым межсайтовым вызовом, использующим методы помимо GET/HEAD/POST или отправляющим пользовательские заголовки; max_age сокращает эти накладные расходы до одного предзапроса в сутки на каждый маршрут.

10.12.2. Что делает CSRF#

Cross-Site Request Forgery (CSRF) – это атака, при которой вредоносная страница заставляет браузер пользователя отправить аутентифицированный запрос доверенному серверу. Даже при включённом CORS скрытая <form> на evil.com, отправляющая POST на https://yard-cam.example.com/config, дойдёт до камеры, и браузер прикрепит cookie сессии камеры – cookie следуют за хостом назначения, а не за источником страницы, делающей запрос – так что камера спокойно обработает POST, как если бы он исходил от владельца.

Защита от CSRF отклоняет такие запросы. microdot.csrf.CSRF добавляет промежуточное ПО, которое проверяет заголовок Sec-Fetch-Site при каждом запросе, изменяющем состояние, и отклоняет всё, что не помечено same-origin (или не приходит от источника, разрешённого в CORS):

from microdot.csrf import CSRF

CSRF(app, cors=cors)

Передача экземпляра cors позволяет промежуточному ПО автоматически пропускать разрешённый источник панели управления – камера по-прежнему принимает POST-запросы от панели, даже если они межсайтовые.

Sec-Fetch-Site устанавливается современными браузерами автоматически; камере не нужно делать ничего на стороне клиента. Для старых браузеров, которые не отправляют этот заголовок, резервной проверкой служит белый список CORS.

10.12.3. Исключения для веб-хуков#

Если камере нужна конечная точка веб-хука, чтобы принимать POST-запросы от стороннего облачного сервиса – скажем, обратный вызов от поставщика архива, – пометьте маршрут как @csrf.exempt, чтобы промежуточное ПО его пропускало. Обработчик отвечает за проверку запроса каким-то другим способом – обычно это Hash-based Message Authentication Code (HMAC) по полезной нагрузке, вычисленный с секретом, общим для камеры и третьей стороны, что доказывает, что запрос пришёл от того, кто знал секрет. У камеры на заднем дворе ничего такого нет, но декоратор есть на случай, когда он понадобится.

10.12.4. Базовая настройка из четырёх строк#

После того как HTTPS включён, рекомендуемый стек для любого развёртывания камеры, обращённой в интернет:

Session(app, secret_key=SECRET,
        cookie_options={'http_only': True, 'secure': True})
login = Login()
cors = CORS(app, allowed_origins=[...], allow_credentials=True)
CSRF(app, cors=cors)

Сессия и авторизация – ранее в главе, CORS и CSRF – здесь, HTTPS – из предыдущей темы. Эти четыре части складываются друг на друга и не мешают каждому маршруту.

Теперь камеру безопасно открывать в публичный интернет – HTTPS, авторизация, CSRF, CORS.