Корпоративный прокси и приложения Windows — как WinINET, WinHTTP и .NET выбирают прокси

· Обновлено: · · Windows, Прокси, WinHTTP, WinINET, .NET, HttpClient, PAC, WPAD, Сеть

История изменений (1 обновлений, последнее 31 Aug 2026)

Журнал изменений этой статьи. Там, где версия до правки была заархивирована, она остаётся доступной для чтения по постоянной ссылке с DOI.

Русский текст переписан по текущему навыку технического перевода как полный перевод японского оригинала.
Первая публикация
Цитирование статьи(DOI (зарегистрированный архив): 10.5281/zenodo.22176230)

Приведённые ниже DOI относятся к ранее зарегистрированным архивным версиям, которые могут отличаться от текущего текста. Для ссылки на текущий текст используйте URL этой страницы.

Го Комура (2026). Корпоративный прокси и приложения Windows — как WinINET, WinHTTP и .NET выбирают прокси. KomuraSoft LLC. https://comcomponent.com/ru/blog/windows-proxy-wininet-winhttp-dotnet/

DOI (зарегистрированный архив)
10.5281/zenodo.22176230
DOI (последняя зарегистрированная версия)
10.5281/zenodo.22176231

«Браузер открывает внешние сайты, а бизнес-приложение не достучится до внешнего API». «На машине разработчика всё работает, а в сети заказчика — таймаут». «Если запустить вручную, связь есть; как только ставишь службу Windows — падает». Когда бизнес-приложение крутится в среде с корпоративным прокси, такие обращения — почти классика.

В большинстве случаев дело не в поломке прокси-сервера и не в баге приложения. В Windows то, что называют «параметрами прокси», живёт в нескольких независимых источниках, и какой из них читает процесс, зависит от HTTP-стека приложения и от учётной записи, от которой оно запущено. Параметры, которые читает браузер, которые читает служба и которые читает HttpClient в .NET, могут не совпадать. Как только эта схема ясна, разбор «в браузере работает, а в приложении нет» идёт заметно быстрее.

Статья для ИТ-специалистов малых и средних компаний и разработчиков Windows-приложений. Здесь в одной картине: три источника параметров прокси — WinINET, WinHTTP и переменные окружения; автоконфигурация PAC и WPAD; разница выбора прокси в .NET Framework и в .NET (Core и новее); прокси с аутентификацией (407); инспекция TLS; практическая диагностика. Как создавать HttpClient и как проектировать таймауты разобрано в «Не оборачивайте HttpClient в using», поэтому здесь речь только о том, как выбирается прокси.

1. Сначала выводы

  • Параметры прокси Windows — не одна вещь; есть как минимум три источника. (1) Пользовательские параметры WinINET (страница «Прокси» в «Параметрах» = прежние параметры интернета), (2) машинные параметры WinHTTP (netsh winhttp) и (3) переменные окружения HTTP_PROXY / HTTPS_PROXY. Какой из них читается, решает само приложение.12
  • «Прокси», который вы видите в «Параметрах», — это пользовательские параметры WinINET. Браузеры и интерактивные приложения их читают; службы Windows — нет. WinINET в службе не поддерживается; для служб предназначен WinHTTP.13
  • Самая частая причина «вручную работает, как служба — нет» — другая учётная запись процесса. LocalSystem и учётная запись службы не видят пользовательский прокси, который администратор настроил на своём экране.34
  • netsh winhttp set proxy — статическая настройка: PAC, автоопределение и аутентификация прокси в неё не входят. Если PAC или WPAD нужно задать на уровне компьютера, это уже netsh winhttp set advproxy.42
  • Результат PAC зависит от URL. Функция FindProxyForURL в файле PAC принимает URL и имя узла и возвращает список прокси или прямое подключение (DIRECT). «Тот сайт открывается, а этот API — нет» нередко оказывается веткой PAC.56
  • HttpClient в .NET (Core и новее) инициализирует прокси по умолчанию в порядке: переменные окружения → пользовательские параметры прокси Windows. Если задана любая из HTTP_PROXY, HTTPS_PROXY или ALL_PROXY, она важнее параметров ОС. Отсюда типичный сбой: «кто-то забыл переменную окружения».7
  • В .NET Framework по умолчанию берутся параметры интернета учётной записи процесса; их можно переопределить через defaultProxy в app.config. Настройки файла конфигурации важнее системных.89
  • 407 — ошибка аутентификации прокси, это не 401 (аутентификация сервера). Схемы — Negotiate, NTLM, Basic и другие; в .NET учётные данные передают через DefaultProxyCredentials или WebProxy.UseDefaultCredentials. Под учётной записью службы содержимое «учётных данных по умолчанию» уже другое.101112
  • Прокси с инспекцией TLS работает только вместе с распространением внутреннего сертификата CA. На машинах и в средах выполнения, куда сертификат не доехал, проверка сертификата падает. Это лечат распространением в хранилище сертификатов, а не отключением проверки в приложении.134

Одной фразой: когда вы говорите «я проверил параметры прокси», всегда умейте назвать, какой из трёх источников вы смотрели и от какой учётной записи — в этом и есть тема статьи.

На схеме сплошная линия обозначает отношение, которое выполняется всегда, а пунктирная — условное отношение (условия указаны в пояснении к каждому отношению на странице сведений). Полный список отношений (всего 19, с доказательствами и степенью уверенности) и определения основных понятий собраны на странице сведений карты знаний (на японском). Данные: JSON-LD / Turtle

2. В Windows «параметры прокси» живут в трёх источниках

Сначала карта целиком. Пути, по которым приложение Windows находит корпоративный прокси, распадаются на три источника.

Источник настроек Где задаёте / команда Область Кто в основном читает
(1) WinINET (параметры интернета) Параметры → Сеть и Интернет → Прокси, inetcpl.cpl На пользователя (по умолчанию) Браузеры, интерактивные настольные приложения, умолчание .NET Framework
(2) WinHTTP (машинные параметры) netsh winhttp set proxy / set advproxy Компьютер Службы Windows, часть компонентов ОС
(3) Переменные окружения HTTP_PROXY / HTTPS_PROXY / ALL_PROXY / NO_PROXY Процесс (наследуются в зависимости от того, где заданы) HttpClient в .NET (Core и новее), curl, кроссплатформенные инструменты вроде Node.js и Python

