TIdHTTP отправляет HTTPS-запрос как HTTP через прокси - PullRequest
0 голосов
/ 29 мая 2018

У меня проблема с реализацией закрепления SSL-сертификата с помощью TIdHTTP.

Итак, вот шаги:

  1. Удаление TIdHTTP, TIdSSLIOHandlerSocketOpenSSL и TIdCompressorZLib в форме.
  2. Назначение TIdSSLIOHandlerSocketOpenSSL и TIdCompressorZLib свойствам IOHandler и Compressor для TIdHTTP.
  3. Настройка TIdSSLIOHandlerSocketOpenSSL:

    Port = 0
    DefaultPort = 0
    SSLOptions.Method = sslvTLSv1_2
    SSLOptions.SSLVersions = [sslvTLSv1_2]
    SSLOptions.Mode = sslmClient
    SSLOptions.VerifyMode = [sslvrfPeer]
    SSLOptions.VerifyDepth = 0
    OnVerifyPeer = SSLIOHandlerVerifyPeer
    
    * 101eer SSL 1016 ** 101H
    function TForm2.SSLIOHandlerVerifyPeer(Certificate: TIdX509; AOk: Boolean; ADepth, AError: Integer): Boolean;
    const
      LCGoogleCert = '98:1D:34:C4:F8:4A:F2:B7:C7:AB:77:AD:51:1C:51:4C:AD:76:ED:0D:0E:FA:C9:63:68:AF:28:69:94:60:BF:7A';
    begin
      Result := Certificate.Fingerprints.SHA256AsString.Equals(LCGoogleCert);
    end;
    
  4. Перетащите кнопку на форму:

    procedure TForm2.Button1Click(Sender: TObject);
    const
      LCGoogleURL = 'https://www.google.com/';
    var
      s: UnicodeString;
    begin
      s := HTTPSender.Get(LCGoogleURL);
    end;
    
  5. Установить Скрипач

  6. В Fiddler: Инструменты - Параметры - установите флажок «Захват HTTPS-подключений» и снимите флажок «Расшифровать HTTPS-трафик».Сгенерируйте и установите сертификат в систему.

  7. Установите для прокси-адреса и порта Fiddler значение TIdHTTP.

  8. Запустите программу и нажмите кнопку.Первый клик - вы получаете исключение о неверном сертификате.Но если вы нажмете второй раз - вы не получите никаких исключений, но вы получите полный ответ и увидите незашифрованный трафик в Fiddler, как если бы вы отправляли запрос по HTTP, а не по HTTPS.

Вы можете увидеть результат первого и второго запроса на картинке ниже.Так это ошибка в компоненте Indy, или я пытаюсь неправильно реализовать закрепление SSL?

Fiddler

1 Ответ

0 голосов
/ 29 мая 2018

При первом вызове TIdHTTP.Get() событие OnVerifyPeer видит сертификат Fiddler SSL / TLS вместо Google, поэтому он отклоняет сертификат, и основное сокетное соединение заканчивается закрытием.

Однако, в IOHandler InputBuffer остались непрочитанные данные (примерно 162 байта зашифрованных данных). По проекту метод TIdIOHandlerStack.Connected() возвращает True, пока есть непрочитанные данные, доступные для выполнения операций чтения, даже если нет физического соединения с сокетом.

Итак, что в итоге происходитво время второго вызова TIdHTTP.Get() происходит следующее:

  • TIdHTTP знает, что он обменивается данными через HTTPS через прокси-сервер и что он делает новый HTTP-запрос к тому же Googleсервер через тот же прокси, что и предыдущий вызов TIdHTTP.Get().Таким образом, TIdHTTP проверяет Connected(), чтобы увидеть, подключен ли он к прокси-серверу, и видит, что Connected() изначально имеет значение True, поэтому он принимает решение пропустить новый запрос CONNECT и действовать так, как если бы он отправлялновый HTTP-запрос по существующему прокси-соединению.

  • Однако, поскольку базовый сокет был отключен, TIdHTTP должен установить новое сокет-соединение с Fiddler.При подготовке к новому HTTP-запросу InputBuffer очищается.Connected() проверяется снова и теперь имеет значение False, поэтому TIdHTTP устанавливает новое сокетное соединение с Fiddler.Это новое сокетное соединение изначально не зашифровано (для IOHandler PassThrough установлено значение True), поэтому последующее CONNECT не будет зашифровано (но этот раздел кода не знает, что TIdHTTP уже решил пропустить CONNECT).

  • TIdHTTP продолжает отправку запроса GET в Fiddler в незашифрованном виде.

  • Fiddler кэширует свои туннели TLS в течение периодавремя, и поэтому он повторно использует существующий туннель в Google, пересылая незашифрованный GET как есть в Google по соединению TLS, а затем перенаправляет ответ в незашифрованном виде обратно в TIdHTTP.

Итак, в конечном счете, здесь есть три проблемы (я открыл билет в системе отслеживания проблем Indy ):

  • TIdHTTP не очищаетсяInputBuffer когда происходит сбой, который требует закрытия основного сокета.Простым решением этой проблемы является очистка InputBuffer метода *1059* от любых существующих данных, прежде чем делать что-либо еще.Таким образом, он видит, что соединение действительно разорвано, прежде чем решить, что делать с CONNECT.Теперь я проверил это исправление в репозитории Indy SVN и проверил, работает ли оно в вашем сценарии.

  • TIdHTTP принимает решение отправить или пропустить CONNECT слишком рано,прежде чем он знает, что он делает с базовым сокетом.Это потребует некоторой перезаписи внутренней логики TIdHTTP, поэтому она будет отложена до более поздней версии Indy.

  • Fiddler пересылает незашифрованные данные туда и обратно черезранее зашифрованный туннель.С этим ничего не поделаешь в TIdHTTP.

...