Skip to content
This repository has been archived by the owner on Nov 6, 2023. It is now read-only.

Detect static securecookies to trivialize more rules #16029

Merged
merged 10 commits into from
Aug 22, 2018
Merged

Detect static securecookies to trivialize more rules #16029

merged 10 commits into from
Aug 22, 2018

Conversation

cschanaj
Copy link
Collaborator

@cschanaj cschanaj commented Jul 6, 2018

This PR modify trivialize-rules.js to detect static securecookie to trivialize more rules. It also trivialize the securecookie tags if all the securecookie domain and target domain are strictly identical. This will further trivializes about 100+ rulesets when ran against 8cfabe5

cc @RReverser @Hainish @Bisaloo @J0WI

@RReverser
Copy link
Contributor

@cschanaj Can you please explain in prose what rules you're using / when it's going to assume that cookies are ok? I believe this is quite tricky to detect statically but I might be wrong and hard to understand the nuances from code.

@cschanaj
Copy link
Collaborator Author

cschanaj commented Jul 9, 2018

@RReverser This PR aims to trivialize rulesets that would have been modified if securecookies are ignored. It is assumed that if all securecookies are static, they can be safely ignored.

A securecookie is called to be static either it is a trivial securecookie or ALL of the following conditions are satisfied:

  1. securecookie.host match cookie.host from the beginning ^ to the end $. Otherwise, it might match subdomains/ partial patterns, thus a non-trivial securecookie.
  2. securecookie.host will not throw an error when passed to explodeRegExp(). Otherwise, it might match patterns too complicated for our interests.
  3. Each exploded securecookie.host should be included in ruleset.target/ exploded target. Otherwise, this ruleset is likely problematic itself. It is dangerous for a rewrite.

P.S. I have force-pushed 917e627 because there was an logic error in the previous implementation. Sadly we cannot trivialize the securecookies anyway.

P.S.2. When running this commit against the master, it is found that the script is problematic for AllianceBernstein.xml, namely a trivial domain is gone from ruleset.target; but I am guessing this a bug from the upstream...

@Bisaloo
Copy link
Collaborator

Bisaloo commented Jul 10, 2018

@cschanaj, it would be useful to have this info somewhere else than a comment in this PR. It would be great if you could copy your text from #16029 (comment) in either the commit message or a standalone README.

This PR aims to trivialize rulesets that would have been modified if
securecookies are ignored. It is assumed that if all securecookies are static,
they can be safely ignored.

A securecookie is called to be static either it is a trivial securecookie or ALL
of the following conditions are satisfied:

1. securecookie.host match cookie.host from the beginning ^ to the end $.
Otherwise, it might match subdomains/ partial patterns, thus a non-trivial
securecookie.

2. securecookie.host will not throw an error when passed to explodeRegExp().
Otherwise, it might match patterns too complicated for our interests.

3. Each exploded securecookie.host should be included in ruleset.target/
exploded target. Otherwise, this ruleset is likely problematic itself. It is
dangerous for a rewrite.
@cschanaj
Copy link
Collaborator Author

@Bisaloo I've added the comment in the commit message of 77f08cf

@RReverser
Copy link
Contributor

@cschanaj I see that you, for example, assume that securecookie.host === '.+' && securecookie.name === '.+' means that domain list can be trivialised; I think the opposite, it means that cookies might cover hosts not covered by rule expressions and so any current targets have to be preserved.

Also I wonder how you treat cookies on subdomains? In general, subdomain1.cookie.com can set cookies for subdomain2.cookie.com as well as cookie.com itself and so just the fact that cookie host doesn't appear in explicit targets, doesn't mean that targets can be trivialised.

@cschanaj
Copy link
Collaborator Author

cschanaj commented Jul 11, 2018

@RReverser For cookies to be covered, there must be at least one rule covering the same domain. This is guarantee in safeToSecureCookie(cookie). In which a random URL is generated to test if we will HTTPSify it before securing a cookie.

Trivial securecookie implies that domain list can be trivialized. This also prevent the case where subdomain1 set cookies for subdomain2 which is not covered. In fact, subdomain2 cannot be secured by HTTPSE under the current implementation.

