Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

FYI: How to connect to a TCP server from an arbitrary Web page in the browser #25

Open
guest271314 opened this issue Aug 26, 2023 · 7 comments

Comments

@guest271314
Copy link

https://gist.github.com/guest271314/ac9e26f71f5b299baa1b4663002ba9e6

Rationale:

  • A window cannot really be completely isolated on the Web/in the browser
  • Marrying API's (Isoalted Web Apps-Direct Sockets) is hazardous in that users could see the IWA as only a means to an end to use Direct SOckets and thereby break the IWA in order to use Direct SOckets how they see fit
  • Options should be provided to user for the user to decide which origins the IWA can communicate with using opener and postMessage()
@cmfcmf
Copy link
Contributor

cmfcmf commented Aug 28, 2023

I think the key here is that for this to work, the IWA must be willing to expose its capabilities to arbitrary web pages via WebRTC. A 'normal' IWA, which doesn't contain such code, would not allow you to access sockets from arbitrary web pages.
You are right in that there are some ways of how an IWA could allow other websites to access its powerful capabilities: E.g., an IWA could probably also use sockets to start a local http server, which arbitrary web pages could then connect to, and thus would allow to expose any IWA functionality to arbitrary web pages.

Similarly, if you go to this amount of effort, you could also always have a server as a proxy between a website with lower capabilities to another website with higher capabilities. In this way, you could also effectively use secure-context-only or IWA-only APIs from an http page. The fact that this is possible doesn't convince me that secure defaults are unnecessary.

@guest271314
Copy link
Author

The points are

  1. There is no way to truly isolate a window;
  2. The end-user ultimately decides how a feature is exposed and used, not absentee authors of proposals, specifications or implementers
  3. Given 1 and 2 the proposal authors and implementers need to provide a means for the end-user to set which origins to expose the feature on, instead of trying to prevent access by end-users, which is not practically possible.

For me IWA is just a means to an end to use Direct Sockets. I understand that you folks really thingk IWA is important in and of itself, however, that's not my interest. So whatever deterents uou might try to come up with to gate Direct Sockets, I am probably just as motivated to break out of those boxes to get the to Direct Sockets you have decided to marry to IWA.

Since IWA is kind of emulating an extension, we can supply the same mechanism to at least expose opener on the IWA so we can make use of transferable streams between the IWA and the origin we set. So inclusing something like "externally_connectable" or its Web API equaivalent should suffice to allow end-users to embed the IWA inan iframe or open an IWA window where parent and opener are set to the Web page hat opened the IWA.

You can't really hide behind anything here as to whether or not your prospective isolation model works or not in certain cases. I'm relatively certain I will find a way to get into or get out of the IWA to do what I want with Direct Sockets for my own use cases. So it would be prudent to just expose a direct means to do so to avoid all of the ways you try to isolate being nullified in the field. Remember chrome.sockets.tcp was exposed in a basic manifest.json.

@guest271314
Copy link
Author

So why am I interested in Direct Sockets?

I'm basically trying to implement Native Messaging with Streams in order to compare the efficacy and efficiency of Streams to JSON over IPC, see https://bugs.chromium.org/p/chromium/issues/detail?id=1214621.

@cmfcmf
Copy link
Contributor

cmfcmf commented Aug 28, 2023

If I understand your logic correctly, you'd also argue that all extension APIs should be available to all websites without the need for an extension, because you can communicate with extensions and thus use their APIs from arbitrary web pages. Is my understanding correct?

@guest271314
Copy link
Author

I'm saying its futile to try to gate capabilities behind gates - without exposing options for the user to expose said capabilities.

Your security model will just wind up being exposedas broken over and over again.

Just write out an option for users to set which site the IWA or whatever is exposed on.

I think the real problem is specification authors don't trust users and think they know best.

@cmfcmf
Copy link
Contributor

cmfcmf commented Aug 28, 2023

I'm not really sure how to move forward here; your tone makes it tough for me to continue responding. An all-encompassing allegation such as "specification authors don't trust users and think they know best" feels like it might not align with Google's Community Guidelines for its Open Source projects. I am sure that many authors of Web Standards, specifications, and proposals, be it at Google, Mozilla, Apple, Microsoft, or at other companies, care deeply about their users.
Again, it is okay and encouraged to respectfully discuss these proposals, but your tone makes me think that a constructive discussion with you might not be possible.

@guest271314
Copy link
Author

A developer

  1. Fetches this repository;
  2. Builds the Isolated Web App (IWA) telnet-client demo locally. Requires downloading Node.js and packages to build the IWA, Signed Web Bundle;
  3. Writes in the script that launches Chrome or Chromium or at command line includes the flag;
# --install-isolated-web-app-from-file="/home/user/telnet-client/dist/telnet.swbn" 
  1. Asks the principal champions of IWA hence Direct Sockets multiple times to provide a means to communicate with the IWA via messaging, e.g., exposing opener; allowing an iframe to postMessage() to parent, the maintainer reply Create an example without depending on TypeScript, Webpack, Node.js, npm #12 (comment)

What you are intending to do is intentionally unsupported.

What is a developer in the field supposed to think or do about that reply?

That proposals authors are genuinely considering the formal feature request?

Abandon their use case, and that's it, because a proposal author said my individual use case is unsupported?

I don't think the maintainers of IWA would so quickly give up on their own proposal, and continuing to update their proposal and implentation after working to get to that point.

After reproducing the steps to create an IWA to use Direct Sockets, locally, I think the developer is aware of the powerful capabilities. Some deference needs to be given to the formal request to expose a means to communicate with the local Signed Web Bundle that they wrote and are launching locally, as they are disclosing to the maintainer that part of isolation they would prefer to control locally.

