Suggestion to ease certificate verification using TFPHTTPClient
## Summary TFPHTTPClient allows certificate verification when accessing servers via HTTPS. IMHO, certificate verification should be encouraged as much as possible, since using TLS without verification has little added value and has security risks. To enable certificate verification, currently the following is needed: - set the property TFPHTTPClient.VerifySSlCertificate to True - implement an AfterSocketHandlerCreate callback to provide a link to trusted (CA) certificates. See example 1 below for an example of the current way to enable verification. Considering that: - certificate verification IMHO should be encouraged as much as possible; - using the VerifySSlCertificate property only makes sense when also providing a link to trusted certificates (otherwise every verification will fail); - setting the VerifySSLCertificate property is very straightforward and easy to implement; - writing a callback implementation is always needed when setting this property to True; - writing a callback implementation is less straightforward and could be seen as a less efficient way to use 'basic' functionality; - both the OpenSSL and GnuTLS implementations accept a CertCA file (e.g. PEM file containing trusted CA certificates) or a TrustedCertsDir (containing trusted (CA) certificates), I think it makes sense to add a CertCAFileName and TrustedCertsDir property to TFPHTTPClient. These properties could be used together with the VerifySSlCertificate property. TFPHTTPClient uses the VerifySSlCertificate property only when it creates the SocketHandler (which, for HTTPS connections, will be a SSLSocketHandler and could use OpenSSL or GnuTLS handlers). When creating a SSL socket handler, it passes the value of the VerifySSlCertificate property to the socket handler. This could be a perfect moment to pass the CertCA file location or a TrustedCertsDir to the socket handler as well. By doing this, no callback implementation for the AfterSocketHandlerCreate event is needed anymore, except for exceptional cases (in which cases this will still be possible). See example 2 below how this could be used in practice. It is now easier and more straightforward to enable certificate verification. See the patch below for an implementation of this suggestion. I think it fits well with the way VerifySSlCertificate is currently implemented. The suggested code additions have no impact on existing code. ## Example 1: using certificate verification in current situation ```pascal program project1; {$mode objfpc}{$H+} uses SysUtils, fphttpclient, ssockets, opensslsockets, fpopenssl, openssl; const URL = 'https://example.com/'; type TTestApp = class(TObject) private procedure DoHaveSocketHandler(Sender: TObject; AHandler: TSocketHandler); procedure Run; end; procedure TTestApp.DoHaveSocketHandler(Sender: TObject; AHandler: TSocketHandler); var SSLHandler: TOpenSSLSocketHandler absolute aHandler; begin if (aHandler is TOpenSSLSocketHandler) then begin SSLHandler.CertificateData.TrustedCertsDir := '/etc/ssl/certs/'; end; end; procedure TTestApp.Run; begin with TFPHTTPClient.Create(nil) do try VerifySSlCertificate := True; AfterSocketHandlerCreate := @DoHaveSocketHandler; WriteLn(Get(URL)); finally Free; end; end; begin with TTestApp.Create do try Run; finally Free; end; end. ``` ## Example 2: using certificate verification after proposed addition ```pascal program project2; {$mode objfpc}{$H+} uses SysUtils, fphttpclient, opensslsockets; const URL = 'https://example.com/'; begin with TFPHTTPClient.Create(nil) do try VerifySSlCertificate := True; TrustedCertsDir := '/etc/ssl/certs/'; // or: CertCAFileName:='ca-bundle.crt'; WriteLn(Get(URL)); finally Free; end; end. ``` ## Suggested code addition In fphttpclient.pp, in essence, I suggest adding the following two lines: ```pascal function TFPCustomHTTPClient.GetSocketHandler(const UseSSL: Boolean): TSocketHandler; Var SSLHandler : TSSLSocketHandler; begin Result:=Nil; if Assigned(FonGetSocketHandler) then FOnGetSocketHandler(Self,UseSSL,Result); if (Result=Nil) then If UseSSL then begin SSLHandler:=TSSLSocketHandler.GetDefaultHandler; SSLHandler.VerifyPeerCert:=FVerifySSLCertificate; SSLHandler.OnVerifyCertificate:=@DoVerifyCertificate; SSLHandler.CertificateData.CertCA.FileName:=FCertCAFileName; // <----- new!! SSLHandler.CertificateData.TrustedCertsDir:=FTrustedCertsDir; // <----- new!! Result:=SSLHandler; end else Result:=TSocketHandler.Create; if Assigned(AfterSocketHandlerCreate) then AfterSocketHandlerCreate(Self,Result); end; ``` ## Suggested patch The patch below adds these lines, including declaration of these properties and an explanatory comment. [fphttpclient.pp.patch](/uploads/ef0f0184eb344d322c7250b2dc883804/fphttpclient.pp.patch)
issue