(1) — то, что обычно имеют в виду под «параметрами прокси Windows»; по сути это конфигурация WinINET. Исторически это параметры интернета Internet Explorer, и по умолчанию они хранятся на каждого пользователя.4

(2) — параметры по умолчанию на уровне компьютера для контекстов вроде службы, где «вошедшего пользователя нет». (3) — в основном соглашение кроссплатформенных инструментов; в Windows их тоже читают .NET (Core и новее), curl и подобные.7

Важный момент: какой источник читается, решает само приложение, а не «настройки Windows». Если внутри приложения WinINET, оно читает (1); если WinHTTP — (2) (или своё явное указание); если .NET (Core и новее) — сначала (3), затем (1). Обычно это не «параметры прокси верные, а подключиться всё равно нельзя». На деле «приложение читало другой источник, чем тот, который вы проверяли».

Три источника параметров прокси WindowsWinINET — пользовательские Параметры и параметры интернета, WinHTTP — машинные параметры через netsh, переменные окружения — область процесса. Какой источник читается, решает приложение, а не сторона настроекКакой источник?Пользовательские параметры WinINETМашинные параметры WinHTTPHTTP_PROXY и родственныеБраузеры и настольные приложенияСлужбы и часть компонентов ОС.NET Core+ и curl

Рис. 1: Три источника стоят рядом. Какой из них читать, выбирает приложение.

Если включить групповую политику «Make proxy settings per-machine (rather than per-user)» (задавать параметры прокси на уровне компьютера, а не пользователя), (1) можно переключить на машинные и раздать одни и те же параметры всем пользователям. Через MDM (Intune и аналоги) то же самое задают на устройство CSP NetworkProxy.4

3. WinINET и WinHTTP — для интерактивных приложений и для служб

3.1. Чем они отличаются

WinINET и WinHTTP — оба штатные HTTP-клиентские стеки Windows, но рассчитаны на разное использование.

  • WinINET: для интерактивных настольных приложений. Сам подхватывает параметры интернета пользователя (прокси, cookie, кэш учётных данных) и при необходимости может показать окно ввода учётных данных. В службе и в процессе, похожем на службу, не поддерживается.1
  • WinHTTP: для служб и серверной стороны. Умеет работать под учётной записью службы, олицетворять поток (impersonation) и изолировать сеансы; взамен не разделяет настройки браузера пользователя, cookie и учётные данные. Окна тоже не показывает.3

У самой Microsoft формулировка прямая: «используйте WinINET, если только вы не работаете внутри службы или в служебном процессе, которому нужны изоляция сеансов и олицетворение» — иначе говоря, если это служба, берите WinHTTP.1

WinINET для интерактивных приложений, WinHTTP для службWinINET наследует параметры интернета вошедшего пользователя и в службе не поддерживается. WinHTTP работает под учётной записью службы без интерфейса и не разделяет настройки браузера пользователяДаСлужба или подобноеИнтерактивное настольное приложение?WinINETWinHTTPЧитает параметры интернета пользователяМашинные параметры, без UI

Рис. 2: Интерактивные приложения используют WinINET. Служба использует WinHTTP.

3.2. Базовые операции netsh winhttp

Машинный прокси по умолчанию WinHTTP задают через netsh.2

:: Показать текущие параметры прокси WinHTTP
netsh winhttp show proxy

:: Задать статический прокси (со списком исключений)
netsh winhttp set proxy proxy-server="proxy.example.co.jp:8080" bypass-list="*.example.co.jp;<local>"

:: Подтянуть параметры интернета (WinINET)
netsh winhttp import proxy source=ie

:: Вернуть умолчание (DIRECT)
netsh winhttp reset proxy

Два ограничения, которые стоит держать в голове.

  1. netsh winhttp set proxy — статическая настройка. Ни автоопределение прокси, ни URL PAC, ни аутентификация прокси сюда не входят.4
  2. import proxy source=ie копирует только статику на момент выполнения; дальше за изменениями параметров интернета он не следит. Если на уровне компьютера нужны PAC или автоопределение, подробные параметры в JSON (Proxy, ProxyBypass, AutoconfigUrl, AutoDetect) задают через netsh winhttp set advproxy.2

3.3. Самая частая ловушка: служба не читает настройки IE пользователя

Самый частый полевой сценарий по шагам выглядит так.

  1. Разработчик запускает инструмент на своём ПК → срабатывают его пользовательские параметры прокси (1), всё работает
  2. В продуктиве оставляют крутиться как службу Windows (Как создавать и эксплуатировать службы Windows) под LocalSystem
  3. Из LocalSystem виден другой набор (пользовательские параметры невидимы, машинные параметры WinHTTP не заданы = DIRECT) → процесс пытается идти к внешнему API напрямую и уходит в таймаут

Это не «на той же машине не работает». Даже на той же машине другая учётная запись процесса означает другой видимый набор параметров прокси. Для процесса, которому нужно ходить в сеть без вошедшего пользователя, правильный ход — задать параметры на уровне компьютера в том виде, который HTTP-стек этого процесса реально читает. У собственного приложения или компонента Windows на WinHTTP сработают параметры WinHTTP из netsh.4 HttpClient в .NET (Core и новее), напротив, машинные параметры WinHTTP не читает (см. главу 5), поэтому для службы на .NET задайте системную переменную окружения (HTTPS_PROXY и аналоги) или явно укажите HttpClientHandler.Proxy из настроек приложения.

Сбой бывает и в другую сторону. Если на ноутбуке, который бывает и в офисе, и вне корпоративной сети, жёстко прописать статический прокси через netsh winhttp set proxy, вне офиса этот прокси недоступен и связь пропадает целиком. Машинную статику считайте средством для серверов, у которых сеть не меняется.4