// If we passed that test, make up a random URL on the domain, and see if
// we would HTTPSify that.
var nonce_path = "/" + Math.random().toString();
var test_uri = "http://" + domain + nonce_path + nonce_path;
// Cap the size of the cookie cache (limit chosen somewhat arbitrarily)
if (this.cookieHostCache.size > 250) {
// Map.prototype.keys() returns keys in insertion order, so this is a FIFO.
this.cookieHostCache.delete(this.cookieHostCache.keys().next().value);
}
util.log(util.INFO, "Testing securecookie applicability with " + test_uri);
var potentiallyApplicable = this.potentiallyApplicableRulesets(domain);
for (let ruleset of potentiallyApplicable) {
if (!ruleset.active) {
continue;
}
if (ruleset.apply(test_uri)) {
util.log(util.INFO, "Cookie domain could be secured.");
this.cookieHostCache.set(domain, true);
return true;
}
}
util.log(util.INFO, "Cookie domain could NOT be secured.");
this.cookieHostCache.set(domain, false);
return false;

@RReverser
Copy link
Contributor

For cookies to be covered, there must be at least one rule covering the same domain.

Ah, that I didn't know. It does change the perspective and optimisation opportunities. Thanks!

@cschanaj
Copy link
Collaborator Author

cschanaj commented Jul 11, 2018

When running this commit against the master, it is found that the script is problematic for AllianceBernstein.xml, namely a trivial domain is gone from ruleset.target; but I am guessing this a bug from the upstream...

@RReverser could you please confirm this? It seem that not all covered domains are enumerated by explodeRegExp() for certain type of RegExp. You might remove the securecookie tag to run trivialize-rules in master to see the bug. More trivialization can be made if this got fixed.

@RReverser
Copy link
Contributor

@cschanaj Do you mean this?

diff --git a/src/chrome/content/rules/AllianceBernstein.xml b/src/chrome/content/rules/AllianceBernstein.xml
index 766b024548..db07742ff9 100644
--- a/src/chrome/content/rules/AllianceBernstein.xml
+++ b/src/chrome/content/rules/AllianceBernstein.xml
@@ -11,16 +11,14 @@
 -->
 <ruleset name="AllianceBernstein (partial)">

+       <target host="bernstein.com" />
        <target host="alliancebernstein.com" />
        <target host="www.alliancebernstein.com" />
-       <target host="bernstein.com" />
-       <target host="www.bernstein.com" />


        <securecookie host="^(?:www\.)?alliancebernstein\.com$" name=".+" />


-       <rule from="^http://(www\.)?(alliance)?bernstein\.com/"
-               to="https://$1$2bernstein.com/" />
+       <rule from="^http:" to="https:" />

 </ruleset>

@RReverser
Copy link
Contributor

Okay, yes, I found the bug.

@RReverser
Copy link
Contributor

Fix is here: #16051

@cschanaj
Copy link
Collaborator Author

@RReverser yes, thanks for filing a fix!!

Copy link
Member

@Hainish Hainish left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good. Please include a comment in the code as per @Bisaloo's suggestion on what a "static" cookie is, since we're introducing new terminology.

