brave / brave-browser

Brave browser for Android, iOS, Linux, macOS, Windows.
https://brave.com
Mozilla Public License 2.0
17.87k stars 2.34k forks source link

Permissioning access to localhost connections #27346

Closed ShivanKaul closed 1 year ago

ShivanKaul commented 1 year ago

Similar to https://github.com/brave/brave-browser/issues/26273, we'd like to add a new site permission called Localhost connections. There are several legitimate use-cases that involve the website asking for access to localhost e.g. a localhost-based websocket.

Currently we block all localhost connections via Adblock, but allowlist some in brave-specific.txt.

We will be able to safely add exceptions to the localhost-blocking adblock rules, once we have this. NOTE: we still want adblock rules to apply before applying this permission. After this permission is added to Brave, in order for a site to have access to localhost resources, it still needs to do 2 things:

  1. It needs to be allowlisted in brave-specific.txt.
  2. The user needs to click Allow in the permission prompt.

NOTE: we only permission/block requests based on the URL, not the resolved IP address. This will be addressed in a follow-up: https://github.com/brave/brave-browser/issues/30038

Functionality:

  1. Create new permission/content setting called Localhost Access.
  2. Apply adblock filter rules as normal.
  3. Detect if a request is from a valid non-localhost requesting origin for a subresource that looks like a localhost resource.
  4. If current permission status is DENY, cancel the request (net:: ERR_ACCESS_DENIED).
  5. If current permission status is ALLOW, allow the request (net::OK).
  6. If current permission status is ASK, cancel the request (net:: ERR_ACCESS_DENIED), and show permission prompt.
  7. If permission granted, reload the tab. Now the permission status will be ALLOW, and the user will not get re-prompted for localhost subresource requests.
nacmonad commented 1 year ago

Hi are there plans to implement this?

ShivanKaul commented 1 year ago

Working on a PR right now. If folks have test websites that don't currently work on Brave, please drop them here, would help a lot with smoke testing.

CharlieGreenman commented 1 year ago