Почему служба не видит настройки IE пользователяЗапуск разработчиком читает пользовательские параметры WinINET и работает. Как LocalSystem эти параметры невидимы. Собственное приложение WinHTTP тогда следует незаданным машинным параметрам (DIRECT). Служба .NET Core+ по-прежнему использует переменные окружения или явный handler.Proxy и не переключается на netsh winhttpWinHTTP.NET Core+Запуск вручную от пользователяПрименяются пользовательские параметры WinINETСлужба Windows как LocalSystemПользовательские параметры невидимыКакой HTTP-стек?WinHTTP не задан = DIRECTПеременные окружения или handler.ProxyВнешний API уходит в таймаут

Рис. 3: Та же машина, другая учётная запись — другой видимый набор параметров прокси.

4. PAC и WPAD — что на самом деле стоит за «автоконфигурацией»

4.1. Файлы PAC и FindProxyForURL

Файл PAC (Proxy Auto-Configuration) — это JavaScript (ECMAScript), который вычисляет, какой прокси взять для данного URL, и всегда содержит функцию FindProxyForURL(url, host). Функция возвращает список прокси либо особое значение DIRECT — «можно идти напрямую, без прокси».5

function FindProxyForURL(url, host) {
    // Внутренние домены и частные адреса — напрямую
    if (dnsDomainIs(host, ".example.co.jp") ||
        isInNet(host, "10.0.0.0", "255.0.0.0")) {
        return "DIRECT";
    }
    // Остальное — через прокси. Если первый недоступен, берём следующий
    return "PROXY proxy1.example.co.jp:8080; PROXY proxy2.example.co.jp:8080; DIRECT";
}

Отсюда два практических следствия.

  • Прокси нужно выбирать для каждого URL. PAC может вернуть другой прокси или прямое подключение в зависимости от URL (узла), поэтому автопрокси WinHTTP тоже устроен так, чтобы каждый раз передавать URL запроса и спрашивать заново.6 «Браузер открывает другой сайт» не доказывает, что проблемный API идёт тем же путём.
  • DIRECT — указание «идти без прокси». Если трафик, который должен быть внутренним, так и не появляется в журнале прокси, сначала проверьте, не вернул ли PAC DIRECT (или не попал ли узел в список исключений).

4.2. Автоопределение через WPAD

Включите «Определять параметры автоматически» — и машина ищет расположение файла PAC по протоколу WPAD (Web Proxy Auto-Discovery). В типичной схеме DHCP раздаёт URL PAC либо через DNS ищут узел с именем wpad и скачивают PAC с URL вроде http://wpad/wpad.dat.14

Иными словами, «автоопределение» — не магия. Это механизм, который работает только в сети, где в DHCP/DNS уже заложена инфраструктура WPAD. Включить одно автоопределение в сети без этой инфраструктуры — лишь добавить ожидание неудачного поиска.

PAC выбирает прокси по URL, WPAD только находит PACFindProxyForURL принимает URL и узел и возвращает список прокси или DIRECT. WPAD находит PAC только через DHCP или DNS. Клиент, который не может вычислить PAC, переключается на статический прокси или переменные окружениясписок проксиDIRECTURL запросаFindProxyForURLИдти через проксиПодключаться без проксиWPAD через DHCP или DNSКлиент не вычисляет PACСтатические параметры или переменные окружения

Рис. 4: PAC решает по URL. WPAD только находит файл PAC.

4.3. Как ведут себя клиенты, которые не могут вычислить PAC

Не каждый клиент умеет вычислить PAC.

  • Статические параметры netsh winhttp set proxy PAC не вычисляют.4
  • Инструменты в стиле переменной HTTP_PROXY как правило принимают только фиксированный URL прокси (поля для URL PAC нет).7
  • У собственного приложения, которое вызывает WinHTTP напрямую, всё зависит от того, как открыта сессия. Приложение, открытое через WinHttpOpen в Windows 8.1 и новее с WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY, получает от WinHTTP автоматический выбор системных/пользовательских параметров прокси (включая WPAD/PAC) на каждый запрос.15 Если открыто более старым WINHTTP_ACCESS_TYPE_DEFAULT_PROXY (с 8.1 не рекомендуется) или аналогом, автопрокси в HTTP-стек не встроен, и приложение само должно вызвать WinHttpGetProxyForUrl и применить результат к запросу. Иначе говоря, в старой реализации PAC может быть, но не использоваться.5

«Браузер по PAC попадает на правильный прокси, а бизнес-приложение PAC не читает, пытается идти напрямую и падает» — ещё одно классическое расхождение. В сети на PAC для клиентов, которые PAC не читают, заранее решите запасной путь: статику или переменные окружения.

5. Выбор прокси в .NET — у Framework и у Core это разные вещи

Какие параметры прокси читает приложение .NET, по умолчанию различается между .NET Framework и .NET (Core и новее). Если перепутать, начнёте смотреть приложение на .NET 8 глазами эпохи Framework и промахнётесь.

5.1. .NET Framework — по умолчанию параметры интернета, переопределяется defaultProxy

В .NET Framework HttpWebRequest и сидящий на нём HttpClient используют прокси по умолчанию, если Proxy не задан явно. Этот прокси складывается из интернет-параметров системы (параметры WinINET учётной записи процесса) и файла конфигурации, и настройки файла конфигурации важнее.8

Этим умолчанием управляет элемент system.net/defaultProxy в app.config (или machine.config).9

<configuration>
  <system.net>
    <!-- useDefaultCredentials: отправлять ли прокси с аутентификацией учётные данные по умолчанию -->
    <defaultProxy enabled="true" useDefaultCredentials="true">
      <proxy usesystemdefault="true"
             proxyaddress="http://proxy.example.co.jp:8080"
             bypassonlocal="true" />
      <bypasslist>
        <add address="[a-z]+\.example\.co\.jp$" />
      </bypasslist>
    </defaultProxy>
  </system.net>
</configuration>

Оставьте элемент defaultProxy пустым — используются системные параметры (параметры интернета); пропишите proxyaddress и аналоги — они важнее. Из программы то же умолчание можно заменить через WebRequest.DefaultWebProxy.98

Ловушка раздела 3.3 действует и здесь. Поскольку умолчание — «параметры интернета учётной записи процесса», приложение .NET Framework под учётной записью службы читает другой (обычно пустой) набор, чем тот, что виден на рабочем столе администратора.