try {
explodeRegExp(securecookie.host, domain => {
if (domain.startsWith('.')) {
domain = domain.slice(1);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this sufficient munging to ensure it matches the target domains?
If not, at worst it will not match and result in a false result, which isn't terrible but not desirable.

Copy link
Collaborator Author

@cschanaj cschanaj Jul 12, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Hainish this should be good enough. As mentioned in #16029 (comment), securecookie.host only match cookie.domain if there is a rule covers cookie.domain. Given the target are trivial, cookie.domain cannot be anything other than domain.example.com and .domain.example.com for a securecookie rule to works. With condition (1) effective, securecookie.host should explode to either one of them. Otherwise, the securecookie will never be applied (dangling rules?) under the current implementation.

With the above logic in mind, I have also add 4bbd731 to remove some of these securecookie rules that will never be effective.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMHO, it would be great to have #15998 and #16000 merged before running this against the master.

@cschanaj
Copy link
Collaborator Author

cschanaj commented Jul 19, 2018

@Hainish would you merge this code before the next release if the changes I made after your first review look good to you? So that I can file a separate PR running the script and fix the failing tests.

@Hainish
Copy link
Member

Hainish commented Jul 19, 2018

@cschanaj yes - releases will be made every other month, so this should be deployed next month. I'll have more time to review next week. No promises if there is a security release, though.

@Hainish
Copy link
Member

Hainish commented Jul 19, 2018

Perhaps @bcyphers has some time to review?

@bcyphers
Copy link
Contributor

Looking at it now

@Hainish
Copy link
Member

Hainish commented Aug 3, 2018

@bcyphers?

@cschanaj
Copy link
Collaborator Author

cschanaj commented Aug 4, 2018

@RReverser my apologies, I have forgotten to rebase this PR before running the script. the issue is a duplicate of the fixed one. sorry for pinging you. please ignore my previous comment.

@bcyphers
Copy link
Contributor

bcyphers commented Aug 4, 2018

Hi, sorry, still working on this -- had to catch up on the rest of the static simplifications first. I'll have some comments this weekend.

// safely.
if (unsupportedDomains.size > 0) {
if (unsupportedDomains.size === localDomains.size) {
shouldRemoveSecurecookies = true;
Copy link
Contributor

@bcyphers bcyphers Aug 6, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If any one of the securecookies is unsupported, this will cause all of them to be removed. I don't think that's the intended behavior.

For example, for the following XML, the script will trivialize the file and remove all securecookies:

<target host="a.foo.com" />
<supercookie host="a.foo.com" />
<supercookie host="b.foo.com" />
...

But if the two securecookie entries are combined into one (<securecookie host="^(a|b)\.foo\.com$" />), the script will refuse to trivialize the XML.

It seems like we should remove one securecookie entry at a time, not drop them all at once. If that's risky (not sure why it would be), we can just refuse to trivialize any XML with unsupported securecookie domains.

See comments in code for more details.
@cschanaj
Copy link
Collaborator Author

cschanaj commented Aug 7, 2018

@bcyphers That's indeed unexpected, thanks for catching the bugs 😃 5c149aa should work, I have tested it with the following rulesets; the diff is the result from running the script.

<ruleset name="Foo.com">
	<target host="*.foo.com" />

	<securecookie host="^www\.foo\.com$" name=".+" />
	<securecookie host="^a\.foo\.com$" name=".+" />

	<rule from="^http://www\.foo\.com/" to="https://www.foo.com/" />
</ruleset>

gives

11c11
< 	<target host="*.foo.com" />
---
> 	<target host="www.foo.com" />
14,16c14
< 	<securecookie host="^a\.foo\.com$" name=".+" />
< 
< 	<rule from="^http://www\.foo\.com/" to="https://www.foo.com/" />
---
> 	<rule from="^http:" to="https:" />

and (noted that the script cannot remove partial dangling securecookie rules)

<ruleset name="Bar.com">
	<target host="*.bar.com" />

	<securecookie host="^(www|a)\.bar\.com$" name=".+" />

	<rule from="^http://www\.bar\.com/" to="https://www.bar.com/" />
</ruleset>

gives

11c11
< 	<target host="*.bar.com" />
---
> 	<target host="www.bar.com" />
15c15
< 	<rule from="^http://www\.bar\.com/" to="https://www.bar.com/" />
---
> 	<rule from="^http:" to="https:" />

@@ -4,8 +4,8 @@ const { parse } = require('regulex');

class UnsupportedRegExp extends Error {}

function explodeRegExp(re, callback) {
(function buildUrls(str, items) {
function explodeRegExp (re, callback) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, this file was previously formatted with prettier. Did it add these whitespaces or did you manually? Looks quite odd.

Copy link
Collaborator Author

@cschanaj cschanaj Aug 7, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

May I know which prettier options did you use? I can revert the changes in a37df21

P.S. a37df21 was mostly done automatically using semistandard from standardjs

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think just single-quotes plus semicolons.

Copy link
Collaborator Author

@cschanaj cschanaj Aug 9, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems to me that prettier will create some strange changes to trivialize-rules.js, e.g. spliting the back-tick quoted string to the following

+            let scReSrc = `\n([\t ]*)<securecookie\\s*host=\\s*"${escapeStringRegexp(
+              securecookie.host
+            )}"(\\s*)name=\\s*"${escapeStringRegexp(
+              securecookie.name
+            )}"\\s*?/>[\t ]*\n`;

So I guess it is better not to run prettier on it. Would you mind if I leave the file as-is in e8f27ee?

@cschanaj
Copy link
Collaborator Author

cschanaj commented Aug 9, 2018

ping @Hainish @bcyphers @RReverser

if (unsupportedDomains.size === localDomains.size) {
return [true, true];
}
// TODO: Can we remove partial dangling securecookie rules???
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't think so. This means that the author intended at least some of the target domains to use secure cookies, we shouldn't throw out that information

@cschanaj
Copy link
Collaborator Author

@bcyphers Does e5ad3c5 looks good to you?

@bcyphers
Copy link
Contributor

Yeah, I can't find any other issues. I think this is good to go.

@cschanaj
Copy link
Collaborator Author

@bcyphers @RReverser @Hainish Thanks for the effort reviewing this PR. linking #16122

@cschanaj
Copy link
Collaborator Author

@bcyphers would you or @Hainish merge this PR? I would like to run this script before the next release and submit a PR to a downstream project in https://github.com/brave/https-everywhere-builder

@Hainish
Copy link
Member

Hainish commented Aug 22, 2018

@cschanaj sorry this didn't make it in to 2018.8.22. This can be part of the ruleset release next week.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants