Skip to content

Commit ced2e87

Browse files
authored
feat: Allow detection of MITM HTTPS proxies like ZScaler (electron#30174)
* feat: Allow detection of MITM HTTPS proxies like ZScaler For security purposes, Figma heavily restrics the origins that are allowed to load within our Electron app. Unfortunately some corporate environments use MITM proxies like ZScaler, which intercepts our connection to `https://www.figma.com` and serves a redirect to e.g. `https://gateway.zscloud.net` before finally redirecting back to `https://www.figma.com`. In order to detect this situation and handle it gracefully, we need to be able to know whether or not the certificate for our own origin (`https://www.figma.com`) is chained to a known root. We do this by exposesing `CertVerifyResult::is_issued_by_known_root`. If the certification verification passed without the certificate being tied to a known root, we can safely assume that we are dealing with a MITM proxy that has its root CA installed locally on the machine. This means that HTTPS can't be trusted so we might as well make life easier for corporate users by loosening our origin restrictions without any manual steps. * Tweak docs wording
1 parent 9693fb9 commit ced2e87

File tree

5 files changed

+7
-2
lines changed

5 files changed

+7
-2
lines changed

docs/api/session.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -443,7 +443,8 @@ the original network configuration.
443443
* `hostname` String
444444
* `certificate` [Certificate](structures/certificate.md)
445445
* `validatedCertificate` [Certificate](structures/certificate.md)
446-
* `verificationResult` String - Verification result from chromium.
446+
* `isIssuedByKnownRoot` Boolean - `true` if Chromium recognises the root CA as a standard root. If it isn't then it's probably the case that this certificate was generated by a MITM proxy whose root has been installed locally (for example, by a corporate proxy). This should not be trusted if the `verificationResult` is not `OK`.
447+
* `verificationResult` String - `OK` if the certificate is trusted, otherwise an error like `CERT_REVOKED`.
447448
* `errorCode` Integer - Error code.
448449
* `callback` Function
449450
* `verificationResult` Integer - Value can be one of certificate error codes

shell/browser/net/cert_verifier_client.cc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ void CertVerifierClient::Verify(
3333
params.error_code = default_error;
3434
params.certificate = certificate;
3535
params.validated_certificate = default_result.verified_cert;
36+
params.is_issued_by_known_root = default_result.is_issued_by_known_root;
3637
cert_verify_proc_.Run(
3738
params,
3839
base::BindOnce(

shell/browser/net/cert_verifier_client.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ struct VerifyRequestParams {
1818
int error_code;
1919
scoped_refptr<net::X509Certificate> certificate;
2020
scoped_refptr<net::X509Certificate> validated_certificate;
21+
bool is_issued_by_known_root;
2122

2223
VerifyRequestParams();
2324
VerifyRequestParams(const VerifyRequestParams&);

shell/common/gin_converters/net_converter.cc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -368,6 +368,7 @@ v8::Local<v8::Value> Converter<electron::VerifyRequestParams>::ToV8(
368368
dict.Set("hostname", val.hostname);
369369
dict.Set("certificate", val.certificate);
370370
dict.Set("validatedCertificate", val.validated_certificate);
371+
dict.Set("isIssuedByKnownRoot", val.is_issued_by_known_root);
371372
dict.Set("verificationResult", val.default_result);
372373
dict.Set("errorCode", val.error_code);
373374
return ConvertToV8(isolate, dict);

spec-main/api-session-spec.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -577,7 +577,7 @@ describe('session module', () => {
577577
it('rejects the request when the callback is called with -2', async () => {
578578
const ses = session.fromPartition(`${Math.random()}`);
579579
let validate: () => void;
580-
ses.setCertificateVerifyProc(({ hostname, certificate, verificationResult }, callback) => {
580+
ses.setCertificateVerifyProc(({ hostname, certificate, verificationResult, isIssuedByKnownRoot }, callback) => {
581581
validate = () => {
582582
expect(hostname).to.equal('127.0.0.1');
583583
expect(certificate.issuerName).to.equal('Intermediate CA');
@@ -590,6 +590,7 @@ describe('session module', () => {
590590
expect(certificate.issuerCert.issuerCert.subject.commonName).to.equal('Root CA');
591591
expect(certificate.issuerCert.issuerCert.issuerCert).to.equal(undefined);
592592
expect(verificationResult).to.be.oneOf(['net::ERR_CERT_AUTHORITY_INVALID', 'net::ERR_CERT_COMMON_NAME_INVALID']);
593+
expect(isIssuedByKnownRoot).to.be.false();
593594
};
594595
callback(-2);
595596
});

0 commit comments

Comments
 (0)