5.2. .NET (Core и новее) — сначала переменные окружения, затем пользовательские параметры ОС

У HttpClient в .NET (Core и новее) есть статическое свойство HttpClient.DefaultProxy. Если обработчик не указывает прокси явно, каждый экземпляр HttpClient его использует. Правило инициализации в Windows: «читать переменные окружения; если они не заданы — читать пользовательские параметры прокси».7

Используются такие переменные окружения.7

Переменная окружения Смысл
HTTP_PROXY Прокси для HTTP-запросов
HTTPS_PROXY Прокси для HTTPS-запросов
ALL_PROXY Запасной вариант, если две предыдущие не заданы
NO_PROXY Список узлов через запятую, для которых прокси не использовать

Три вещи, за которыми следить.

  • Если задана любая из HTTP_PROXY, HTTPS_PROXY или ALL_PROXY, она важнее параметров прокси ОС. Задать только NO_PROXY прокси из переменных окружения не включает: в Windows по-прежнему берутся пользовательские параметры прокси ОС. «Невидимые настройки» вроде системной HTTPS_PROXY, забытой после старого эксперимента, или шаблона CI/CD, который её подставляет, — типичный источник сбоев.
  • NO_PROXY не поддерживает подстановочные знаки (*). Чтобы захватить поддомен, поставьте точку в начале (.example.com совпадает с www.example.com, но не с самим example.com).7
  • Вне Windows (контейнеры Linux и аналоги), если переменные окружения не заданы, инициализация идёт без прокси. Что одно и то же приложение по умолчанию ведёт себя по-разному на Windows и на Linux, стоит проверить при переносе в контейнер.7

5.3. Явное указание — HttpClientHandler.Proxy и UseProxy

На любой среде выполнения наивысший приоритет — явное указание на обработчике. HttpClientHandler.Proxy важнее параметров ОС и файла конфигурации, а UseProxy = false не использует прокси вовсе.14

using System.Net;

// Явно использовать прокси из настроек приложения
var handler = new HttpClientHandler
{
    Proxy = new WebProxy("http://proxy.example.co.jp:8080")
    {
        BypassProxyOnLocal = true,
        BypassList = new[] { @"^intra\.example\.co\.jp$" },
        UseDefaultCredentials = true // Прокси с аутентификацией: отвечать учётными данными процесса
    },
    UseProxy = true
};
var client = new HttpClient(handler);

// Клиент без прокси (для прямого доступа к внутренним API)
var directHandler = new HttpClientHandler { UseProxy = false };
var directClient = new HttpClient(directHandler);

Если явного указания нет и берутся параметры ОС, у автоматического обхода локальных назначений есть правила. Короткое имя без точки, loopback-адрес, назначение, совпадающее с DNS-суффиксом самой машины, и подобное могут считаться «локальными».14 Явления вроде «по IP поведение другое» или «поставил FQDN — и вдруг пошло через прокси» нередко вызваны именно этой проверкой.

Порядок приоритета такой.

Приоритет (высокий → низкий) .NET Framework .NET (Core и новее)
1 Явное указание вроде HttpClientHandler.Proxy То же
2 defaultProxy в app.config Присваивание HttpClient.DefaultProxy
3 Параметры интернета учётной записи процесса Переменные окружения (HTTP_PROXY и другие)
4 — Пользовательские параметры прокси Windows
Выбор прокси по умолчанию в Framework против Core и новееЯвный HttpClientHandler.Proxy всегда побеждает. Framework затем использует app.config defaultProxy и параметры интернета учётной записи процесса. Core и новее использует присваивание HttpClient.DefaultProxy, затем переменные окружения, затем пользовательские параметры прокси WindowsFrameworkCore и новееЯвный handler.ProxyИспользуется этот проксиНет явного ProxyКакая среда выполнения?app.config defaultProxyПараметры интернета учётной записиHttpClient.DefaultProxyHTTP_PROXY и родственныеПользовательские параметры прокси Windows

Рис. 5: Явное указание всегда побеждает. Путь по умолчанию зависит от среды выполнения.

6. Прокси с аутентификацией — 407 это ошибка аутентификации прокси

6.1. Не путайте 407 с 401

Когда вы пытаетесь пройти через прокси, который требует аутентификации, прокси возвращает код состояния 407 (Proxy Authentication Required) и заголовок Proxy-Authenticate со списком доступных схем. Это не требование аутентификации сервера назначения (401 и WWW-Authenticate): и сторона, которой передают учётные данные, и место, где их задают, другие.10

407 это прокси, 401 это сервер назначения407 и Proxy-Authenticate приходят от прокси. 401 и WWW-Authenticate приходят от сервера назначения. Учётные данные и место настройки различаютсяПроксиНазначениеИсходящий запросКто требует аутентификацию?407 + Proxy-Authenticate401 + WWW-AuthenticateDefaultProxyCredentials

Рис. 6: 407 — аутентификация прокси. 401 — аутентификация сервера.

Схемы включают Basic, который отправляет имя пользователя и пароль как есть, и схемы «вызов — ответ» вроде Negotiate (Kerberos/NTLM). В схеме «вызов — ответ» сам пароль по сети не идёт, и аутентификация завершается за несколько обменов.10 Механизм, на какую схему проверка «скатывается», подробнее разобран в «NTLM и Kerberos на схемах».

6.2. Как передать учётные данные в .NET

Если хотите использовать прокси по умолчанию из параметров ОС и только провести аутентификацию, используйте HttpClientHandler.DefaultProxyCredentials. Это учётные данные, которые отправляются тому прокси по умолчанию, когда UseProxy = true и Proxy = null (= системный прокси по умолчанию).11

using System.Net;

var handler = new HttpClientHandler
{
    UseProxy = true,   // Значение по умолчанию. Вместе с Proxy = null берётся системный прокси
    Proxy = null,
    // Ответить на 407 учётными данными процесса (вошедший пользователь или учётная запись службы)
    DefaultProxyCredentials = CredentialCache.DefaultCredentials
};
var client = new HttpClient(handler);