I do not come without proposals myself for how to achieve the feature request.

A. Perhaps something like Web Audio API AudioWorklet defines, a MessagePort port that can exchange data with the context which created the AudioWorkletNode, currently exposed in the AudioWorkletProcessor constructor, the last I checked exposed in AudioWokletGlobalScope PR was merged.

B. In Chrome extensions domain we have in the Native Messaging manifest json stored in NativeMessagingHosts folder in Chrome and Chromium configuration folder on the system

{
  "name": "capture_system_audio",
  "description": "Capture system audio",
  "path": "/path/to/capture_system_audio.js",
  "type": "stdio",
  "allowed_origins": [
     "chrome-extension://<ID>/"
  ]
}

C. In Chrome extensions domain we also have the Manifest Version 3, where we have content scripts setting origins to run the content script in an "ISOLATED" "world" by default or "MAIN" "world", e.g.,

{
 "name": "no-so-banner",
 "version": "1.0",
 "manifest_version": 3,
 "content_scripts": [
   {
     "matches": [
       "https://*.stackoverflow.com/*",
       "https://*.stackexchange.com/*",
       "https://*.superuser.com/*",
       "https://*.askubuntu.com/*"
     ],
     "js": ["no-so-banner.js"],
     "world"
   }
 ]

Content script can also be dynamically registered.

D. In Chrome extension domain we have "externally_connectable", which provides a means to communicate from a Web page to an extension, e.g., https://github.com/guest271314/telnet-client/blob/user-defined-tcpsocket-controller/direct-sockets/manifest.json

{
    "name": "direct-sockets",
    "short_name": "direct_sockets",
    "version": "1.0",
    "manifest_version": 3,
    "background": {
      "service_worker": "background.js"
    },
    "permissions": [
      "nativeMessaging", 
      "tabs", 
      "activeTab", 
      "windows", 
      "scripting"
    ],
    "host_permissions": ["<all_urls>", "file:///*"],
    "externally_connectable": {
      "matches": [
        "isolated-app://<ID>/*"
      ],
      "ids": [
        "*"
      ]
    },
    "action": {},
    "homepage_url": "https://github.dev/guest271314/telnet-client/tree/user-defined-tcpsocket-controller"
  }

used in background.js

chrome.runtime.onMessageExternal.addListener(async (message, sender, sendResponse) => {
  const sdp = await promise;
  sendResponse(sdp);
});

chrome.runtime.onConnectExternal.addListener(async (port) => {
  if (port.name === 'IWA') {
    port.onMessage.addListener((sdp) => {
      nativeMessagingPort = chrome.runtime.connectNative('nm_tjs');
      nativeMessagingPort.onMessage.addListener((message) => {
        port.postMessage(message);
        resolve(sdp);
      });
      nativeMessagingPort.onDisconnect.addListener((nativePort) => {
        if (chrome.runtime.lastError) {
          console.log(chrome.runtime.lastError);
        }
        console.log(nativePort.name, 'Disconnected');
      });
      nativeMessagingPort.postMessage(null);
    });
    port.onDisconnect.addListener(() => {
      nativeMessagingPort.disconnect();
    });
  }
});

from the Web site

chrome.runtime.sendMessage('<ID>', null, {}, async (message) => {
  var text = atob(message);
  local.setRemoteDescription({
    type: 'answer',
    sdp: text
  });
});

E. From my perspective, the simplest that is actionable by the maintainers here is to provide a means for the user to set which origins opener and parent are set on when the IWA is launched as a standalone window or in an iframe on the usrs own machine, pointing to an .swbn file on the users' own machine. That can be done in the webmanifest.json file, and headers if necessary. That will provide a means to use postMessage(), transfer the readable side of a TransformStream from the Web page the developer deliberately set in the files they write locally.

I do not have rancor, nor malice towards specification authors. Sometimes I think they take constructive feedback as an attack on their person or profession. It's not. It's just constructive feedback.

I build your proposal. Went through all the steps. I'm providing feedback.

I would think you want direct feedback from the field, rather than exclusively in-house amongst the proposal authors and implementers, for practical purposes; Kurt Godel demonstrated mathemtically impossible to prove if an axiom in a system is true or not from within the system. It's not personal. It's business.

I'm doing my job as a developer in the wild providing constructive feedback. I'm not the only one who has done this re Direct Sockets. The Direct Sockets repository issues on GitHub shows that. The goal posts have moved multiple times; from Progressive Web App to Isolated Web App and Signed Web Bundles. Rather quickly. I'll give you that.

I think it's fair for the goal posts to move somewhat in the directon of developers saying, "Hey, I'm just trying to test your gear. Alright I tested your gear. This and that can be improved in this and that way". Just saying "Well, we intentionally don't support that" is like, really? After I've jumped through all the hoops you've kept moving?

If I'm on my own machine, writing my own code, deciding which origins I want to use Direct Sockets on locally, while on the Web, the end-user should have an official way to do that.

So, I'm trying to fathom a rational reason why the end-user, on their own machine, deciding to deliberately use Direct Sockets, by necessity of the circumstance, via an IWA, would be intetionally not support?

In the mean time I created several workarounds to achieve the use case. Using browser extension code, and using only Web API's exposed on a Web page in Chrome. Nothing nefarious happened. The Internet didn't break. There is no security issue.

Again, I would think proposal authors and implementers would prefer to have constructive feedback from the field, outside of the inner-circle of people who have depolyed this to the world at large.

Thank you for your time and efforts in this matter. And thank for working on this. Kindly consider what I wrote above here. It's not an "attack" on you. It's feedback from testing your gear and vetting the claims of "security" concerns.

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

No branches or pull requests

2 participants