mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-09-23 00:47:31 +09:00
(from mypal68) Full webRequest support
This commit is contained in:
parent
d90e52fac4
commit
ea6b948f3b
28 changed files with 6431 additions and 13 deletions
328
toolkit/components/webextensions/webrequest/SecurityInfo.jsm
Normal file
328
toolkit/components/webextensions/webrequest/SecurityInfo.jsm
Normal file
|
|
@ -0,0 +1,328 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
const EXPORTED_SYMBOLS = ["SecurityInfo"];
|
||||
|
||||
const { XPCOMUtils } = ChromeUtils.import(
|
||||
"resource://gre/modules/XPCOMUtils.jsm"
|
||||
);
|
||||
|
||||
const wpl = Ci.nsIWebProgressListener;
|
||||
XPCOMUtils.defineLazyServiceGetter(
|
||||
this,
|
||||
"NSSErrorsService",
|
||||
"@mozilla.org/nss_errors_service;1",
|
||||
"nsINSSErrorsService"
|
||||
);
|
||||
XPCOMUtils.defineLazyServiceGetter(
|
||||
this,
|
||||
"sss",
|
||||
"@mozilla.org/ssservice;1",
|
||||
"nsISiteSecurityService"
|
||||
);
|
||||
|
||||
// NOTE: SecurityInfo is largely reworked from the devtools NetworkHelper with changes
|
||||
// to better support the WebRequest api. The objects returned are formatted specifically
|
||||
// to pass through as part of a response to webRequest listeners.
|
||||
|
||||
const SecurityInfo = {
|
||||
/**
|
||||
* Extracts security information from nsIChannel.securityInfo.
|
||||
*
|
||||
* @param {nsIChannel} channel
|
||||
* If null channel is assumed to be insecure.
|
||||
* @param {Object} options
|
||||
*
|
||||
* @returns {Object}
|
||||
* Returns an object containing following members:
|
||||
* - state: The security of the connection used to fetch this
|
||||
* request. Has one of following string values:
|
||||
* * "insecure": the connection was not secure (only http)
|
||||
* * "weak": the connection has minor security issues
|
||||
* * "broken": secure connection failed (e.g. expired cert)
|
||||
* * "secure": the connection was properly secured.
|
||||
* If state == broken:
|
||||
* - errorMessage: full error message from
|
||||
* nsITransportSecurityInfo.
|
||||
* If state == secure:
|
||||
* - protocolVersion: one of TLSv1, TLSv1.1, TLSv1.2, TLSv1.3.
|
||||
* - cipherSuite: the cipher suite used in this connection.
|
||||
* - cert: information about certificate used in this connection.
|
||||
* See parseCertificateInfo for the contents.
|
||||
* - hsts: true if host uses Strict Transport Security,
|
||||
* false otherwise
|
||||
* - hpkp: true if host uses Public Key Pinning, false otherwise
|
||||
* If state == weak: Same as state == secure and
|
||||
* - weaknessReasons: list of reasons that cause the request to be
|
||||
* considered weak. See getReasonsForWeakness.
|
||||
*/
|
||||
getSecurityInfo(channel, options = {}) {
|
||||
const info = {
|
||||
state: "insecure",
|
||||
};
|
||||
|
||||
/**
|
||||
* Different scenarios to consider here and how they are handled:
|
||||
* - request is HTTP, the connection is not secure
|
||||
* => securityInfo is null
|
||||
* => state === "insecure"
|
||||
*
|
||||
* - request is HTTPS, the connection is secure
|
||||
* => .securityState has STATE_IS_SECURE flag
|
||||
* => state === "secure"
|
||||
*
|
||||
* - request is HTTPS, the connection has security issues
|
||||
* => .securityState has STATE_IS_INSECURE flag
|
||||
* => .errorCode is an NSS error code.
|
||||
* => state === "broken"
|
||||
*
|
||||
* - request is HTTPS, the connection was terminated before the security
|
||||
* could be validated
|
||||
* => .securityState has STATE_IS_INSECURE flag
|
||||
* => .errorCode is NOT an NSS error code.
|
||||
* => .errorMessage is not available.
|
||||
* => state === "insecure"
|
||||
*
|
||||
* - request is HTTPS but it uses a weak cipher or old protocol, see
|
||||
* https://hg.mozilla.org/mozilla-central/annotate/def6ed9d1c1a/
|
||||
* security/manager/ssl/nsNSSCallbacks.cpp#l1233
|
||||
* - request is mixed content (which makes no sense whatsoever)
|
||||
* => .securityState has STATE_IS_BROKEN flag
|
||||
* => .errorCode is NOT an NSS error code
|
||||
* => .errorMessage is not available
|
||||
* => state === "weak"
|
||||
*/
|
||||
|
||||
let securityInfo = channel.securityInfo;
|
||||
if (!securityInfo) {
|
||||
return info;
|
||||
}
|
||||
|
||||
securityInfo.QueryInterface(Ci.nsITransportSecurityInfo);
|
||||
|
||||
if (NSSErrorsService.isNSSErrorCode(securityInfo.errorCode)) {
|
||||
// The connection failed.
|
||||
info.state = "broken";
|
||||
info.errorMessage = securityInfo.errorMessage;
|
||||
if (options.certificateChain && securityInfo.failedCertChain) {
|
||||
info.certificates = this.getCertificateChain(
|
||||
securityInfo.failedCertChain,
|
||||
options
|
||||
);
|
||||
}
|
||||
return info;
|
||||
}
|
||||
|
||||
const state = securityInfo.securityState;
|
||||
|
||||
let uri = channel.URI;
|
||||
if (uri && !uri.schemeIs("https") && !uri.schemeIs("wss")) {
|
||||
// it is not enough to look at the transport security info -
|
||||
// schemes other than https and wss are subject to
|
||||
// downgrade/etc at the scheme level and should always be
|
||||
// considered insecure.
|
||||
// Leave info.state = "insecure";
|
||||
} else if (state & wpl.STATE_IS_SECURE) {
|
||||
// The connection is secure if the scheme is sufficient
|
||||
info.state = "secure";
|
||||
} else if (state & wpl.STATE_IS_BROKEN) {
|
||||
// The connection is not secure, there was no error but there's some
|
||||
// minor security issues.
|
||||
info.state = "weak";
|
||||
info.weaknessReasons = this.getReasonsForWeakness(state);
|
||||
} else if (state & wpl.STATE_IS_INSECURE) {
|
||||
// This was most likely an https request that was aborted before
|
||||
// validation. Return info as info.state = insecure.
|
||||
return info;
|
||||
} else {
|
||||
// No known STATE_IS_* flags.
|
||||
return info;
|
||||
}
|
||||
|
||||
// Cipher suite.
|
||||
info.cipherSuite = securityInfo.cipherName;
|
||||
|
||||
// Key exchange group name.
|
||||
if (securityInfo.keaGroupName !== "none") {
|
||||
info.keaGroupName = securityInfo.keaGroupName;
|
||||
}
|
||||
|
||||
// Certificate signature scheme.
|
||||
if (securityInfo.signatureSchemeName !== "none") {
|
||||
info.signatureSchemeName = securityInfo.signatureSchemeName;
|
||||
}
|
||||
|
||||
info.isDomainMismatch = securityInfo.isDomainMismatch;
|
||||
info.isExtendedValidation = securityInfo.isExtendedValidation;
|
||||
info.isNotValidAtThisTime = securityInfo.isNotValidAtThisTime;
|
||||
info.isUntrusted = securityInfo.isUntrusted;
|
||||
|
||||
info.certificateTransparencyStatus = this.getTransparencyStatus(
|
||||
securityInfo.certificateTransparencyStatus
|
||||
);
|
||||
|
||||
// Protocol version.
|
||||
info.protocolVersion = this.formatSecurityProtocol(
|
||||
securityInfo.protocolVersion
|
||||
);
|
||||
|
||||
if (options.certificateChain && securityInfo.succeededCertChain) {
|
||||
info.certificates = this.getCertificateChain(
|
||||
securityInfo.succeededCertChain,
|
||||
options
|
||||
);
|
||||
} else {
|
||||
info.certificates = [
|
||||
this.parseCertificateInfo(securityInfo.serverCert, options),
|
||||
];
|
||||
}
|
||||
|
||||
// HSTS and static pinning if available.
|
||||
if (uri && uri.host) {
|
||||
// SiteSecurityService uses different storage if the channel is
|
||||
// private. Thus we must give isSecureURI correct flags or we
|
||||
// might get incorrect results.
|
||||
let flags = 0;
|
||||
if (
|
||||
channel instanceof Ci.nsIPrivateBrowsingChannel &&
|
||||
channel.isChannelPrivate
|
||||
) {
|
||||
flags = Ci.nsISocketProvider.NO_PERMANENT_STORAGE;
|
||||
}
|
||||
|
||||
info.hsts = sss.isSecureURI(sss.HEADER_HSTS, uri, flags);
|
||||
info.hpkp = sss.isSecureURI(sss.STATIC_PINNING, uri, flags);
|
||||
} else {
|
||||
info.hsts = false;
|
||||
info.hpkp = false;
|
||||
}
|
||||
|
||||
return info;
|
||||
},
|
||||
|
||||
getCertificateChain(certChain, options = {}) {
|
||||
let certificates = [];
|
||||
for (let cert of certChain) {
|
||||
certificates.push(this.parseCertificateInfo(cert, options));
|
||||
}
|
||||
return certificates;
|
||||
},
|
||||
|
||||
/**
|
||||
* Takes an nsIX509Cert and returns an object with certificate information.
|
||||
*
|
||||
* @param {nsIX509Cert} cert
|
||||
* The certificate to extract the information from.
|
||||
* @param {Object} options
|
||||
* @returns {Object}
|
||||
* An object with following format:
|
||||
* {
|
||||
* subject: subjectName,
|
||||
* issuer: issuerName,
|
||||
* validity: { start, end },
|
||||
* fingerprint: { sha1, sha256 }
|
||||
* }
|
||||
*/
|
||||
parseCertificateInfo(cert, options = {}) {
|
||||
if (!cert) {
|
||||
return {};
|
||||
}
|
||||
|
||||
let certData = {
|
||||
subject: cert.subjectName,
|
||||
issuer: cert.issuerName,
|
||||
validity: {
|
||||
start: cert.validity.notBefore
|
||||
? Math.trunc(cert.validity.notBefore / 1000)
|
||||
: 0,
|
||||
end: cert.validity.notAfter
|
||||
? Math.trunc(cert.validity.notAfter / 1000)
|
||||
: 0,
|
||||
},
|
||||
fingerprint: {
|
||||
sha1: cert.sha1Fingerprint,
|
||||
sha256: cert.sha256Fingerprint,
|
||||
},
|
||||
serialNumber: cert.serialNumber,
|
||||
isBuiltInRoot: cert.isBuiltInRoot,
|
||||
subjectPublicKeyInfoDigest: {
|
||||
sha256: cert.sha256SubjectPublicKeyInfoDigest,
|
||||
},
|
||||
};
|
||||
if (options.rawDER) {
|
||||
certData.rawDER = cert.getRawDER();
|
||||
}
|
||||
return certData;
|
||||
},
|
||||
|
||||
// Bug 1355903 Transparency is currently disabled using security.pki.certificate_transparency.mode
|
||||
getTransparencyStatus(status) {
|
||||
switch (status) {
|
||||
case Ci.nsITransportSecurityInfo.CERTIFICATE_TRANSPARENCY_NOT_APPLICABLE:
|
||||
return "not_applicable";
|
||||
case Ci.nsITransportSecurityInfo
|
||||
.CERTIFICATE_TRANSPARENCY_POLICY_COMPLIANT:
|
||||
return "policy_compliant";
|
||||
case Ci.nsITransportSecurityInfo
|
||||
.CERTIFICATE_TRANSPARENCY_POLICY_NOT_ENOUGH_SCTS:
|
||||
return "policy_not_enough_scts";
|
||||
case Ci.nsITransportSecurityInfo
|
||||
.CERTIFICATE_TRANSPARENCY_POLICY_NOT_DIVERSE_SCTS:
|
||||
return "policy_not_diverse_scts";
|
||||
}
|
||||
return "unknown";
|
||||
},
|
||||
|
||||
/**
|
||||
* Takes protocolVersion of TransportSecurityInfo object and returns human readable
|
||||
* description.
|
||||
*
|
||||
* @param {number} version
|
||||
* One of nsITransportSecurityInfo version constants.
|
||||
* @returns {string}
|
||||
* One of TLSv1, TLSv1.1, TLSv1.2, TLSv1.3 if version
|
||||
* is valid, Unknown otherwise.
|
||||
*/
|
||||
formatSecurityProtocol(version) {
|
||||
switch (version) {
|
||||
case Ci.nsITransportSecurityInfo.TLS_VERSION_1:
|
||||
return "TLSv1";
|
||||
case Ci.nsITransportSecurityInfo.TLS_VERSION_1_1:
|
||||
return "TLSv1.1";
|
||||
case Ci.nsITransportSecurityInfo.TLS_VERSION_1_2:
|
||||
return "TLSv1.2";
|
||||
case Ci.nsITransportSecurityInfo.TLS_VERSION_1_3:
|
||||
return "TLSv1.3";
|
||||
}
|
||||
return "unknown";
|
||||
},
|
||||
|
||||
/**
|
||||
* Takes the securityState bitfield and returns reasons for weak connection
|
||||
* as an array of strings.
|
||||
*
|
||||
* @param {number} state
|
||||
* nsITransportSecurityInfo.securityState.
|
||||
*
|
||||
* @returns {array<string>}
|
||||
* List of weakness reasons. A subset of { cipher } where
|
||||
* * cipher: The cipher suite is consireded to be weak (RC4).
|
||||
*/
|
||||
getReasonsForWeakness(state) {
|
||||
// If there's non-fatal security issues the request has STATE_IS_BROKEN
|
||||
// flag set. See https://hg.mozilla.org/mozilla-central/file/44344099d119
|
||||
// /security/manager/ssl/nsNSSCallbacks.cpp#l1233
|
||||
let reasons = [];
|
||||
|
||||
if (state & wpl.STATE_IS_BROKEN) {
|
||||
if (state & wpl.STATE_USES_WEAK_CRYPTO) {
|
||||
reasons.push("cipher");
|
||||
}
|
||||
}
|
||||
|
||||
return reasons;
|
||||
},
|
||||
};
|
||||
Loading…
Add table
Add a link
Reference in a new issue