Если прокси задаёте явно, учётные данные кладут на сторону WebProxy. Во многих клиентских сценариях рекомендуют не отдельные имя и пароль, а учётные данные по умолчанию вошедшего пользователя; это WebProxy.UseDefaultCredentials = true.12

6.3. Проблема 407 у учётной записи службы

Учётная запись процесса важна и здесь. «Учётные данные по умолчанию» — это учётные данные той записи, от которой запущен процесс. Запустите как интерактивного пользователя — к прокси идёт аутентификация от этого пользователя; запустите как службу LocalSystem — от учётной записи компьютера.

  • Если прокси аутентифицирует пользователей через Active Directory, учётную запись компьютера или локальную запись он принять не может, и 407 продолжается, как только приложение становится службой
  • Наоборот, в некоторых средах на стороне прокси есть исключение аутентификации для служб (по исходному IP или по учётной записи)

Поэтому разбор 407 не закрывается одними «настройками приложения»: это ещё и проверка на стороне инфраструктуры, может ли прокси аутентифицировать учётную запись процесса. Для приложения, которое будет службой, ещё на этапе проектирования стоит выбрать одно из: запускать под доменной учётной записью службы (gMSA и аналоги), сделать исключение аутентификации на стороне прокси или поднять внутренний промежуточный прокси без требования аутентификации.

Есть и способ вписать учётные данные в переменную окружения, как HTTP_PROXY=http://user:pass@proxy:80807, но тогда пароль открытым текстом оказывается в переменной окружения (= в сведениях о процессе), поэтому для постоянной эксплуатации это не рекомендуется.

7. HTTPS и прокси — туннели CONNECT и инспекция TLS

7.1. HTTPS проходит через прокси как «туннель»

Когда для HTTPS используют прокси, клиент сначала отправляет прокси запрос CONNECT целевой-узел:443, и прокси открывает TCP-туннель. При успехе прокси возвращает 200, после чего клиент и сервер назначения выполняют TLS-рукопожатие внутри этого туннеля. Если туннель не открывается, прокси возвращает 407 (нужна аутентификация), 502 или аналог.16

В этой модели прокси не читает содержимое туннеля (зашифрованный HTTPS). В журнале прокси остаются имя узла назначения и успех соединения; путь URL не виден — так ведёт себя прокси без расшифровки.

HTTPS через прокси это туннель CONNECTКлиент отправляет CONNECT прокси, прокси открывает TCP-туннель и возвращает 200, затем клиент и назначение выполняют TLS-рукопожатие в туннеле. Журнал прокси видит узел, не путь URLCONNECT host:443200 и TCP-туннельTLS внутри туннеляКлиентПроксиНазначениеЖурнал: только узел и успех

Рис. 7: Прокси без расшифровки видит узел, не зашифрованный путь.

7.2. Прокси с инспекцией TLS и ошибки сертификата

У продуктов безопасности, с другой стороны, есть тип инспекции TLS (расшифровка SSL, break and inspect): прокси завершает TLS, смотрит содержимое и заново шифрует перед пересылкой. В этой схеме сертификат сервера, который видит клиент, уже не настоящий; его заменяют сертификатом, переподписанным собственным CA прокси.13

Посылка, без которой схема не работает: «сертификат CA прокси раздан в доверенные корневые каждого клиента». На машине, куда он не доехал, или в среде выполнения, которая не смотрит хранилище сертификатов Windows (инструменты со своим хранилищем доверия), проверка сертификата падает. В .NET это обычно проявляется как HttpRequestException с внутренним AuthenticationException (сообщение вроде «the remote certificate is invalid»).

Принципы исправления такие.

  • Распространите внутренний сертификат CA в хранилище «Доверенные корневые центры сертификации» локального компьютера. Когда класть в пользовательское хранилище, а когда в хранилище компьютера, разобрано в «Практическое руководство по хранилищу сертификатов Windows».
  • Не отключайте проверку сертификатов в коде. Обход, который всегда возвращает true из ServerCertificateCustomValidationCallback, становится уязвимым приложением: во внешней сети атаку «человек посередине» оно уже не увидит.
  • Трафик с пиннингом сертификата инспектировать нельзя. Соединения, которые проверяют конкретный сертификат Microsoft, как делают некоторые компоненты Windows, падают в момент, когда прокси подменяет сертификат; обхода кроме исключения нет.4 Для трафика в сторону SaaS вроде Microsoft 365 сама Microsoft рекомендует исключать его из расшифровки и инспекции на сетевом уровне.13

Симптом «все внутренние сайты открываются, а конкретная облачная служба в приложении даёт ошибку сертификата» сначала стоит проверять как сочетание списка исключений инспекции TLS и пиннинга.

Прокси с инспекцией TLS переподписывает сертификатПрокси завершает TLS, проверяет содержимое и предъявляет сертификат, переподписанный собственным CA. Проверка проходит только если этот CA в доверенных корневых. Не отключайте проверку в кодеCA в доверенных корневыхCA нетНастоящий сертификат сервераПрокси с инспекцией TLSПереподписан CA проксиПроверка клиентаУспехОшибка сертификатаРаспространить CA в хранилище

Рис. 8: Инспекция работает только вместе с распространением внутреннего CA.

8. Диагностика — пять шагов, чтобы найти причину

Разбор «не подключается» ведите механически в этом порядке.

Шаг Что делаете Что узнаёте
(1) Воспроизвести Обратиться к проблемному URL через curl.exe -v или Invoke-WebRequest (лучше на той же машине, под той же учётной записью) Проблема приложения или среды
(2) Снять параметры Снять три источника: netsh winhttp show proxy, пользовательские параметры и переменные окружения Что лежит в каком источнике
(3) Определить учётную запись Определить учётную запись процесса (служба, Планировщик заданий, другой пользователь) Под какими параметрами и какими учётными данными оно работает
(4) Классифицировать ошибку Различить 407 / 403 / сбой разрешения имени / таймаут / ошибку сертификата Отделить аутентификацию прокси, отказ политики, маршрут и инспекцию TLS
(5) Журнал прокси Проверить соответствующее время в журнале доступа прокси-сервера Дошёл ли трафик до прокси вообще и кого он аутентифицировал

Шаг (2) можно снять разом через PowerShell.

