WebSocket origin should have deterministic and validated origin header

UNCONFIRMED
Unassigned

Status

P5
normal
UNCONFIRMED
3 years ago
4 months ago

People

(Reporter: jamie, Unassigned)

Tracking

46 Branch

Firefox Tracking Flags

(Not tracked)

Details

(Whiteboard: triaged)

(Reporter)

Description

3 years ago
User Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_4) AppleWebKit/601.5.17 (KHTML, like Gecko) Version/9.1 Safari/601.5.17

Steps to reproduce:

Packaged 1Password extension code as a WebExtension per https://developer.mozilla.org/en-US/Add-ons/WebExtensions/Packaging_and_installation


Actual results:

Received unknown WebSocket origin moz-extension://e9d08159-f94a-5646-b22f-24082b5f7a75

Asked in IRC #webextensions and evilpie said it is a device-specific random UUID.



Expected results:

The add-on should have a deterministic origin. 1Password uses this for validating the origin of the extension as a valid 1Password extension to guard against rogue extensions attempting to connect to the user's 1Password data. This would also apply to non-local uses of WebSocket that we might use in the future including but not limited to communicating with 1Password for Teams/Families.

In Chrome, the origin includes the extension identifier, which is tied to the code signing certificate, so if an add-on tries to spoof the identity, it will not be validated by Chrome.

Comment 1

3 years ago
This certainly seems like a worthwhile thing to fix, however a rogue xpcom extension could still open a tcp socket and spoof the origin header.  So even if we fix this for websockets opened from webextensions, I don't think 1Password can necessarily trust origin headers coming from firefox...

Updated

3 years ago
Priority: -- → P2
Whiteboard: triaged
(Reporter)

Comment 2

3 years ago
In looking at the fetch specs[1], it looks like anything with Sec- prefix as well as the non-legacy Origin header should be prevented from being set except by the user agent.  Seems to me this should also apply to WebSocket connections as well. I'm not sure if there's an argument to be made for an xpcom extension to be considered a separate user agent from Firefox… Thoughts?

[1] https://fetch.spec.whatwg.org/#forbidden-header-name
Flags: needinfo?(aswan)

Comment 3

