Я пишу прокладку аутентификации / авторизации перед моим HTTP-сервером, которая обнаружит отсутствие токена авторизации - либо токена Bearer в поле Authorization, либо зашифрованного токена в файле cookie - и отказаться от удовлетворения запроса. Эта прокладка будет перехватывать каждый запрос ресурса от исходного сервера (это функция Lambda @ Edge для CDN AWS CloudFront, перед статическим контентом S3, для контекста).

В случае, когда запрос был сделан, потому что пользователь перешел на URL, либо введя его в браузере, либо перейдя по ссылке, я хочу, чтобы ответ вызвал перенаправление на мою страницу аутентификации (которая в конечном итоге вернет пользователя к исходному URL, на этот раз с токеном авторизации). Это поток OAuth2, в отдельном домене защищенный домен не обрабатывает ввод имени пользователя / пароля.

В случае, когда запрос был сделан браузером, просто загружающим ресурс, на который ссылается страница (например, изображение, встроенное в страницу, или файлы Javascript / CSS), я хочу просто отказаться от ответа 401/403 в зависимости от ситуации.

То, с чем я борюсь, - это как по заголовкам запросов определить разницу между этими двумя случаями. Есть ли канонический способ сделать это? Такое ощущение, что заголовок Accept должен быть моим руководством в этом, но мне нужно, чтобы это работало, даже если пользователь ввел URL ресурса изображения в свой браузер (т.е. я не могу просто перенаправить, только если {{ X1}} включает text/html). Или я могу рассчитывать на присутствие */*, чтобы указать, что это запрос на навигацию, который заранее не знает, каким будет Content-Type?

Или это просто плохая практика, по какой-то причине я не вижу, и я должен либо всегда перенаправлять, либо всегда отказываться?

4
MrCranky 20 Апр 2020 в 23:19

2 ответа

Лучший ответ

В отсутствие ответа, который на самом деле решает проблему, я подумал, что должен ответить тем, с чем я неохотно остановился.

Хотя технически может быть правильным, что отсутствие действительных учетных данных должно привести к ответу 401/403 для ресурса, это также относится и к загрузке навигации. Если вы отвечаете 307 для перенаправления для аутентификации, вы уже подрываете это ожидание.

На практике важно то, что видит конечный пользователь: когда браузер пытается загрузить встроенный ресурс, но возвращает 307, он отправляется и выбирает HTML-страницу формы входа, на которую он был перенаправлен. Поскольку это не ресурс изображения (или что-то еще ожидаемое), а также потому, что он изменил тип содержимого на что-то, что может быть вредоносным, современные браузеры блокируют его на основе CORB. Я не уверен, что делают старые браузеры, но я сомневаюсь, что они отображают HTML вместо img или любого другого элемента, указанного на странице. Чистый результат одинаков в обоих случаях - вместо встроенного ресурса пользователь видит неработающую ссылку.

Сравните это с нагрузкой, возникающей в результате действия навигации, когда браузер с радостью перенаправит и покажет HTML-форму входа в систему, как и предполагалось. Это может быть немного затруднительно, если вы вставили страницу, которая будет перенаправлена на другую страницу, но мы уже получили X-Frame-Options: DENY в нашей форме входа в систему из соображений безопасности, чтобы в этом случае она не работала.

Таким образом, в отсутствие хорошего способа сделать это «должным образом», вы можете просто выбрать перенаправление во всех случаях, и вы получите конечный результат, который, возможно, в порядке. Было бы гораздо приятнее, если бы сам HTTP имел более изящную интеграцию, например, возможность добавить перенаправление в ответе 401/403 и отправить браузеру «куда-то еще», чтобы получить токен Bearer, но есть причины, по которым я не могу понять, почему это плохая идея

0
MrCranky 6 Май 2020 в 11:23

Не существует 100% способа отличить прямой запрос пользователя от автоматического запроса браузера, потому что они оба сделаны браузером. Но вы можете использовать несколько методов.

  1. Во-первых, это разделить ваши ресурсы на 2 класса.

    • Один класс - это HTML-страницы (обычно они могут быть обнаружены по имени ресурса. Например, по расширению .php , .aspx и т. д. или вообще без расширения). , Пользователи во время их типичного рабочего процесса обычно запрашивают только HTML-страницы. Эти ресурсы перенаправляют неавторизованные запросы на страницу авторизации.
    • Другой класс - это все остальные ресурсы (js, css, images и т. д.). Они просто отклоняют все несанкционированные запросы с кодом 401/403.
  2. Второй подход - пометить все автоматически загружаемые ресурсы на вашей веб-странице специальной пометкой (например, добавив параметр запроса в uri , например ?auto=1). И тогда все запросы с таким параметром будут считаться автоматическими. Маркировка ресурсов наиболее проста при генерации страниц на бэкэнде или веб-интерфейсе.

  3. Третий подход - это расширение первого. Кроме того, мы используем заголовок Referer HTTP-запроса. Он идентифицирует адрес веб-страницы, которая связана с запрашиваемым ресурсом.

    • Когда пользователь вводит URL-адрес непосредственно в поле адреса браузера, Referer заголовок отсутствует в запросе.
    • Когда браузер запрашивает изображение, css и т. д. Referer содержит URL-адрес веб-страницы с этим ресурсом.
    • Когда пользователь перемещается между страницами, он содержит URL предыдущей страницы.

Таким образом, мы можем рассмотреть все веб-страницы и изображения с пустым заголовком Referer по запросу. И все ресурсы (кроме веб-страниц) с Referer - как автоматический запрос.

К сожалению, Referer заголовок ненадежен - браузеры иногда не отправляют этот заголовок по некоторым причинам - политики конфиденциальности, по соображениям безопасности и т. д. Вы можете попробовать это на свой страх и риск.

1
Alexander Ushakov 5 Май 2020 в 14:19