# (1) Пользовательские параметры (WinINET) — читается HKCU учётной записи процесса
Get-ItemProperty 'HKCU:\Software\Microsoft\Windows\CurrentVersion\Internet Settings' |
    Select-Object ProxyEnable, ProxyServer, ProxyOverride, AutoConfigURL

# (2) Машинные параметры (WinHTTP)
netsh winhttp show proxy

# (3) Переменные окружения
Get-ChildItem env: | Where-Object Name -match 'proxy'

Несколько практических советов.

  • В тесте воспроизведения (1) помните, какой источник параметров читает инструмент. Штатный curl.exe Windows может явно указать прокси через -x http://proxy:8080, а для проверки TLS обычно использует хранилище сертификатов ОС (Schannel). Invoke-WebRequest в Windows PowerShell 5.1 идёт по стороне .NET Framework (по умолчанию параметры интернета); PowerShell 7 — по стороне .NET (сначала переменные окружения). «curl проходит, приложение нет» само по себе намекает на расхождение источников настроек.
  • Если в (3) цель — служба, перепроверьте (1) и (2) под той же учётной записью, что и служба. Проверка в собственной сессии администратора не доказывает, что видит LocalSystem.
  • В классификации ошибки (4) для 407 первым кандидатом берите главу 6 (аутентификация), для ошибки сертификата — главу 7 (инспекция TLS), для таймаута — «до прокси не дошло» (маршрут, разрешение имени, брандмауэр). Случай, когда виновато входящее правило брандмауэра Windows, а не прокси, разобран в «Брандмауэр Windows и бизнес-приложения».
  • Если дошли до (5), а в журнале прокси следа нет, трафик до прокси не дошёл. Проверяйте решение DIRECT у PAC, список исключений или забытую переменную окружения и при необходимости подтвердите фактическое назначение захватом пакетов («Практический захват пакетов в Windows — как выбрать pktmon, netsh trace и Wireshark»).
Пять шагов диагностики сбоя проксиВоспроизвести под той же учётной записью, снять три источника параметров, определить учётную запись процесса, классифицировать ошибку, затем проверить журнал проксиВоспроизвести curlСнять три источникаОпределить учётную записьКлассифицировать ошибкуПроверить журнал прокси407: аутентификацияОшибка сертификата: инспекцияТаймаут: не дошло

Рис. 9: Пройдите пять шагов по порядку. Класс ошибки выбирает следующую главу.

9. Рекомендация по проектированию — сделать приложение, в котором прокси можно задать

Переверните процедуру разбора — получите руководство по проектированию на стороне приложения. Для Windows-приложения, которое сдаёте в среду с корпоративным прокси, рекомендуется следующее.

  1. Сделайте прокси задаваемым из настроек приложения. По умолчанию — «следовать параметрам ОС». В большинстве сред умолчания достаточно; только в исключительных средах — PAC нельзя прочитать, работает как служба, особая конфигурация прокси — дайте возможность указать URL прокси, список исключений и «не использовать прокси» из файла настроек. Точка реализации — HttpClientHandler.Proxy / UseProxy в разделе 5.3.14
  2. Зафиксируйте, как внутренние назначения (API, базы данных, серверы лицензий и аналоги) исключаются из прокси. Запишите во внедренческую инструкцию, чем их исключаете: PAC DIRECT, списком исключений или NO_PROXY. Правила сопоставления NO_PROXY (без подстановочных знаков, что значит ведущая точка) часто понимают неверно, поэтому приложите примеры.7
  3. Проектируйте таймауты и повторы в расчёте на проход через прокси. Если прокси лежит или застрял на аутентификации, реализация, которая ждёт длинный таймаут по умолчанию, подвешивает и интерфейс, и эксплуатацию. Отделите более короткий таймаут соединения и ограничьте повторы идемпотентными запросами (детали проектирования в «Не оборачивайте HttpClient в using»).
  4. Пишите в журнал, какой прокси использовался. Сделайте так, чтобы само приложение могло ответить на первый вопрос разбора сбоя.

Журнал как в пункте 4 уже полезен, если записывает только результат выбора. Суть — вывести маршрут из тех параметров (обработчика), которыми вы реально собрали клиент. Если журналировать HttpClient.DefaultProxy напрямую, запишете значение, расходящееся с фактическим маршрутом, когда обработчик явно указывает Proxy или ставит UseProxy = false.

using System.Net.Http;

// handler — тот же экземпляр, которым создавали HttpClient
// UseProxy=false всегда напрямую. Если есть явное указание — оно, иначе DefaultProxy
var effectiveProxy = handler.UseProxy
    ? handler.Proxy ?? HttpClient.DefaultProxy
    : null;
var target = new Uri("https://api.example.com/v1/orders");
var route = effectiveProxy is null || effectiveProxy.IsBypassed(target)
    ? "DIRECT"
    : effectiveProxy.GetProxy(target)?.ToString() ?? "DIRECT";
logger.LogInformation("HTTP-отправка {Target} маршрут {Route} учётная запись {User}",
    target, route, Environment.UserName);

Если при запуске один раз записать «маршрут» и «учётную запись процесса» для основных назначений, шаги (1)–(3) главы 8 заканчиваются чтением журнала. Когда говорят «в браузере работает, а…», уметь ответить со стороны приложения «я использовал эти параметры и этот маршрут» — условие приложения, устойчивого к неприятностям с прокси.

Сделать прокси настраиваемым и журналировать маршрутПо умолчанию следовать параметрам ОС, разрешить явный URL прокси или обход или отсутствие прокси из настроек приложения и журналировать фактически использованный маршрут вместе с учётной записью процессаPAC не прочитан / служба / особоеОбычный случайПо умолчанию: следовать параметрам ОСИсключительная среда?Задать URL, исключения или без проксиИспользовать умолчание ОСЖурналировать маршрут и учётную запись

Рис. 10: Настраивайте, когда нужно. Всегда пишите в журнал, какой маршрут использовался.

