ma1 pushed to branch tor-browser-115.6.0esr-13.5-1 at The Tor Project / Applications / Tor Browser

Commits:

3 changed files:

Changes:

  • browser/components/onionservices/OnionAliasStore.sys.mjs
    ... ... @@ -7,6 +7,7 @@ const lazy = {};
    7 7
     
    
    8 8
     ChromeUtils.defineESModuleGetters(lazy, {
    
    9 9
       JSONFile: "resource://gre/modules/JSONFile.sys.mjs",
    
    10
    +  TorRequestWatch: "resource:///modules/TorRequestWatch.sys.mjs",
    
    10 11
     });
    
    11 12
     
    
    12 13
     /* OnionAliasStore observer topics */
    
    ... ... @@ -272,6 +273,7 @@ class _OnionAliasStore {
    272 273
       }
    
    273 274
     
    
    274 275
       async init() {
    
    276
    +    lazy.TorRequestWatch.start();
    
    275 277
         await this._loadSettings();
    
    276 278
         if (this.enabled) {
    
    277 279
           await this._startUpdates();
    
    ... ... @@ -286,6 +288,7 @@ class _OnionAliasStore {
    286 288
         }
    
    287 289
         this._rulesetTimeout = null;
    
    288 290
         Services.prefs.removeObserver(kPrefOnionAliasEnabled, this);
    
    291
    +    lazy.TorRequestWatch.stop();
    
    289 292
       }
    
    290 293
     
    
    291 294
       async getChannels() {
    

  • browser/components/onionservices/TorRequestWatch.sys.mjs
    1
    +/* This Source Code Form is subject to the terms of the Mozilla Public
    
    2
    + * License, v. 2.0. If a copy of the MPL was not distributed with this
    
    3
    + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
    
    4
    +
    
    5
    +/*
    
    6
    + * This module implements Tor-specific Web Request policies, such as
    
    7
    + * preventing observable cross-site requests to .tor.onion and .bit.onion sites.
    
    8
    + */
    
    9
    +
    
    10
    +import { ConsoleAPI } from "resource://gre/modules/Console.sys.mjs";
    
    11
    +
    
    12
    +const log = new ConsoleAPI({
    
    13
    +  maxLogLevel: "warn",
    
    14
    +  maxLogLevelPref: "browser.torRequestWatch.log_level",
    
    15
    +  prefix: "TorRequestWatch",
    
    16
    +});
    
    17
    +
    
    18
    +class RequestObserver {
    
    19
    +  static #topics = [
    
    20
    +    "http-on-modify-request",
    
    21
    +    "http-on-examine-response",
    
    22
    +    "http-on-examine-cached-response",
    
    23
    +    "http-on-examine-merged-response",
    
    24
    +  ];
    
    25
    +  #asObserver(addOrRemove) {
    
    26
    +    const action = Services.obs[`${addOrRemove}Observer`].bind(Services.obs);
    
    27
    +    for (const topic of RequestObserver.#topics) {
    
    28
    +      action(this, topic);
    
    29
    +    }
    
    30
    +  }
    
    31
    +
    
    32
    +  start() {
    
    33
    +    this.#asObserver("add");
    
    34
    +    log.debug("Started");
    
    35
    +  }
    
    36
    +  stop() {
    
    37
    +    this.#asObserver("remove");
    
    38
    +    log.debug("Stopped");
    
    39
    +  }
    
    40
    +
    
    41
    +  // nsIObserver implementation
    
    42
    +  observe(subject, topic, data) {
    
    43
    +    try {
    
    44
    +      let channel = ChannelWrapper.get(
    
    45
    +        subject.QueryInterface(Ci.nsIHttpChannel)
    
    46
    +      );
    
    47
    +      switch (topic) {
    
    48
    +        case "http-on-modify-request":
    
    49
    +          this.onRequest(channel);
    
    50
    +          break;
    
    51
    +        case "http-on-examine-cached-response":
    
    52
    +        case "http-on-examine-merged-response":
    
    53
    +          channel.isCached = true;
    
    54
    +        // falls through
    
    55
    +        case "http-on-examine-response":
    
    56
    +          this.onResponse(channel);
    
    57
    +          break;
    
    58
    +      }
    
    59
    +    } catch (e) {
    
    60
    +      log.error(e);
    
    61
    +    }
    
    62
    +  }
    
    63
    +
    
    64
    +  onRequest(channel) {
    
    65
    +    if (this.shouldBlind(channel, channel.documentURL)) {
    
    66
    +      log.warn(`Blocking cross-site ${channel.finalURL} ${channel.type} load.`);
    
    67
    +      channel.cancel(Cr.NS_ERROR_ABORT);
    
    68
    +    }
    
    69
    +  }
    
    70
    +  onResponse(channel) {
    
    71
    +    if (!channel.documentURL && this.shouldBlind(channel, channel.originURL)) {
    
    72
    +      const COOP = "cross-origin-opener-policy";
    
    73
    +      // we break window.opener references if needed to mitigate XS-Leaks
    
    74
    +      for (let h of channel.getResponseHeaders()) {
    
    75
    +        if (h.name.toLowerCase() === COOP && h.value === "same-origin") {
    
    76
    +          log.debug(`${COOP} is already same-origin, nothing to do.`);
    
    77
    +          return;
    
    78
    +        }
    
    79
    +      }
    
    80
    +      log.warn(`Blinding cross-site ${channel.finalURL} load.`);
    
    81
    +      channel.setResponseHeader(COOP, "same-origin-allow-popups");
    
    82
    +    }
    
    83
    +  }
    
    84
    +
    
    85
    +  isCrossOrigin(url1, url2) {
    
    86
    +    return new URL(url1).origin !== new URL(url2).origin;
    
    87
    +  }
    
    88
    +  shouldBlindCrossOrigin(uri) {
    
    89
    +    try {
    
    90
    +      let { host } = uri;
    
    91
    +      if (host.endsWith(".onion")) {
    
    92
    +        const previousPart = host.slice(-10, -6);
    
    93
    +        return (
    
    94
    +          previousPart && (previousPart === ".tor" || previousPart === ".bit")
    
    95
    +        );
    
    96
    +      }
    
    97
    +    } catch (e) {
    
    98
    +      // no host
    
    99
    +    }
    
    100
    +    return false;
    
    101
    +  }
    
    102
    +  shouldBlind(channel, sourceURL) {
    
    103
    +    return (
    
    104
    +      sourceURL &&
    
    105
    +      this.shouldBlindCrossOrigin(channel.finalURI) &&
    
    106
    +      this.isCrossOrigin(channel.finalURL, sourceURL)
    
    107
    +    );
    
    108
    +  }
    
    109
    +}
    
    110
    +
    
    111
    +let observer;
    
    112
    +export const TorRequestWatch = {
    
    113
    +  start() {
    
    114
    +    if (!observer) {
    
    115
    +      (observer = new RequestObserver()).start();
    
    116
    +    }
    
    117
    +  },
    
    118
    +  stop() {
    
    119
    +    if (observer) {
    
    120
    +      observer.stop();
    
    121
    +      observer = null;
    
    122
    +    }
    
    123
    +  },
    
    124
    +};

  • browser/components/onionservices/moz.build
    ... ... @@ -4,4 +4,5 @@ EXTRA_JS_MODULES += [
    4 4
         "OnionAliasStore.sys.mjs",
    
    5 5
         "OnionLocationChild.sys.mjs",
    
    6 6
         "OnionLocationParent.sys.mjs",
    
    7
    +    "TorRequestWatch.sys.mjs",
    
    7 8
     ]