@ShivanKaul if interested razroo.com x razroo vscode plugin(https://marketplace.visualstudio.com/items?itemName=Razroo.razroo-vscode-plugin) is a good example(will need to sign up/sign into razroo in order to use). We spin up a local server/localhost in the vscode plugin and pass information directly from web to local vscode plugin. It works on chrome, and edge, but not on brave for I believe this reason. If authentication works with vscode plugin, it means officially fixed.

ShivanKaul commented 1 year ago

Cool, that seems to work well on my local feature branch for this issue. This is the experience:

https://user-images.githubusercontent.com/5284154/220427702-b3cb09db-f848-4f02-905f-3a71ed1557b7.mov

CharlieGreenman commented 1 year ago

cool that means it should be working as expected. Prior brave users were unable to authenticate. Thank you

ShivanKaul commented 1 year ago

Thanks for the use-case! Helps a lot. If others have ones too, that would help get this out faster because we'd be surer we're not breaking things.

nacmonad commented 1 year ago

Thanks for the use-case! Helps a lot. If others have ones too, that would help get this out faster because we'd be surer we're not breaking things.

Is there a dev branch I can check out? I am running into this issue presently, but the software is proprietary/beta...

CharlieGreenman commented 1 year ago

@nacmonad just to confirm is that question directed to me or @ShivanKaul

CharlieGreenman commented 1 year ago

🚀

ShivanKaul commented 1 year ago

This feature has been merged in for both Desktop and Android but is default-disabled while we work on a better way of shipping which websites should be allowed to request permission.

CharlieGreenman commented 1 year ago

@ShivanKaul why not allow users to decide that on their own?

ShivanKaul commented 1 year ago

There is a very real danger of port-scanning attacks to fingerprint users which is why we'd blocked localhost connections in the first place. We want to prevent those websites from being able to spam users with permission prompts, which has an additional danger of training users to say yes to dangerous actions that they may not understand the significance of while they're just trying to get work done in the browser (which is why Brave disables Filesystem API, for instance).

@CharlieGreenman the razroo auth use-case you brought up seems legitimate. We will add that to the initial list when we roll it out.

CharlieGreenman commented 1 year ago

Thank you. It goes without saying my thanks as always

Screenshot 2023-04-27 at 9 28 44 PM
MicahZoltu commented 1 year ago

default-disabled while we work on a better way of shipping which websites should be allowed to request permission.

I'm generally not a fan of a whitelist (which it sounds like you are suggesting), as that seems fairly antithetical to the idea of a "decentralized and permissionless internet". Could you solve the spam problem similar to how Firefox (and maybe Brave too) solves the alert popup and new tab spam problem with a dialog that asks if you want to let the site continue to prompt you?

It seems that the same technique as mic/camera/notification access could be used, where the page asks for permission to access other services running on your device and you can accept, reject, and reject with prejudice (never ask again for this site).

ShivanKaul commented 1 year ago

The issue is not one particular website doing it (Brave has finer-grained permission scope and lifetime than other browsers), but multiple websites prompting for the localhost permission. Port-scanning fingerprinting attacks are very common and easy [1], [2].

Our general stance has been that Web APIs too harmful for user privacy should be disabled without a permission prompt, because users cannot meaningfully consent to them. In the case of localhost connections, it seems like there are some benign use-cases, so we're experimenting with selectively allowing them.

MicahZoltu commented 1 year ago

Perhaps I'm misunderstanding. How would you selectively allow some use cases without doing specific website whitelisting? Are you thinking of allowing some subset of ports or protocols or something when communicating with localhost, while disallowing port scanning? Or perhaps constrain how many ports a given website can attempt to query locally before they get locked out from querying any additional ports?

ShivanKaul commented 1 year ago

We will be allowing only specific websites to ask for this permission.

MicahZoltu commented 1 year ago

I don't think that a permissioned/gatekept web is an appropriate solution for this. Even though I want this feature, I think permissioning it is a strictly worse solution than not having it at all because it allows large companies to create moats around their exclusive access to browser features and it reduces the impetus and pressure for the Brave team to find a solution that works for everyone.

As a user and a web/extension developer, Brave implementing a permissioned web in their browser is a huge turnoff for me. I generally like Brave's position on security, privacy, and censorship resistance, but if you all start permissioning/censoring the web it will result in massive negative marks in my internal browser scoring rubric.

ShivanKaul commented 1 year ago

Just to clarify, there will be a way for a user to override which websites get this permission by going to the permission setting page: brave://settings/content/localhostAccess. This is just how Chromium permissions work.

This is part of the enhancement we're exploring -- currently adblocking happens first, so requests are blocked before they get to the permissioning code, but we're working on changing that because we'd like to decouple localhost blocking from adblocking. Details TBD.

image
ShivanKaul commented 1 year ago

But there will be a default list of websites that can ask for this permission.

MicahZoltu commented 1 year ago

If users can add sites to the whitelist then I think that mitigates a lot of my concerns. I'm not a huge fan of having a built-in whitelist as it gives preferential treatment to "friends of Brave" (or people who can afford the registration costs, or people who KYC, or people who go through some bureaucratic process or whatever).

Will it be possible to have certain localhost resources made available to all websites? For example, if I am running a local Ethereum client or IPFS client, I would like any website to be able to probe for and access those. A website might first probe to see if I have IPFS running locally and if so use that (which I would prefer) then fallback to a central server (same for Ethereum client). I don't want to have to whitelist every website that wants to use these resources as they are intended to made available to web apps.

In this scenario, I would still be fine with getting a prompt "This website wants to access your Ethereum client: Allow, Deny", but I wouldn't want to have to add every single website to the whitelist by hand, and I also wouldn't necessarily want to give every website full unrestricted access to all localhost resources (only a couple that I have intended to make available as part of a suite of decentralized web tools).

stephendonner commented 1 year ago

Verification PASSED using

Brave 1.52.86 Chromium: 113.0.5672.77 (Official Build) beta (x86_64)
Revision c4236862955e005c2187105415ac4a2ecf86dff1-refs/branch-heads/5672_62@{#3}
OS macOS Version 13.4 (Build 22F5059b)

Prerequisites:

1. created a directory `tests`, at `/Users/stephendonner/Desktop/tests` 2. dropped a `logo.png` image into `/tests` 3. ran `python3 -m http.server 8000` from `/tests`: ``` stephendonner@Stephens-MBP Desktop % cd tests stephendonner@Stephens-MBP tests % python3 -m http.server 8000 ```

Shared Steps:

1. installed `1.52.86` 5. launched Brave 6. opened `brave://flags` 7. set `brave://flags/#brave-localhost-access-permission` to `Enabled` 8. clicked `Relaunch` 9. opened `brave://adblock` 10. scrolled to `Create custom filters` 11. entered `@@||localhost^$domain=shivankaul.com` 12. clicked `Save changes` 13. loaded `https://shivankaul.com/brave/localhost/` `brave://adblock` | `brave://flags` ---------------|----------------- Screen Shot 2023-05-07 at 3 07 10 PM | Screen Shot 2023-05-07 at 3 07 26 PM

Case 1: Subresource image test - PASSED

### `Allow` (continued from `Shared Steps`) 1. loaded `https://shivankaul.com/brave/localhost/subresource.html` 2. confirmed I got the permission prompt 3. clicked `Allow` 5. opened `brave://settings/content/localhostAccess` 6. confirmed the site was listed under `Allowed to access localhost resources` ### Confirmed `logo.png` rendered permission dialog | `Allow`ed | `brave://settings/content/localhostAccess` ------------------|------------|------------------------------------------ Screen Shot 2023-05-07 at 2 11 58 PM | Screen Shot 2023-05-07 at 2 12 02 PM | Screen Shot 2023-05-07 at 2 13 10 PM ### `Block` (continued from `Shared Steps`) 1. loaded `https://shivankaul.com/brave/localhost/subresource.html` 2. confirmed I got the permission prompt 3. clicked `Block` 4. opened `brave://settings/content/localhostAccess` 5. confirmed the site was listed under `Not allowed to access localhost resources` ### Confirmed `logo.png` was blocked, and a broken-image icon displayed `Block`ed | `brave://settings/content/localhostAccess` -----------|------------------------------------------- Screen Shot 2023-05-07 at 2 50 37 PM | Screen Shot 2023-05-07 at 2 45 52 PM

Case 2: Service worker test - PASSED

### `Allow` (continued from `Shared Steps`) 1. loaded `https://shivankaul.com/brave/localhost/sw.html` 2. clicked `Allow` 3. opened `brave://settings/content/localhostAccess` 4. confirmed the site was listed under `Allowed to access localhost resources` ### Confirmed `logo.png` rendered permission dialog | `Allow`ed | `brave://settings/content/localhostAccess` ------------------|------------|------------------------------------------ Screen Shot 2023-05-07 at 3 10 40 PM | Screen Shot 2023-05-07 at 3 10 48 PM | Screen Shot 2023-05-07 at 3 16 04 PM ### `Block` (continued from `Shared Steps`) 1. loaded `https://shivankaul.com/brave/localhost/sw.html` 2. clicked `Block` 3. opened `brave://settings/content/localhostAccess` 4. confirmed the site was listed under `Not allowed to access localhost resources` ### Confirmed `logo.png` was blocked, and a broken-image icon displayed `Block`ed | `brave://settings/content/localhostAccess` -----------|------------------------------------------- Screen Shot 2023-05-07 at 3 18 23 PM | Screen Shot 2023-05-07 at 3 18 28 PM

Case 3: Websockets test - PASSED

Prerequisites: * installed `Node.js` * ran `npm install ws` * ran `node ws_server.js` (continued from `Shared Steps`) 1. loaded `https://shivankaul.com/brave/localhost/` 2. clicked on `websockets test page` (`https://shivankaul.com/brave/localhost/ws_client.html`) 3. waited 5 seconds for the redirect to happen 4. opened the `Developer` console 5. confirmed message `ping from server` 6. confirmed message `pong from client` in my `node` terminal 7. loaded `brave://settings/content/localhostAccess` 8. confirmed site entry was added to `Allowed to access localhost resources` ### `Allow` permission dialog | `Allow`ed | `pong from client!` | `brave://settings/content/localhostAccess` ------------------|------------|--------------------|------------------------------------------ Screen Shot 2023-05-08 at 8 47 51 AM | Screen Shot 2023-05-08 at 8 55 32 AM | Screen Shot 2023-05-08 at 8 55 36 AM | Screen Shot 2023-05-08 at 8 58 57 AM ### `Block` (continued from `Shared Steps`) 1. loaded `https://shivankaul.com/brave/localhost/` 2. clicked on `websockets test page` (`https://shivankaul.com/brave/localhost/ws_client.html`) 3. waited 5 seconds for the redirect to happen 4. opened the `Developer` console 5. clicked `Block` 6. opened `brave://settings/content/localhostAccess` 9. confirmed site entry was added to `Not allowed to access localhost resources` `Block`ed | `brave://settings/content/localhostAccess` -----------|------------------------------------------ Screen Shot 2023-05-08 at 9 05 04 AM | Screen Shot 2023-05-08 at 9 04 40 AM

Case 4: Request in