10. Итог

  • Параметры прокси Windows делятся на три источника — пользовательские параметры WinINET, машинные параметры WinHTTP и переменные окружения — и какой из них читается, решают приложение (его HTTP-стек) и учётная запись процесса.
  • WinINET — для интерактивных приложений и в службе не поддерживается; для служб предназначен WinHTTP (netsh winhttp). Для «вручную работает, как служба — нет» сначала проверяйте, не другая ли учётная запись процесса.
  • netsh winhttp set proxy — статическая настройка: PAC, автоопределение и аутентификация в неё не входят. В сети на PAC нужно заранее решить, как обращаться с клиентами, которые PAC не читают.
  • FindProxyForURL в PAC возвращает прокси или DIRECT в зависимости от URL. WPAD работает только в сети с инфраструктурой DHCP/DNS.
  • Умолчание .NET Framework — параметры интернета учётной записи процесса (переопределяется defaultProxy); .NET (Core и новее) — переменные окружения, затем пользовательские параметры прокси. Явное указание (HttpClientHandler.Proxy) всегда наивысший приоритет.
  • 407 — ошибка аутентификации прокси; в приложении под учётной записью службы типичная причина — «учётные данные по умолчанию» становятся другим лицом.
  • Прокси с инспекцией TLS предполагает распространение внутреннего сертификата CA, и правильный ответ на ошибку сертификата — раздача в хранилище сертификатов, а не отключение проверки. Трафику с пиннингом нужно исключение.
  • Диагностируйте механически в порядке «воспроизвести → снять три источника параметров → определить учётную запись процесса → классифицировать ошибку → журнал прокси». На стороне приложения лучшая профилактика — проектирование, при котором «прокси можно задать, а использованный маршрут попадает в журнал».

В следующий раз, когда скажут «только бизнес-приложение не подключается», сначала задайте это.

Под какой учётной записью запущено это приложение и какой из трёх источников параметров прокси оно читает?

Один этот вопрос сильно меняет вход в разбор.

Похожие статьи

Смежные области консультирования

KomuraSoft LLC занимается разбором сбоев связи Windows-приложений в средах корпоративного прокси, прокси с аутентификацией и инспекции TLS — «на машине разработчика работает, в сети заказчика не ходит», «после запуска как службы внешний API перестал быть доступен» — и консультациями по проектированию связи бизнес-приложений в расчёте на прокси (пункты настроек, таймауты, журналирование). Можно начать с того, как воспроизвести явление и как снять журналы.

Справочные ссылки

  1. Microsoft Learn, WinINet vs. WinHTTP. О рекомендации использовать WinINET, если только процесс не работает внутри службы или служебного процесса, которому нужны олицетворение и изоляция сеансов, и о сравнительной таблице функций: кэш учётных данных, запрос учётных данных, поддержка служб, олицетворение, изоляция сеансов и другое. ↩ ↩2 ↩3 ↩4

  2. Microsoft Learn, netsh winhttp. О синтаксисе netsh winhttp show/set/import/reset; proxy-server и bypass-list у set proxy; import proxy source=ie; и подробных параметрах прокси в JSON (Proxy, ProxyBypass, AutoconfigUrl, AutoDetect) через set advproxy. ↩ ↩2 ↩3 ↩4

  3. Microsoft Learn, About WinHTTP. О том, что WinHTTP — HTTP-стек для служб и серверной стороны: умеет работать под учётной записью службы и олицетворять, но не разделяет cookie, кэш, учётные данные браузера и параметры интернета пользователя. ↩ ↩2 ↩3

  4. Microsoft Learn, Using a proxy with Delivery Optimization. О том, что netsh winhttp set proxy — статическая настройка без автоопределения, URL PAC и аутентификации прокси; о параметрах прокси на устройство для контекстов без вошедшего пользователя (CSP NetworkProxy, политика «Make proxy settings per-machine»); и о трафике с пиннингом сертификата, который падает при инспекции TLS и нуждается в исключении. ↩ ↩2 ↩3 ↩4 ↩5 ↩6 ↩7 ↩8 ↩9 ↩10

  5. Microsoft Learn, WinHTTP AutoProxy Support. О том, что скрипт PAC содержит функцию FindProxyForURL(url, host), которая на каждый запрос вычисляет список прокси и особым возвращаемым значением указывает прямое подключение, и о том, что более старый AutoProxy API не встраивает автопрокси в HTTP-стек сам, так что приложение должно вызвать WinHttpGetProxyForUrl. ↩ ↩2 ↩3

  6. Microsoft Learn, WinHttpGetProxyForUrl function. О том, что это реализация протокола WPAD, её нужно вызывать по URL, потому что файл PAC может вернуть другой прокси для другого URL, и она поддерживает и явный URL PAC, и автоопределение из сети. ↩ ↩2

  7. Microsoft Learn, HttpClient.DefaultProxy Property. О том, что Windows сначала читает переменные окружения HTTP_PROXY, HTTPS_PROXY, ALL_PROXY и NO_PROXY и, если они не заданы, пользовательские параметры прокси; что Linux инициализируется без прокси, если переменных окружения нет; что NO_PROXY не поддерживает подстановочные знаки и сопоставляет поддомен по ведущей точке; и что URL прокси может включать имя пользователя и пароль. ↩ ↩2 ↩3 ↩4 ↩5 ↩6 ↩7 ↩8 ↩9

  8. Microsoft Learn, Configuring Internet Applications. О том, что элемент defaultProxy задаёт прокси по умолчанию в .NET Framework; что HttpWebRequest без свойства Proxy использует прокси по умолчанию; и что системные параметры интернета и файл конфигурации складываются с приоритетом файла конфигурации. ↩ ↩2 ↩3

  9. Microsoft Learn, defaultProxy element (network settings). Об атрибутах enabled и useDefaultCredentials элемента system.net/defaultProxy, дочерних элементах proxy, bypasslist и module, о том, что при пустом элементе берутся системные параметры прокси, и о настройке через HttpClient.DefaultProxy при миграции на .NET 6 и новее. ↩ ↩2 ↩3

  10. Microsoft Learn, Authentication in WinHTTP. О том, что при необходимости аутентификации прокси возвращаются код состояния 407 и заголовок Proxy-Authenticate (аутентификация сервера — 401 и WWW-Authenticate); о разнице между Basic и схемами «вызов — ответ» вроде Kerberos; и о том, что в схеме «вызов — ответ» имя пользователя и пароль по сети не идут. ↩ ↩2 ↩3

  11. Microsoft Learn, HttpClientHandler.DefaultProxyCredentials Property. О свойстве, которое задаёт учётные данные для аутентификации к прокси по умолчанию, когда UseProxy истинно и Proxy равен null, так что используется системный прокси по умолчанию. ↩ ↩2

  12. Microsoft Learn, WebProxy.Credentials Property. О том, что свойство Credentials — учётные данные, отправляемые прокси в ответ на HTTP 407, и о рекомендации во многих клиентских сценариях ставить UseDefaultCredentials в true, чтобы использовались учётные данные по умолчанию вошедшего пользователя. ↩ ↩2

  13. Microsoft Learn, Understanding implications when using network intermediation to decrypt or manipulate Microsoft 365 traffic at the network layer. О том, что инспекция TLS (расшифровка SSL) — схема, в которой прокси или брандмауэр расшифровывает, проверяет и заново шифрует TLS; что она может вызвать сбой работы и деградацию производительности в службах, рассчитанных на сквозной TLS; и о рекомендации исключать трафик в сторону Microsoft 365 из расшифровки и инспекции на сетевом уровне. ↩ ↩2 ↩3

  14. Microsoft Learn, Make HTTP requests with the HttpClient class. О двух способах конфигурации HttpClient.DefaultProxy и HttpClientHandler.Proxy; о том, что указание Proxy важнее файла конфигурации и параметров локального компьютера; о типичной схеме WPAD, когда файл PAC (wpad.dat и аналоги) берут по DNS-имени wpad или через DHCP; и о правиле обхода локальных назначений по короткому имени, loopback и совпадению DNS-суффикса. ↩ ↩2 ↩3 ↩4

  15. Microsoft Learn, WinHttpOpen function. О значении каждого значения dwAccessType. WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY (Windows 8.1 и новее) сам выбирает прокси из системных/пользовательских параметров и также сам обрабатывает отказоустойчивость и аутентификацию, а WINHTTP_ACCESS_TYPE_DEFAULT_PROXY с 8.1 не рекомендуется. ↩

  16. Microsoft Learn, Work with existing on-premises proxy servers. О том, что исходящий HTTPS устанавливается запросом CONNECT к прокси; что успех возвращает HTTP 200; и что ответы вроде 407 (нужна аутентификация) или 502 означают, что прокси связь не разрешает, поэтому разбор стоит продолжать вместе с командой, отвечающей за прокси. ↩

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

