Аутентификация прокси в браузере и гигиена учетных данных
Отделите учетные данные прокси от входа на сайт, не пускайте их в журналы и код, правильно кодируйте их в URL и безопасно разбирайте ошибки 407 и SOCKS.
Нужна структурированная документация по теме Сеть?
Эта статья относится к редакционной библиотеке. Для пошаговой настройки, справки и постоянных обновлений переходите сразу в соответствующий раздел docs.
Почему учетным данным прокси нужна отдельная работа
Браузер, который работает через прокси с аутентификацией, использует два не связанных между собой набора учетных данных. Учетные данные прокси показывают, что ваша организация вправе пользоваться маршрутом. Вход на сайте назначения показывает, что человек вправе пользоваться учетной записью на сайте. Их выдают разные стороны, они истекают по разным графикам, и утечка одного набора требует иных действий, чем утечка другого. Считать их одним «логином» значит допустить первую ошибку многих проверок учетных данных.
Различие носит практический, а не только концептуальный характер. Учетные данные прокси обычно принадлежат служебной учетной записи или панели провайдера, и ими пользуются задачи, работающие через этот маршрут. Вход на сайте назначения принадлежит пользователю или тестовой учетной записи и передается внутри трафика страницы. Если оба набора лежат в одном файле конфигурации или в одном секрете, ротация учетных данных прокси может заблокировать тестовую учетную запись, а смена пароля на сайте назначения может выглядеть как сбой прокси. Храните их в отдельных секретах, с разными владельцами и отдельными записями о ротации.
Эти рекомендации относятся к прокси, которыми ваша организация вправе пользоваться на условиях провайдера, выдавшего учетные данные. Они не касаются поиска, подбора, передачи или перепродажи учетных данных и не описывают способы обхода ограничений доступа провайдера. Если учетные данные вам не принадлежат, никакая практика обращения с ними не делает маршрут допустимым.
Названия протоколов тоже задают ожидания. Аутентификация HTTP Basic и метод имени пользователя и пароля в SOCKS5 лишь позволяют передать имя пользователя и пароль. Ни один из них сам по себе не обеспечивает конфиденциальность секрета: Basic использует кодирование, которое может обратить любой, а метод SOCKS5 передает значения как есть, если соединение не защищено чем-то другим. Конфиденциальность зависит от транспорта между браузером и прокси, от того, что фиксирует в журналах провайдер, и от того, как со значением обращаются ваши собственные инструменты. Эти элементы различаются от развертывания к развертыванию, поэтому проверка должна называть их, а не предполагать.
Мест, где учетные данные могут утечь, больше, чем кажется. Секрет может попасть в систему контроля версий, в командную строку, видимую в списке процессов, в журнал задачи, повторяющий аргументы запуска, в сообщение об ошибке с адресом прокси, в снимок экрана терминала, в обращение в поддержку или в общий файл конфигурации. Гигиена учетных данных означает, что для каждого из таких мест вы решаете, может ли там появляться секрет, и затем проверяете, что он там не появляется.
Настройка маршрутов описана в материале Настройка прокси, а то, как браузеры используют туннели CONNECT и заголовки прокси, объясняется в материале Семантика HTTP-прокси и запросы браузера. Здесь внимание сосредоточено на самих учетных данных: как их передают, записывают, хранят, меняют и не допускают туда, где им не место.
Как читать сбои аутентификации 407, 401 и SOCKS
Когда прокси требует аутентификацию, он отвечает на запрос статусом 407 Proxy Authentication Required и заголовком Proxy-Authenticate с названием принимаемой схемы. Клиент повторяет запрос с заголовком Proxy-Authorization. RFC 9110 определяет оба заголовка и статус, а страницы MDN о Proxy-Authorization и 407 кратко описывают их для веб-разработчиков. Важно понимать, кому принадлежит требование аутентификации: статус 407 выдает прокси, поэтому он указывает на участок прокси в маршруте.
Ответ 401 Unauthorized является собственным требованием сайта назначения. Он содержит заголовок WWW-Authenticate, а ответом на него служит заголовок Authorization. Оба обмена могут происходить при одной загрузке страницы, и в каждом используются свои учетные данные. Статус 407 означает, что прокси не принял учетные данные прокси. Статус 401 означает, что прокси принял маршрут, а сайт назначения не принял вход. Если их перепутать, расследование уйдет к неправильному владельцу и к неправильному секрету.
HTTPS-сайты добавляют еще одну деталь. Браузер сначала просит прокси открыть туннель запросом CONNECT, и аутентификация прокси происходит именно на этом запросе. TLS-сеанс сайта назначения и любое требование 401 появляются только после того, как туннель уже существует. Поэтому сбой аутентификации прокси на HTTPS-странице проявляется как неудачное открытие туннеля, часто до появления какого-либо содержимого страницы, а не как ответ сайта. Кроме того, Proxy-Authorization предназначен для прокси, который его запросил, и не передается дальше на сайт назначения.
HTTP Basic определен в RFC 7617. Клиент соединяет имя пользователя и пароль двоеточием и применяет Base64. Base64 является обратимым кодированием и не дает секретности; в RFC сказано, что сама по себе схема Basic не обеспечивает конфиденциальность и должна использоваться по защищенному соединению. Поскольку двоеточие служит разделителем, имя пользователя не может его содержать, а пароль может. Если учетные данные содержат такие символы, важно, как они записаны в URL.
SOCKS5 использует другой обмен. После того как клиент и сервер согласовали метод имени пользователя и пароля, определенный в RFC 1929, клиент отправляет имя пользователя и пароль, каждый длиной до 255 байт, а сервер отвечает статусом. Нулевой статус означает успех. Любое другое значение означает сбой, и сервер закрывает соединение. Браузер не получает страницу 407, потому что в SOCKS нет кодов состояния HTTP. Сбой аутентификации на маршруте SOCKS5 проявляется как отклоненное или закрытое соединение на этапе установки, поэтому текст ошибки обычно менее конкретен, чем HTTP-ответ. Сам метод значения не шифрует.
Ограниченный отчет о сбое аутентификации называет участок и категорию и ничего больше. Например: «аутентификация прокси отклонена для маршрута R, требование получено, туннель не открыт». В нем нет значения Proxy-Authorization, имени пользователя, полного URL прокси и тела ответа провайдера. Если сообщить исход как ошибку аутентификации, а не как истекшее время ожидания или общий сетевой сбой, следующее действие останется верным: проверить учетные данные и срок их действия, а не настройку DNS или сайт назначения.
Повторные попытки нуждаются в ограничении. Маршрут, который возвращает 407 после передачи верных учетных данных, как правило, продолжит это делать, а множество быстрых повторов может заблокировать учетную запись или включить защиту на стороне провайдера. Остановитесь после небольшого числа попыток, заданного вашей собственной политикой, пометьте маршрут как неработающий с категорией аутентификации и передайте его владельцу, который может обновить или сменить учетные данные. Не пробуйте другие учетные данные, не назначенные этому маршруту, в надежде найти рабочие.
Как записывать учетные данные в URL прокси
URL прокси следует общему синтаксису из RFC 3986. Учетные данные помещаются в компонент userinfo перед хостом: схема, затем имя пользователя, двоеточие, пароль, символ «собака», хост и порт. Тот же RFC называет форму «имя пользователя и пароль» в userinfo устаревшей, потому что передача аутентификационной информации открытым текстом оказалась риском для безопасности. Это повод обращаться со строкой осторожно, а не повод записывать ее иначе, поскольку многие интерфейсы запуска ожидают именно такую форму.
Ряд символов имеет в URL особое значение и должен кодироваться процентной записью, когда встречается в имени пользователя или пароле. Символ «собака» завершает userinfo, поэтому буквальный символ в пароле был бы прочитан как начало хоста. Косая черта, вопросительный знак и знак номера завершают часть authority. Знак процента начинает закодированное значение, поэтому буквальный знак тоже нужно кодировать. Двоеточие отделяет имя пользователя от пароля. Кодирование символа означает замену его знаком процента и двузначным шестнадцатеричным кодом.
Например, пароль p@ss:word записывается в URL как p%40ss%3Aword, а полное значение может выглядеть так: http://svc_user:p%40ss%3Aword@proxy.example.com:8080. Хост в этом примере является условным значением для документации. Кодируйте имя пользователя и пароль по отдельности и только потом собирайте URL, потому что кодирование готового URL изменило бы и его разделители. Проверенная функция из стандартной библиотеки вашего языка надежнее самодельных замен, например encodeURIComponent в JavaScript или urllib.parse.quote с пустым набором безопасных символов в Python.
Инструменты разбирают userinfo немного по-разному, поэтому значение, работающее в одном клиенте, может не сработать в другом. После кодирования проверьте точную строку в том инструменте, который будет ее использовать, и считайте ошибку разбора проблемой конфигурации. Не ослабляйте кодирование, чтобы неработающая строка прошла. Пароль со знаком плюса, пробелом или текстом вне ASCII заслуживает отдельной проверки, потому что кодировщики расходятся в обработке таких символов; в URL пробел записывается как %20, а знак плюса относится к кодированию форм.
Неверное кодирование дает вводящий в заблуждение сбой. Если некодированный символ «собака» разделит userinfo не в том месте, инструмент может отправить прокси усеченный пароль и получить 407 либо попытаться разрешить несуществующий хост и сообщить о сетевой ошибке. В обоих случаях сами учетные данные были верными. Когда сбой появляется сразу после смены пароля, сравните закодированную строку, прежде чем подозревать провайдера.
Если провайдер поддерживает другой метод аутентификации, например прием запросов с одобренного исходного адреса, возможно, вы сможете запускать с URL без userinfo. Это убирает секрет из командной строки, но переносит доверие на адрес и его владельца, поэтому зафиксируйте это как отдельный метод с собственным владельцем и собственной проверкой. Некоторые провайдеры такого не предлагают, а условия провайдера определяют, вправе ли вы им пользоваться.
Не оставляйте буквальный URL в местах, которые хранятся долго. Исходные файлы, образы контейнеров, история оболочки и комментарии в обращениях сохраняют значения задолго после смены учетных данных. В таких местах вместо значения должна находиться ссылка на секрет.
Хранение, ротация и маскирование учетных данных
Храните каждые учетные данные прокси в менеджере секретов или в равноценном хранилище, которое ваша организация уже контролирует, и обращайтесь к ним по имени. Тогда запись запуска содержит метку маршрута, например «regional-checks-eu», и ссылку на секрет, такую как имя сохраненной записи, а не буквальный URL прокси. Любой, кто читает запись, понимает, какой маршрут использовался и кто им владеет, но не может им воспользоваться.
Разрешайте ссылку как можно позже. Средство запуска получает значение, кодирует имя пользователя и пароль, собирает URL, запускает браузер и не сохраняет значение ни в собственном состоянии, ни в журналах. Собирайте URL в минимально возможной области видимости и не записывайте его в файл, который переживет запуск. Благодаря этому то же определение задачи остается верным и после ротации, ведь меняется только сохраненное значение.
Трезво оценивайте, что раскрывает аргумент командной строки. Аргумент запуска виден другим учетным записям на том же хосте, которые могут просматривать список процессов, и может быть перехвачен агентами мониторинга, отчетами о сбоях или выводом инспекции среды контейнеров. Поэтому размещение учетных данных в аргументе не делает их закрытыми на этом хосте. Ограничьте круг тех, кто может входить на машину и читать сведения о ее процессах, и отдавайте предпочтение учетным данным, привязанным к одному маршруту, ограниченным по возможностям и краткоживущим, если провайдер это поддерживает.
Маскирование является отдельным средством контроля, и его нужно проверять, а не предполагать. Журналы задач, обертки-скрипты, обработчики ошибок и отчеты о тестах часто печатают аргументы запуска при сбое. Маскируйте userinfo до записи строки, заменяйте пароль фиксированным маркером и маскируйте также закодированную форму, потому что в журнале может оказаться любая из двух. После неудачного запуска прочитайте реальные выходные файлы и список процессов и найдите в них имя пользователя и пароль как в исходном, так и в закодированном виде.
Ротации нужны владелец и план. Решите, как часто заменяются учетные данные, кто может их заменить и как новое значение попадает в средство запуска. Замените сохраненное значение, перезапустите контексты, использующие этот маршрут, и убедитесь, что старое значение больше не работает. Ротация из-за подозрения на утечку требует также проверить, куда записывалось старое значение, поскольку журналы и обращения могут хранить его и после отзыва провайдером.
Дайте каждому маршруту собственные учетные данные и собственную ссылку. Когда одни общие учетные данные обслуживают много маршрутов и контекстов, одна ротация прерывает эти маршруты одновременно, а одна утечка затрагивает каждый из них. Раздельные ссылки позволяют заменить учетные данные одного маршрута, пока остальные продолжают работать со своими. Если провайдер привязывает учетные данные к тарифу или субаккаунту, повторите эту структуру в своих именах.
Факты на стороне провайдера остаются у провайдера. Как долго провайдер хранит журналы запросов, записывает ли имя пользователя, защищено ли соединение с прокси протоколом TLS и как истекают учетные данные, зависит от конкретного провайдера. Запросите эти сведения письменно и сохраните ответы вместе с маршрутом. Не считайте, что безопасно выглядящая схема в URL означает шифрование первого участка: прокси HTTPS защищает соединение с прокси, прокси HTTP его не защищает, а маршрут SOCKS5 требует собственной оценки.
Не храните входы на сайты назначения в этом хранилище. Пароль учетной записи на сайте назначения принадлежит владельцу записи и приложению, которое выполняет вход, и имеет собственное хранение и собственную ротацию. Если положить его рядом с учетными данными прокси, расширяется круг тех, кто может прочитать каждый из них, и связываются две не связанные между собой ротации.
Операционная проверка
Рассматривайте обращение с учетными данными как свойство, которое проверяют после изменений, а не как этап настройки, завершаемый один раз. Повторяйте проверку после смены провайдера, смены тарифа, ротации учетных данных, обновления средства запуска, изменения журналирования, крупного обновления браузера или добавления нового маршрута. Записывайте, что вы проверили, метку маршрута, ссылку на секрет и результат, не копируя секрет в запись.
Распределяйте роли так же, как маршруты. Владелец маршрута решает, какие учетные данные обслуживают какой контекст, и утверждает ротации. Владелец платформы управляет хранилищем секретов и конвейером журналов. Владелец приложения определяет, что видит пользователь, когда маршрут недоступен. Короткая передача дел между этими тремя владельцами является более весомым свидетельством, чем общий документ с вставленными в него учетными данными.
Определите видимый исход сбоя аутентификации до того, как он произойдет. Задача может остановиться с понятным сообщением о том, что маршруту требуется внимание, функция может показать состояние недоступности, а утвержденный альтернативный маршрут может взять трафик на себя, если это разрешает ваша политика. Прямое соединение, которое случайно загрузило страницу, не является неявной альтернативой. Оно меняет границу сети и источник трафика без решения, поэтому не должно становиться молчаливым итогом сбоя учетных данных.
BotBrowser поддерживает учетные данные прокси, встроенные в URL --proxy-server, для маршрутов HTTP, HTTPS, SOCKS5, SOCKS5H и QUIC с процентным кодированием специальных символов, поэтому оператор может передать при запуске учетные данные утвержденного маршрута без page.authenticate(). BotBrowser не предоставляет хранение секретов, ротацию и маскирование в журналах и не делает встроенные учетные данные закрытыми от списка процессов или журналов провайдера; эти средства контроля остаются за вашими инструментами развертывания и провайдером прокси.
Маршрутизация по контексту помогает держать ссылки раздельно. Когда одно развертывание браузера обслуживает несколько утвержденных рабочих процессов, каждый контекст может использовать собственный маршрут и, значит, собственные учетные данные, как описано в материале Прокси для контекста. Эта настройка выбирает среди маршрутов, которые ваше развертывание уже квалифицировало; она не выдает учетные данные и не меняет того, что разрешает провайдер.
Когда маршрут не проходит аутентификацию, ответственные действия состоят в том, чтобы обновить или сменить учетные данные через их владельца, выбрать другой утвержденный маршрут или остановить рабочий процесс. Поиск учетных данных в других местах, использование чужих у другой команды или переход на другую учетную запись провайдера без согласования к ним не относятся. В записи должно быть видно, какое действие выполнено и кем.
Сообщения о состоянии и ответы поддержки должны использовать тот же словарь, что и запись. Формулировка «аутентификация прокси отклонена» точнее и полезнее, чем «сетевая ошибка», а «требуется вход на сайте назначения» указывает, что действовать должен другой владелец. Точная формулировка не дает отправить инцидент не той команде и не пускает секреты в обращения, которые могут читать многие люди.
Выполните проверки гигиены учетных данных
Применяйте эти проверки к каждому маршруту и фиксируйте результат «пройдено» или «не пройдено» для каждой, не копируя секрет в запись.
- Проследите неудачный запрос до его участка. Пройдено, если 407 с требованием Proxy-Authenticate отнесен к участку прокси, 401 с требованием WWW-Authenticate отнесен к сайту назначения, а в записи нет ни значения Proxy-Authorization, ни значения Authorization. Не пройдено, если оба случая слиты в одну категорию «вход не удался».
- Прочитайте запись запуска. Пройдено, если в ней есть метка маршрута и ссылка на секрет. Не пройдено, если в ней есть буквальный URL прокси, имя пользователя или пароль.
- Найдите имя пользователя и пароль в исходном и процентно закодированном виде в журналах задач, выводе ошибок и списке процессов обычного запуска и намеренно неудачного запуска. Пройдено, если в журналах и выводе ошибок совпадений нет, а в списке процессов совпадений нет или он доступен только одобренным вами учетным записям. Не пройдено, если учетные данные появляются в журнале, в выводе ошибок или в списке процессов, который могут читать другие учетные записи.
- Запустите с тестовым паролем, содержащим зарезервированные символы, такие как «собака», двоеточие, косая черта и знак процента. Пройдено, если пароль закодирован процентной записью в URL и маршрут проходит аутентификацию. Не пройдено, если соединение работает только после ослабления кодирования.
- Передайте намеренно неверные или просроченные учетные данные. Пройдено, если запуск остановился в пределах вашего лимита попыток и сообщил об ошибке аутентификации для названного маршрута. Не пройдено, если сообщается об истекшем времени ожидания, ошибке DNS или общем сетевом сбое либо если повторы идут без лимита.
- Смените учетные данные одного маршрута. Пройдено, если этот маршрут проходит аутентификацию с новым значением, старое значение отклоняется, а остальные маршруты и контексты продолжают работать со своими ссылками. Не пройдено, если ротация одного маршрута меняет исход другого.
- Повторите проверки с 1 по 6 после смены провайдера, смены тарифа, обновления средства запуска или крупного обновления браузера. Сохраняйте последнюю принятую запись, пока повторный прогон не будет пройден.
Источники
- RFC 9110: HTTP Semantics
- RFC 7617: The Basic HTTP Authentication Scheme
- RFC 1929: аутентификация по имени пользователя и паролю в SOCKS версии 5
- RFC 3986: Uniform Resource Identifier (URI) Generic Syntax
- MDN: заголовок Proxy-Authorization
- MDN: 407 Proxy Authentication Required
- BotBrowser: настройка прокси
Похожие статьи
Переведите BotBrowser из исследований в продакшн
Используйте эти руководства, чтобы понять модель, а затем перейти к кроссплатформенной валидации, изолированным контекстам и масштабируемому браузерному развертыванию.