TIdHTTP通过代理

时间:2018-05-29 15:24:12

标签: delphi fiddler indy delphi-xe8 idhttp

我在使用TIdHTTP实施SSL证书固定时遇到了问题。

所以,以下是步骤:

  1. 在表单上删除TIdHTTP,TIdSSLIOHandlerSocketOpenSSL和TIdCompressorZLib。
  2. 将TIdSSLIOHandlerSocketOpenSSL和TIdCompressorZLib分配给TIdHTTP的IOHandler和Compressor属性。
  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
    
  4. SSLIOHandlerVerifyPeer的代码:

    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;
    
  5. 在表单上放置一个按钮:

    procedure TForm2.Button1Click(Sender: TObject);
    const
      LCGoogleURL = 'https://www.google.com/';
    var
      s: UnicodeString;
    begin
      s := HTTPSender.Get(LCGoogleURL);
    end;
    
  6. 安装Fiddler

  7. 在Fiddler中:工具 - 选项 - 选中“捕获HTTPS连接”并取消选中“解密HTTPS流量”。生成证书并将其安装到系统。

  8. 将Fiddler的代理地址和端口设置为TIdHTTP。

  9. 运行程序并单击按钮。首先单击 - 您将获得有关错误证书的例外情况。但是如果你第二次点击 - 你不会得到任何例外,但你会得到完整的回复,你会在Fiddler中看到未加密的流量,就像你通过HTTP而不是HTTPS发送请求一样。

  10. 您可以在下面的图片中看到第一个和第二个请求的结果。那么这是Indy组件中的一个错误,还是我试图错误地实现SSL Pinning?

    Fiddler

1 个答案:

答案 0 :(得分:2)

在第一次拨打TIdHTTP.Get()时,OnVerifyPeer活动会看到Fiddler的SSL / TLS证书而非Google,因此拒绝了证书和底层套接字连接最终被关闭。

但是,IOHandler的InputBuffer(大约162字节的加密数据)中遗留了未读数据。 按设计,只要有未读数据可用于满足读取操作,TIdIOHandlerStack.Connected()方法就会返回True,即使没有物理套接字连接。

因此,在第二次调用TIdHTTP.Get()期间最终发生的事情如下:

  • TIdHTTP知道它是通过代理通过HTTPS进行通信,并且它通过与之前调用TIdHTTP.Get()相同的代理向同一个Google服务器发出新的HTTP请求。因此TIdHTTP检查Connected()以查看它是否仍然连接到代理,并看到Connected()最初为True,因此它决定跳过新的CONNECT请求并继续进行,就像它通过现有代理连接发送新的HTTP请求一样。

  • 但是,由于底层套接字已断开连接,TIdHTTP必须与Fiddler建立新的套接字连接。在准备新的HTTP请求时,InputBuffer被清除。再次检查Connected(),现在为False,因此TIdHTTP与Fiddler建立新的套接字连接。新的套接字连接最初是未加密的(IOHandler' PassThrough设置为True),因此后续的CONNECT不会被加密(但这部分代码不知道{{1}已经决定跳过TIdHTTP)。

  • CONNECT继续向Fiddler发送TIdHTTP请求,未加密。

  • Fiddler将其TLS隧道缓存一段时间,因此它会重用现有隧道到Google,从而通过TLS连接将未加密的GET原样转发给Google,然后转发未加密的回复回GET

所以,最终,这里有三个问题(我打开了一个ticket in Indy's issue tracker):

    当发生需要关闭底层套接字的故障时,
  • TIdHTTP未清除TIdHTTP。一个简单的解决方法是在执行任何其他操作之前使InputBuffer方法清除任何现有数据的TIdCustomHTTP.ConnectToHost()。这样,在决定如何处理InputBuffer之前,它确实看到连接已经消失。我现在已经将此修复程序检入了Indy的SVN存储库,并测试它在您的方案中是否有效。

  • CONNECT决定在它知道底层套接字正在做什么之前,过早发送或跳过TIdHTTP。这将需要重新编写CONNECT的内部逻辑,因此它将推迟到Indy的更高版本。

  • Fiddler通过先前加密的隧道来回转发未加密的数据。在TIdHTTP

  • 中无法做到这一点