Open Bug 1257989 Opened 5 years ago Updated 28 days ago

WebSocket origin should have deterministic and validated origin header

Categories

(WebExtensions :: General, defect, P5)

46 Branch
defect

Tracking

(Not tracked)

UNCONFIRMED

People

(Reporter: jamie, Unassigned)

References

Details

(Whiteboard: triaged)

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.
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...
Priority: -- → P2
Whiteboard: triaged
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)
(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)
Duplicate of this bug: 1281258
Depends on: 1271663
Flags: needinfo?(amckay)
(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.
Component: WebExtensions: Untriaged → WebExtensions: General
See Also: → 1307852
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)
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)
Thanks Jamie!
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.
(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.
(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.
(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.
(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.
moved back to untriaged with no priority to re-evaluate based on comment 11
Priority: P2 → --
Whiteboard: triaged
Priority: -- → P5
Whiteboard: triaged
Product: Toolkit → WebExtensions
Duplicate of this bug: 1596889

The "Origin" header with the deterministic UUID is matter for the server. It is like a signature, telling the server how trustworthy the client is.

I am talking about the server is owned by the extension developers, not the 3rd-party server, the browser should provides a mechanism to help the the extension developers to protect their server.

Currently in Chrome:

  • if you install the extension from the Chrome Store, the UUID is deterministic, then the server will know the client is unmodified, is trust-able.
  • If you install the extension with the Developer Mode, the UUID is random generated, then the server will know the client maybe modified, is not trust-able.

For example, the client has a restriction that request frequency is 1/min, a cracker and change this restriction to 1/sec in the extension code, and run the extension in the Developer Mode. But the server know the the client modified by the Origin header, so it can reject this client. Now the cracker need to crack the browser as well, he need to modify the browser source and compile it.

Cracking the extension JavaScript code more easier then compile the browser source, it only need to unzip the extension archive file, beautify the JavaScript code, find the restriction configuration keyword and change it. And the browser Store also doesn't allow obfuscate the code, so the extension code is more easier to read and debugged.

The deterministic UUID won't protect the server completely from a cracker, but at least it can prevent the modified client from being distributed to general users by the cracker, because general users also require a cracked browser binary file.

Please consider fix this bug, or make this behavior opt-in for the users.

(In reply to muzuiget from comment #18)

the browser should provides a mechanism to help the the extension developers to protect their server.

The browser is a User Agent, it acts on behalf of the user, not for the server developer. If the user decides to spoof a request to 3rd party software (e.g. by installing an alternative frontend for it) then they should be able to.

You need to log in before you can comment on or make changes to this bug.