Skip to content

Commit 0fcd111

Browse files
committed
Fix cookie securing under new style guide.
Cookies are only eligible to be secured if a ruleset matches them using potentiallyApplicableRulesets. Previously, cookie wildcard domains (".example.com") happened to match target host wildcards ("<target host=*.example.com />") through a quirk in the implementation of potentiallyApplicableRulesets. However, the new ruleset style guide recommends against including target host wildcards unless all subdomains are actually rewritten to HTTPS. Under the old quirky behavior, the cookie domain attribute ".example.com" does not match the target host "example.com". This change clarifies the function of potentiallyApplicableRulesets and modifies the cookie-related functions to strip a leading dot before calling potentiallyApplicableRulesets. This restores cookie securing functionality for cookies with wildcard domain attributes.
1 parent 47ec5af commit 0fcd111

File tree

1 file changed

+72
-26
lines changed

1 file changed

+72
-26
lines changed

src/chrome/content/code/HTTPSRules.js

Lines changed: 72 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -640,8 +640,15 @@ const HTTPSRules = {
640640
return output;
641641
},
642642

643+
/**
644+
* Return a list of rulesets that declare targets matching a given hostname.
645+
* The returned rulesets include those that are disabled for various reasons.
646+
* This function is only defined for fully-qualified hostnames. Wildcards and
647+
* cookie-style domain attributes with a leading dot are not permitted.
648+
* @param host {string}
649+
* @return {Array.<RuleSet>}
650+
*/
643651
potentiallyApplicableRulesets: function(host) {
644-
// Return a list of rulesets that declare targets matching this host
645652
var i, tmp, t;
646653
var results = [];
647654

@@ -655,6 +662,10 @@ const HTTPSRules = {
655662
var segmented = host.split(".");
656663
for (i = 0; i < segmented.length; ++i) {
657664
tmp = segmented[i];
665+
if (tmp.length === 0) {
666+
this.log(WARN,"Malformed host passed to potentiallyApplicableRulesets: " + host);
667+
return null;
668+
}
658669
segmented[i] = "*";
659670
t = segmented.join(".");
660671
segmented[i] = tmp;
@@ -702,23 +713,48 @@ const HTTPSRules = {
702713
this.log(NOTE, count + " hits: average subsequent call to potentiallyApplicableRulesets took " + (t2 - t1) / domains_l + " milliseconds");
703714
},
704715

705-
shouldSecureCookie: function(applicable_list, c, known_https) {
706-
// Check to see if the Cookie object c meets any of our cookierule citeria
707-
// for being marked as secure.
708-
// @applicable_list : an ApplicableList or record keeping
709-
// @c : an nsICookie2
710-
// @known_https : true if we know the page setting the cookie is https
716+
/**
717+
* If a cookie's domain attribute has a leading dot to indicate it should be
718+
* sent for all subdomains (".example.com"), return the actual host part (the
719+
* part after the dot).
720+
*
721+
* @param cookieDomain {string} A cookie domain to strip a leading dot from.
722+
* @return {string} a fully qualified hostname.
723+
*/
724+
hostFromCookieDomain: function(cookieDomain) {
725+
if (cookieDomain.length > 0 && cookieDomain[0] == ".") {
726+
return cookieDomain.slice(1);
727+
} else {
728+
return cookieDomain;
729+
}
730+
},
711731

732+
/**
733+
* Check to see if the Cookie object c meets any of our cookierule citeria
734+
* for being marked as secure.
735+
*
736+
* @param applicable_list {ApplicableList} an ApplicableList for record keeping
737+
* @param c {nsICookie2} The cookie we might secure.
738+
* @param known_https {boolean} True if the cookie appeared in an HTTPS request and
739+
* so we know it is okay to mark it secure (assuming a cookierule matches it.
740+
* TODO(jsha): Double-check that the code calling this actually does that.
741+
* @return {boolean} True if the cookie in question should have the 'secure'
742+
* flag set to true.
743+
*/
744+
shouldSecureCookie: function(applicable_list, c, known_https) {
712745
this.log(DBUG," rawhost: " + c.rawHost + " name: " + c.name + " host" + c.host);
713746
var i,j;
714-
var rs = this.potentiallyApplicableRulesets(c.host);
747+
// potentiallyApplicableRulesets is defined on hostnames not cookie-style
748+
// "domain" attributes, so we strip a leading dot before calling.
749+
var rs = this.potentiallyApplicableRulesets(this.hostFromCookieDomain(c.host));
715750
for (i = 0; i < rs.length; ++i) {
716751
var ruleset = rs[i];
717752
if (ruleset.active) {
718753
ruleset.ensureCompiled();
719754
// Never secure a cookie if this page might be HTTP
720-
if (!known_https && !this.safeToSecureCookie(c.rawHost))
755+
if (!(known_https || this.safeToSecureCookie(c.rawHost))) {
721756
continue;
757+
}
722758
for (j = 0; j < ruleset.cookierules.length; j++) {
723759
var cr = ruleset.cookierules[j];
724760
if (cr.host_c.test(c.host) && cr.name_c.test(c.name)) {
@@ -727,37 +763,45 @@ const HTTPSRules = {
727763
return true;
728764
}
729765
}
730-
if (ruleset.cookierules.length > 0)
731-
if (applicable_list) applicable_list.moot_rule(ruleset);
766+
if (ruleset.cookierules.length > 0 && applicable_list) {
767+
applicable_list.moot_rule(ruleset);
768+
}
732769
} else if (ruleset.cookierules.length > 0) {
733-
if (applicable_list) applicable_list.inactive_rule(ruleset);
770+
if (applicable_list) {
771+
applicable_list.inactive_rule(ruleset);
772+
}
734773
this.log(INFO,"Inactive cookie rule " + ruleset.name);
735774
}
736775
}
737776
return false;
738777
},
739778

779+
/**
780+
* Check if the domain might be being served over HTTP. If so, it isn't
781+
* safe to secure a cookie! We can't always know this for sure because
782+
* observing cookie-changed doesn't give us enough context to know the
783+
* full origin URI. In particular, if cookies are set from Javascript (as
784+
* opposed to HTTP/HTTPS responses), we don't know what page context that
785+
* Javascript ran in.
786+
787+
* First, if there are any redirect loops on this domain, don't secure
788+
* cookies. XXX This is not a very satisfactory heuristic. Sometimes we
789+
* would want to secure the cookie anyway, because the URLs that loop are
790+
* not authenticated or not important. Also by the time the loop has been
791+
* observed and the domain blacklisted, a cookie might already have been
792+
* flagged as secure.
793+
*
794+
* @param domain {string} The cookie's 'domain' attribute.
795+
* @return {boolean} True if it's safe to secure a cookie on that domain.
796+
*/
740797
safeToSecureCookie: function(domain) {
741-
// Check if the domain might be being served over HTTP. If so, it isn't
742-
// safe to secure a cookie! We can't always know this for sure because
743-
// observing cookie-changed doesn't give us enough context to know the
744-
// full origin URI.
745-
746-
// First, if there are any redirect loops on this domain, don't secure
747-
// cookies. XXX This is not a very satisfactory heuristic. Sometimes we
748-
// would want to secure the cookie anyway, because the URLs that loop are
749-
// not authenticated or not important. Also by the time the loop has been
750-
// observed and the domain blacklisted, a cookie might already have been
751-
// flagged as secure.
752-
753798
if (domain in https_blacklist_domains) {
754799
this.log(INFO, "cookies for " + domain + "blacklisted");
755800
return false;
756801
}
757802

758803
// If we passed that test, make up a random URL on the domain, and see if
759804
// we would HTTPSify that.
760-
761805
try {
762806
var nonce_path = "/" + Math.random().toString();
763807
nonce_path = nonce_path + nonce_path;
@@ -769,7 +813,9 @@ const HTTPSRules = {
769813
}
770814

771815
this.log(DBUG, "Testing securecookie applicability with " + test_uri);
772-
var rs = this.potentiallyApplicableRulesets(domain);
816+
// potentiallyApplicableRulesets is defined on hostnames not cookie-style
817+
// "domain" attributes, so we strip a leading dot before calling.
818+
var rs = this.potentiallyApplicableRulesets(this.hostFromCookieDomain(domain));
773819
for (var i = 0; i < rs.length; ++i) {
774820
if (!rs[i].active) continue;
775821
var rewrite = rs[i].apply(test_uri);

0 commit comments

Comments
 (0)