I`m working on an API behind CloudFlare and I would like to validate the connection fully for extended security. The platform I am using right now is Windows 10.
First I downloaded some CA's found on CloudFlare's website (Cloudflare_CA.pem, origin_ca_rsa_root.pem, origin_ca_ecc_root.pem) and then tried to contact the API after settings the required options in CURL:
struct curl_blob blob;
std::string cf_pem = ...;
blob.data = cf_pem.data();
blob.len = cf_pem.size();
blob.flags = CURL_BLOB_COPY;
curl_easy_setopt(pcurl, CURLOPT_CAINFO_BLOB, &blob);
/* Set the default value: strict certificate check please */
curl_easy_setopt(pcurl, CURLOPT_SSL_VERIFYPEER, 1L);
/* Set the default value: strict name check please */
curl_easy_setopt(pcurl, CURLOPT_SSL_VERIFYHOST, 2L);
All failed with PEER validation error. Then I have tested with openssl the following:
openssl s_client -connect website:443
The result was:
New, TLSv1.3, Cipher is TLS_AES_256_GCM_SHA384
Server public key is 256 bit
Secure Renegotiation IS NOT supported
Compression: NONE
Expansion: NONE
No ALPN negotiated
Early data was not sent
Verify return code: 20 (unable to get local issuer certificate)
Afterwards I found a very nice command which basically "extracts" the CA from the given website:
openssl s_client -showcerts -servername website -connect website:443 > cacert.pem
At the end I still received:
Verify return code: 20 (unable to get local issuer certificate)
However the cacert.pem was created with some content inside it. I grabbed the first certificate between then -----BEGIN CERTIFICATE-----/-----END CERTIFICATE----- and put it inside the code.
Validation succeeded and CURL did not complain anymore. However, I am unable to contact other hosts under CloudFlare which means this CA is not "ok".
So my question is, what to do? How I find the correct CA for CloudFlare?
Please advise, I`m sure other developers might face the same issue.
CodePudding user response:
Looking at a typical Cloudflare hosted site, the certification path ends with a root CA of "DigiCert Baltimore Root".
In Windows 10, should be able to find this cert under Local Machine Certs -> Trusted Root Certification Authority -> Certificates -> Baltimore CyberTrust Root.
Or alternatively, download from the source (will probably want to also verify fingerprints if you use this method) - https://www.digicert.com/kb/digicert-root-certificates.htm
CodePudding user response:
As @David mentioned, it is correct that CloudFlare uses DigiCert Root CA (in general). However, as they state on their website this can change; which means if you hard code such check your software might break in future at any point.
Without further due the options are as following:
Option A
Buy an "Advanced" certificate from CloudFlare instead of the Universal one, and select your desired Root CA. This ensures (up to a point) that it will be the same and you can validate any domain using that CA (in my case I tested with DigiCert Root CA).
Option B
Use mTLS feature (mTLS is a great way to protect your API). This is preferred as a long-term solution however the downside is that you need to supply/embed the certificate private key as well in your software. You can however revoke at any point the certificate if it was compromised.
For my choice I want to also validate the CA however using the DigiCert Root CA does not work for mTLS, and it's pretty clear because on your dashboard you will see:
Review Client Certificate for CN=Cloudflare, C=US
Validity Period: 15 Years
Authority: Cloudflare Managed CA for ....
I could not find/download the root certificate for "Cloudflare Managed CA". I`m not happy with this so to ensure everything works OK I have used the "Baltimore CyberTrust Root" from here: https://baltimore-cybertrust-root.chain-demos.digicert.com/info/index.html
Now you can have mTLS with CURL with CA validation as well:
// https://baltimore-cybertrust-root.chain-demos.digicert.com/info/index.html
std::string ca_pem = ...
// mtls.cert
std::string cert = ...
// mtls.key
std::string key = ...
struct curl_blob blob;
blob.data = cert.data();
blob.len = cert.size();
blob.flags = CURL_BLOB_COPY;
curl_easy_setopt(pcurl, CURLOPT_SSLCERT_BLOB, &blob);
curl_easy_setopt(pcurl, CURLOPT_SSLCERTTYPE, "PEM");
blob.data = key.data();
blob.len = key.size();
curl_easy_setopt(pcurl, CURLOPT_SSLKEY_BLOB, &blob);
curl_easy_setopt(pcurl, CURLOPT_SSLKEYTYPE, "PEM");
blob.data = ca_pem.data();
blob.len = ca_pem.size();
curl_easy_setopt(pcurl, CURLOPT_CAINFO_BLOB, &blob);
IMPORTANT NOTES REGARDING mTLS:
- Make sure you input effort to encrypt your cert/key if you embed it inside your software.
- I do not recommend you to supply your cert/key as individual files next to your software.
- Even if you encrypt your cert/key inside your software there is always a way to get it, either via unpacking/de-onfuscabtion/de-virtualization; either during runtime... etc. My advise is for you to immediately re-encrypt the data or delete it from memory ASAP.
- In case someone gets your cert/key, first generate a new cert/key then update your software and afterwards revoke the existing one. Then you should look into how someone got your cert/key and try to mitigate the issue.
Please feel free to comment if you disagree with anything and/or/if you have some suggestions/improvements.