3 years ago
(In reply to Jamie Phelps from comment #2)
> In looking at the fetch specs[1], it looks like anything with Sec- prefix as
> well as the non-legacy Origin header should be prevented from being set
> except by the user agent.  Seems to me this should also apply to WebSocket
> connections as well. I'm not sure if there's an argument to be made for an
> xpcom extension to be considered a separate user agent from Firefox…
> Thoughts?
> 
> [1] https://fetch.spec.whatwg.org/#forbidden-header-name

Right, anything that goes through Firefox HTTP handling (including WebSocket via DOM APIs or via XPCOM) should be prevented from overriding the Origin header.  But a determined attacker could create a raw TCP socket via XPCOM and implement (enough of) the websocket protocol to communicate with your native application with arbitrary headers.
This is of course not a technique we would encourage but if you want to be thorough in considering things an attacker might try to do, be aware of it.
Flags: needinfo?(aswan)
(In reply to Jamie Phelps from comment #0)
> Received unknown WebSocket origin
> moz-extension://e9d08159-f94a-5646-b22f-24082b5f7a75
> 
> Asked in IRC #webextensions and evilpie said it is a device-specific random
> UUID.

WebExtensions are signed and we know therefore that the ID is unique[*]. Couldn't we make the origin moz-extension://<id> instead? Is there some privacy reason for using a per-device UUID? This was also an issue for app:// origins in Firefox OS and I believe they made deterministic-IDs an option in the manifest. Paul, do you remember the issues there?


[*] for most people. If you've got a dev build with signing disabled then an add-on could fake any ID it wanted.
Flags: needinfo?(ptheriault)
Flags: needinfo?(amckay)
(In reply to Daniel Veditz [:dveditz] from comment #4)
> (In reply to Jamie Phelps from comment #0)
> > Received unknown WebSocket origin
> > moz-extension://e9d08159-f94a-5646-b22f-24082b5f7a75
> > 
> > Asked in IRC #webextensions and evilpie said it is a device-specific random
> > UUID.
> 
> WebExtensions are signed and we know therefore that the ID is unique[*].
> Couldn't we make the origin moz-extension://<id> instead? Is there some
> privacy reason for using a per-device UUID? This was also an issue for
> app:// origins in Firefox OS and I believe they made deterministic-IDs an
> option in the manifest. Paul, do you remember the issues there?

Yes, we allowed apps to define their own origin in the manifest, like app://whatever.developer.wants. In theory you were support to use a domain you owned[1], but I think this was only enforced through the review process which is obviously not ideal. 

So long as we can ensure that we never sign two extensions with the same effective origin (whether that be based off id or whatever), that sounds ok to me.


[1] https://developer.mozilla.org/en-US/docs/Archive/Firefox_OS/Firefox_OS_apps/Building_apps_for_Firefox_OS/Manifest#origin
Flags: needinfo?(ptheriault)

Updated

2 years ago
Duplicate of this bug: 1281258

Updated

2 years ago
Depends on: 1271663
Flags: needinfo?(amckay)

Comment 7

2 years ago
(In reply to Jamie Phelps from comment #0)
> The add-on should have a deterministic origin. 1Password uses this for
> validating the origin of the extension as a valid 1Password extension to
> guard against rogue extensions attempting to connect to the user's 1Password
> data. 

In my opinion this is in conflict with interoperability and openness. I don't see why other extensions should not be allowed to interact with a native service assuming their use is non-malicious, which should be ensured by review and not through vendor-lockin.

Updated

2 years ago
Component: WebExtensions: Untriaged → WebExtensions: General

Updated

a year ago
See Also: → bug 1307852

Comment 8

a year ago
Hi Jamie,

Is this related to the one that Rob landed in Fx55, bug 1307852?  We were tracking that as a blocker for 1password, but also found this bug.. :) 

I'm hoping bug 1307852 solves the issue and this can be closed as a duplicate.
Flags: needinfo?(jamie)
(Reporter)

Comment 9

a year ago
This can be closed from our perspective. It's not properly a duplicate since the bug 1307852 is regarding Native Messaging and this is about WebSockets. I still think that predictability of the WebSocket Origin header could be useful, but it isn't a blocker for us since our WebExtension will no longer use WebSocket.
Flags: needinfo?(jamie)

Comment 10

a year ago
Thanks Jamie!

Comment 11

a year ago
Hi,

We are in process of porting XUL extension of Enpass password manager to WebExtensions. It is using web-sockets, and this issue of sending a random origin ID is still a blocker for us. Until the origin is not deterministic, we can't verify the source of request at the Enpass app <link here> to grant access to Enpass's data.
Enpass extension: https://addons.mozilla.org/en-US/firefox/addon/enpass-password-manager/
Enpass App: https://www.enpass.io/downloads/

In chrome, it is not an issue as their origin consist of chrome extension unique ID i.e chrome-extension://<addon-id>. Please look into this.

Comment 12

a year ago
(In reply to Vinod Kumar from comment #11)
> we can't verify the source of request at the Enpass app <link here> to grant
> access to Enpass's data.

You should not "verify" an implementation (this reeks of DRM or vendor-lockin), you should *authenticate* the user based on credentials that are securely stored. Security is not achieved by having an implementation self-report its identity.

Personally I would very much prefer of origin IDs were spoofable to enable interoperable implementations.

Comment 13

a year ago
(In reply to The 8472 from comment #12)
> Personally I would very much prefer of origin IDs were spoofable to enable
> interoperable implementations.

The app web-socket server is running on localhost port only and not visible to outside world. Also, It can verify that connecting client process is valid a Firefox binary or not. It just need to make sure that the connection is initiated by Firefox on behalf of Enpass extension.

Comment 14

a year ago
(In reply to Vinod Kumar from comment #13)
> The app web-socket server is running on localhost port only and not visible
> to outside world. Also, It can verify that connecting client process is
> valid a Firefox binary or not. It just need to make sure that the connection
> is initiated by Firefox on behalf of Enpass extension.

Then have the user enter some pairing ID to let the app communicate with the extension? I.e., authenticate the user's intent, not code. Otherwise you're implementing an anti-competitive solution that would prevent others from writing a drop-in replacement that interoperates with your app.
(Reporter)

Comment 15

a year ago
(In reply to Vinod Kumar from comment #13)
> The app web-socket server is running on localhost port only and not visible
> to outside world. Also, It can verify that connecting client process is
> valid a Firefox binary or not. It just need to make sure that the connection
> is initiated by Firefox on behalf of Enpass extension.

This is no longer a valid assumption for Firefox (and Chrome for that matter) even if this bug were fixed.

Comment 16

a year ago
moved back to untriaged with no priority to re-evaluate based on comment 11
Priority: P2 → --
Whiteboard: triaged

Updated

a year ago
Priority: -- → P5
Whiteboard: triaged

Updated

4 months ago
Product: Toolkit → WebExtensions
You need to log in before you can comment on or make changes to this bug.