Эти страницы показывают тему статьи в более широком контексте услуг и решений.

Статья напрямую связана со следующими услугами.

Частые вопросы

Вопросы, которые часто возникают при консультациях по теме статьи.

Браузер выходит в интернет, а бизнес-приложение не проходит корпоративный прокси. Почему?
Браузер читает пользовательские параметры прокси WinINET, но бизнес-приложение вовсе не обязательно смотрит туда же. Если оно запущено как служба Windows или под другой учётной записью, оно берёт параметры, видимые этой записи, машинные параметры WinHTTP и переменные окружения. Сначала определите учётную запись процесса и проверьте прокси и командой netsh winhttp show proxy, и в пользовательских параметрах, которые видит эта запись. Если то же самое воспроизводится через curl.exe на той же машине и под той же учётной записью, это не баг приложения, а расхождение источников настроек.
Я задал netsh winhttp set proxy, но трафик приложения не изменился. Почему?
netsh winhttp задаёт параметры по умолчанию WinHTTP на уровне компьютера. На браузеры и интерактивные приложения, которые читают WinINET, и на HttpClient в .NET (Core и новее), который сначала смотрит переменные окружения, это не влияет. К тому же netsh winhttp set proxy — статическая настройка: PAC, автоопределение и аутентификация прокси в неё не входят. Сначала выясните, каким HTTP-стеком пользуется приложение и из какого источника оно берёт прокси.
Какие параметры прокси читает приложение .NET?
.NET Framework по умолчанию берёт параметры интернета (эквивалент WinINET) той учётной записи, от которой запущен процесс; их можно переопределить элементом system.net/defaultProxy в app.config. HttpClient в .NET (Core и новее) сначала читает HTTP_PROXY, HTTPS_PROXY и NO_PROXY; если они не заданы, переключается на пользовательские параметры прокси Windows. В обоих случаях явный HttpClientHandler.Proxy имеет приоритет. Порядок по умолчанию у Framework и у Core разный, поэтому при миграции поведение прокси нужно проверить заново.
Что проверять, когда возвращается 407 Proxy Authentication Required?
407 означает, что аутентификацию требует сам прокси, а не сервер назначения (это 401). Сначала по заголовку Proxy-Authenticate посмотрите схему (Negotiate, NTLM, Basic), а в .NET передайте учётные данные через HttpClientHandler.DefaultProxyCredentials или WebProxy.UseDefaultCredentials. Если приложение работает под учётной записью службы, «учётные данные по умолчанию» — это уже данные этой службы, поэтому типичный случай: у интерактивного пользователя проходит, а после запуска как службы появляется 407. В журнале прокси дополнительно проверьте, кого он аутентифицировал.
Прокси с инспекцией TLS даёт ошибки сертификата. Можно ли отключить проверку сертификатов?
Отключать не рекомендуется. Прокси с инспекцией TLS расшифровывает трафик и предъявляет клиенту сертификат, переподписанный собственным центром сертификации (CA). Если этот сертификат CA нет среди доверенных корневых, проверка падает. Правильное исправление — распространить внутренний сертификат CA в хранилище сертификатов Windows (обычно «Доверенные корневые центры сертификации» локального компьютера). Если отключить проверку в коде, атаку «человек посередине» во внешней сети уже не обнаружить, и уязвимость останется.

Об авторе

Страница с профилем автора статьи.

Го Комура

Представитель KomuraSoft LLC

Специализируется на разработке программного обеспечения для Windows, техническом консалтинге и расследовании сбоев, особенно в проектах с унаследованными системами и трудно воспроизводимыми ошибками.

Публичные ссылки

Вернуться в блог