Closed purplesyringa closed 5 years ago
@HelloZeroNet @geekless @rllola @DATSEC @ValdikSS @filips123 @anthyg @anoadragon453 @github-zeronet
Waiting for review!
I've first predicted compatiblity issues, but, surprisingly, latest Firefox and Chrome in both Incognito and classic mode work well.
I've found a minor issue with pushState
/replaceState
: sites could pretend as other sites because http://127.0.0.1:43110/talk.zeronetwork.bit
and http://127.0.0.1:43110/me.zeronetwork.bit
are same-origin. This can, however, be fixed by overriding history.pushState
.
Nice presentation. Very hard to get your head around without something to kick the tires on. Hopefully it won't take much effort to get make the concept testable enough to validate it doesn't break apps or core ZN functionality like the side bar.
Does this impact the URL at all? Currently, the Angular app needs to set the base href so references work. Otherwise, they try to access content from the root. e.g., "/site.css".
In reality, it needs to do some parsing of the URL so you don't have to hard code the site address. There is a current issue right now with .bit domains that I haven't tested with an app because I don't have a .bit domain. I just know it is an issue today with relative referencing.
It's not clear if this prefix concept in the DOM can have an impact today. One thing that makes this challenging to predict up-front is you can't realistically know what all the third party libraries are doing in a Node-based app until you see an error. You have the core ones used to build a basic Angular, React or Vue app, then you have a bunch more you add for app functionality (such as charting). Later, we upgrade these, hoping they don't break.
What's the minimal you can do (time wise) to be able to have a branch where we can test the highest risk portions of the design, such as the new PREFIX (shadow DOM)? It doesn't have to do everything or provide new functionality. And, for testing, can still rely on NOSANDBOX, so long as it provides a way to test new scenarios. Just need to see validate it doesn't breaks apps.
What's the minimal you can do (time wise) to be able to have a branch where we can test the highest risk portions of the design, such as the new PREFIX (shadow DOM)?
If you're asking for a high-risk PoC (meaning unsafe, i.e. can be abused by sites), it'll probably be finished today (it's 9am for me). By now, ZeroTalk, static sites, ZeroSites and ZeroHello all mostly work (except localStorage stuff and such).
Ok, so, are there any sites using wrapperWebNotification
/ wrapperCloseWebNotification` at all yet? I have only one, but it's not released yet, and I don't know other people using these commands, so I don't think it makes sense to support them.
@imachug That commands should probably be available, but marked as deprecated and removed on next major version.
Also, how would you prevent CORS? Because all sites would be on same origin, any site would be able to access all data from other sites even without permission.
I think that this would be good improvement. This would also allow Progressive Web Apps with Web Manifests and Service Workers available.
Can you share your unsafe PoC when it is available? I would like to test it.
That commands should probably be available, but marked as deprecated and removed on next major version.
Sure, but innerLoaded
, wrapperPushState
and wrapperReplaceState
are still useful, so we can probably leave them (or remove in two major versions or something)
@HelloZeroNet
Also, how would you prevent CORS? Because all sites would be on same origin, any site would be able to access all data from other sites even without permission.
We'd compare Origin
/Referer
to the request URL, and if it doesn't match, we'd use X-Frame-Options
and Content-Security-Policy
for iframes and just 403 Forbidden
for data.
Can you share your unsafe PoC when it is available? I would like to test it.
Sure, ping me tomorrow if I forget to send you a link.
I'm currently a bit stuck with site-in-site (aka picture-in-picture) mode implementation. Good old 3 years old StackOverflow thread without an answer. Please tell me if you know how to fix that issue.
Unfortunately I couldn't find a way to fix that issue, so I'm switching to another architecture:
┌─────────────────────────────────────────────────────┐
│ HTML PAGE │
│ Secret (wrapper key [site]) │
│ ┌─────────────────────────────────────────────────┐ │
│ │ PREFIX (shadow DOM) │ │
│ │ Practically invisible to site content │ │
│ │ ┌─────────────────────────────────────────────┐ │ │
│ │ │ SITE NOTIFICATIONS (<div>) │ │ │
│ │ │ Unsafe content (possible XSS) │ │ │
│ │ └─────────────────────────────────────────────┘ │ │
│ │ ╔═════════════════════════════════════════════╗ │ │
│ │ ║ WRAPPER NOTIFICATIONS (<iframe>) ║ │ │
│ │ ║ Safe content (no leaking or spoofing) ║ │ │
│ │ ╚═════════════════════════════════════════════╝ │ │
│ │ ╔═════════════════════════════════════════════╗ │ │
│ │ ║ SIDEBAR (iframe) ║ │ │
│ │ ║ Site and key management ║ │ │
│ │ ║ Secret (wrapper key [ADMIN]) ║ │ │
│ │ ║ ┌─────────────────────────────────────────┐ ║ │ │
│ │ ║ │ WS UPLINK │ ║ │ │
│ │ ║ │ UiWebsocket API (ADMIN) │ ║ │ │
│ │ ║ └─────────────────────────────────────────┘ ║ │ │
│ │ ╚═════════════════════════════════════════════╝ │ │
│ │ ┌─────────────────────────────────────────────┐ │ │
│ │ │ WS UPLINK │ │ │
│ │ │ UiWebsocket API (site) │ │ │
│ │ └─────────────────────────────────────────────┘ │ │
│ └─────────────────────────────────────────────────┘ │
│ ┌─────────────────────────────────────────────────┐ │
│ │ SITE DATA │ │
│ │ Unsafe content (managed by site code) │ │
│ └─────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────┘
Basically, we're getting rid of iframe gate and using wrapper key instead. This architecture is still secure, but is more error-prone, so I should be careful enough.
What is different between site and wrapper notifications? Could both of them use DIV or IFRAME or even be in same element?
Site notifications are possibly insecure and don't store sensitive information; wrapper notifications are always secure and may store sensitive information. For example, Database was rebuilt
notification is a site notification, while Enter private key: [...]
(you get it when you press Sign & publish
) is a wrapper notification.
It's unsafe to use div
for wrapper notifications. But it's possible to use iframe
for site notifications, though that's overcomplicated and slow.
@filips123 Forgot to ping you
I've noticed that a lot of wrapper code uses jQuery. We probably don't want to pollute sites' environment, so I'm trying to port as much code as possible to Vanilla JS.
Making sidebar work turned out to be a bit more difficult than expected, so you'll have to wait a bit.
Looking good, but the history access could be problematic and probably there is multiple ways to recover the original function, eg. using an iframe:
history.pushState = console.log
ƒ log() { [native code] }
$("<iframe id='itemp'>hello</iframe>").appendTo(document.body)[0].contentWindow.history.pushState
ƒ pushState() { [native code] }
Sure; but what would you do after getting pushState
? pushState "", "", "http://facebook.com"
because http://facebook.com
and about:blank
are not same-origin.
@imachug Maybe changing URL to address of another ZeroNet site. All sites would be on the same origin but I don't know if HTTP CORS headers actually prevent this.
@filips123 Eh, that's why we're replacing pushState
. If you're referring to nofish'es iframe PoC, it won't work because http://127.0.0.1:43110
and about:blank
aren't same-origin.
@imachug Ok, this makes sense. And you probably can't open http://127.0.0.1:43110
in iframe, right?
Also, would it be possible to differentiate between legitimate and malicious iframes? Because site can use iframe to just display other content on it (embedded game, sidebar... hosted on it) or to attack different site.
Ok, this makes sense. And you probably can't open http://127.0.0.1:43110 in iframe, right?
Well, it's possible (site-in-site case), but it's restricted, so you can't call pushState
anyhow.
Also, would it be possible to differentiate between legitimate and malicious iframes? Because site can use iframe to just display other content on it (embedded game, sidebar... hosted on it) or to attack different site.
It's impossible to attack a site with an iframe because there's a sandbox. The only way of communication is postMessage
. I'm still thinking about issues, but I don't see any yet.
Sure; but what would you do after getting
pushState
?pushState "", "", "http://facebook.com"
becausehttp://facebook.com
andabout:blank
are not same-origin.
You are right, but it works this way:
> history.replaceState = "nope"
> history.replaceState = $("<iframe id='itemp'>hello</iframe>").appendTo(document.body)[0].contentWindow.history.replaceState
> history.replaceState("", "", "/AnyUrl")
> window.location.href
"http://127.0.0.1:43110/AnyUrl"
What browser are you using? That sounds like a major issue
Chrome, but just tested and also works in FF
Do you have any ideas how to solve this? Switching to site_address.zero
or a similar way would help here (and solve other problems too), but that won't work well with proxies and is also a major change.
Not really, maybe we could setup a setInterval that monitors the window.top.location, but it's pretty hackish
@HelloZeroNet What if the site would somehow delete/replace setInterval
?
@filips123 The prefix code is run before site code, so we can save setInterval beforehand.
@imachug Can site code access prefix's intervals? If so, the site could maybe guess (or brute-force) all interval IDs and clear (clearInterval
) them which would disable security check.
What's the minimal you can do (time wise) to be able to have a branch where we can test the highest risk portions of the design, such as the new PREFIX (shadow DOM)?
If you're asking for a high-risk PoC (meaning unsafe, i.e. can be abused by sites), it'll probably be finished today (it's 9am for me). By now, ZeroTalk, static sites, ZeroSites and ZeroHello all mostly work (except localStorage stuff and such).
One of the most useful steps I've learned to do in projects early is test high risks. A high risk is any threat to the success of the project. We begin by listing all risks, rating them High, Medium, Low, then focusing on mitigating High risks in the next step. Your goal is to at least lower to Medium.
Typically, a risk is high because there are unknowns. You may be including a new third party library, and don't know if this library will work because you've never used it before.
To mitigate, you test it to verify assumptions, bringing the risk down to Med, Low or None, because you can at least verify that the library meets core assumptions of functionality.
Your goal is to eliminate all high level risk with minimal effort ASAP, before spending a lot of time on the project. What you don't want to do is spend 6 weeks developing something only to run into a show stopper or reason others can't use your project, when you could of identified it up-front with a little bit of test code.
Obviously, if you have major security concerns, they could be a high level risks, too, because they can doom the project if you can't find a resolution. But, in the case of what you're testing, a high level risk is that the shadow DOM breaks the types of applications you are trying to enable with this project.
So, you'd want to create a test, as easy and minimal as possible, that allows us to then test the Angular 8 app I created. That is why this test can still run it under NOSANDBOX, because this isn't production code. Its purpose is only to prove that the shadow DOM isn't a show stopper.
The code can be throw-away solely for the purpose of the test (testing a 3rd party library), or it can be code ultimately used in the project.
TOTALLY OFFTOPIC:
@HelloZeroNet Can you check out this thread in UNLIMIT TALK, please? (not sure how best to reach you).
Can you clone ZT Talk into a new "ZeroNet Development" ZT with high user limits like UNLIMIT TALK, or give me or imachug the your blessing to do so?
I've noticed that a lot of wrapper code uses jQuery. We probably don't want to pollute sites' environment, so I'm trying to port as much code as possible to Vanilla JS.
I can't speak to React or Vue, but I can say that
In order to get Angular working, I have to include:
<script>document.write('<base href="' + document.location + '" />');</script>
which effectively becomes the site's base (which works if you could hard code the site address, which isn't practical):
<base href="/1Gtzk5w72SmSx7GW6Y1JaGLgPfkXH2Wanz/">
or, if I inspect it:
<base href="http://127.0.0.1:43110/1Gtzk5w72SmSx7GW6Y1JaGLgPfkXH2Wanz/?wrapper_nonce=a23982da7c19577d48c588e311f256b5a272db7bd0dc13212555479a0e5594f0">
I don't know much about locking down accessing outside this because until ZN, I've always assumed the browser is insecure and relied 100% on server-side for security, tokenizing the UI with cookies and AJAX/WebSockets. Obviously, ZN is unique in this respect.
I'm just wondering if the "base href" can play a role in helping you lock down a site. You probably already dismissed it with good reason. :)
@github-zeronet I'm about to finish sidebar, I'll give you a PoC soon.
Can site code access prefix's intervals? If so, the site could maybe guess (or brute-force) all interval IDs and clear (clearInterval) them which would disable security check.
Due to how browsers implement setInterval (notice: browsers, not spec), site code can disable our intervals, and I don't think there's a good solution. I could have fun handling iframe
creation and replacing pushState
there, but it looks too fragile. Maybe we should just say that changing URL to another site isn't a trouble (and you can use the sidebar if you want to make sure you're browsing the correct site).
Maybe we should just say that changing URL to another site isn't a trouble
But it probably it. Site could pretend to be another site and trick user into entering sensitive information.
Uh, that's why I said "and you can use the sidebar if you want to make sure you're browsing the correct site". I agree that it's not the best solution, but that's the best thing we can do without big changes to ZeroNet.
If we allow big changes, however... The best thing to do would be to utilize browsers' same-origin policy, so each site would have it's own origin. This is possible to do with a local DNS server -- we'd proxy *.bit
and *.zero
to 127.0.0.1
and delegate all other requests to the default DNS server. This would solve, uh, all problems we're having. The only issue here is that we'll have to make the user change proxy settings.
@HelloZeroNet If we're having problems with such a simple thing as History API, we'll probably have more problems in the future. Thus, I see no reason to continue developing this issue without switching to better domain names like http://talk.zeronetwork.bit
and http://1talkfrmwvbnsoof4iokay9euxtbtjipt.zero
instead of http;//127.0.0.1:43110/talk.zeronetwork.bit
and http;//127.0.0.1:43110/1TaLkFrMwvbNsooF4ioKAY9EuxTBTjipT
. Please notice that this change won't break proxies -- we'd use https://talk.zeronetwork.bit.zn.amorgan.xyz
and https://1talkfrmwvbnsoof4iokay9euxtbtjipt.zn.amorgan.xyz
.
Pinging people from related issues:
Choosing what hostname format to use has become more important than before. We should switch as soon as possible.
For now, we have to deal with case-insensitive hostnames -- that breaks Bitcoin addresses. Possible solutions:
When announcing sites, we'd announce lowercase addresses instead. When checking signatures, we'd compare recovered_address.lower()
to real (lowercase) site address.
Pros:
Cons:
1
and L
are different, while their lowercase counterparts 1
and l
look similar;Pros:
Cons:
I'd choose the first solution, but I'm open to other ideas.
We can have both: bech32 for new sites, lower case for old sites.
Ok, are you fine with me making a PR for this?
My main problem is with this solution is I (and probably many others) would not trust any application to modify the proxy settings of the browser. Other problem is it would make the deployment of the client harder especially if the remote machine does not have domain names configured (eg. on LAN network)
@HelloZeroNet Od just use both (along with otherbl cryptographies) and let user decide what they want to use.
Discussion should be moved to #2087 which is relevant issue for this problem. So, domains should end with TLD .zeronet
(but still allow access via 127.0.0.1
and proxies should be accessible via doman.bir.proxy.com
. But it should be still available to access sites normal way.
but still allow access via 127.0.0.1
Do you understand that it breaks the goal of same-origin policy? That's the reason why I asked to speed up domain naming scheme standartization.
My main problem is with this solution is I (and probably many others) would not trust any application to modify the proxy settings of the browser.
Seriously, ZeroNet core can do more harm than changing proxy, so there's no reason to worry about proxy settings.
Other problem is it would make the deployment of the client harder especially if the remote machine does not have domain names configured (eg. on LAN network)
We could set 192.168.12.34:53
as DNS server then. Another solution is to use ports for this (ip:43110
for site 1, ip:43111
for site 2, ip:43112
for site 3, etc.). But yeah, using IPs is troublesome with this solution.
Do you know a better solution?
Do you understand that it breaks the goal of same-origin policy?
Code could serve old/current version of wrapper when accessing from same-origin.
Also, you can simply use Proxy PAC file to set 127.0.0.1
as proxy for all .zeronet
domains. For start, ZeroNet needs to be modified to allow that domains and properly handle them. This should not be so hard as some part for this is already in ZeroNet but it doesn't work properly.
The next steps would be to redesign wrapper code and create more complete ways to handle URL rewrites (DNS server, proxies, extensions...).
I think this unnecessarily increases code size, and also makes site support troublesome -- you'd have to make sure that your sites work in both (rather different) cases.
imachug, how could you change DNS server just for ZN without impacting other tabs, sites, browsers, app, OS? I'd at least try to limit scope of this to the current running browser.
I can see how that would solve this problem if every site had a unique host. I'm just not aware of ability to scope DNS to a tab, window or the browser. I'd love to know if there is a way.
How do subdomains play a role in all this? Because it would be easier to minimize DNS issues if
127.0.0.1/
redirects to
@github-zeronet Yes, configuring DNS and rewriting at OS lever could be a difficult problem. That's why I think that we should have multiple options for users: legacy 127.0.0.1
, Proxy PAC file, browser extension, OS rewrites, registry changes, network changes... But first feature should be to provide possibility to use ZeroNet with Proxy PAC which is simplest way for now.
imachug, how could you change DNS server just for ZN without impacting other tabs, sites, browsers, app, OS? I'd at least try to limit scope of this to the current running browser.
Yes, configuring DNS and rewriting at OS lever could be a difficult problem.
Uh, what? We could just set up a local DNS server that'd delegate clearnet requests to a real DNS, e.g. 8.8.8.8 or whatever the user chooses.
How do subdomains play a role in all this? Because it would be easier to minimize DNS issues if
.zeronet.xyz solved all your problems, because then you would wildcard that to 127.0.0.1.
Using subdomains was the first thing I considered, but Linux & MacOS (not sure about Windows though) don't understand wildcards in /etc/hosts.
The challenge here is that it needs to happen in browser, so rules out URL rewriting like what Apache does, unless you get into redirects.
But why? Configuring a DNS server (which will be automatic) is easier than setting up extensions or something.
Some of us use very complex DNS setups, and doing at OS level, among other things, prevents a user from running their own DNS or doing special configurations (e.g., other software that also extends DNS).
Using several DNS servers and delegating requests is used rather often, and I don't see how that would be a problem here.
Are you using a new wrapper template and tying that to a new site permission to provide backward comparability?
Nope, the new prefix way must be the default one and should be the only one.
Abstract
Currently ZeroNet uses a wrapper for sidebar and notifications UI, and embeds site content with an iframe. This proposal fixes some issues caused by iframing by embedding ZeroNet UI into site content.
Rationale
2165, #2057 - All features have to be marked unsafe by default, so supporting new ones is troublesome;
2154 - Most people don't understand what NOSANDBOX means;
2086 - Some interactive content in iframes might get blocked;
2058 - Pull-to-refresh is working weird with iframes;
2003 - "Opening as child window" is sometimes impossible to bypass;
1736 - Opening link in a new tab is more difficult than required;
1707, #565 - Embedding a site into a site is unsafe with an iframe;
1667 - Browser header style can only be set by root frame;
1403 - Scrolling is impossible, iframe is most likely the reason;
1399, #1054, #29 - localStorage, IndexedDB, etc. are not available in sandboxed iframes;
1237 - Fullscreen API doesn't work;
1236, #1262 - Some Vue plugins don't work;
1010 - Creating Web Workers requires data: URI usage;
469 - History management requires ZeroFrame usage.
Overview
Current architecture:
Safety layers are separated with double border.
No software has zero bugs. This includes security issues. There are many possible attack points here:
Proposed architecture;
Sure, it might look more difficult at the first glance, but security comes with a cost.
Resulting HTML
The resulting HTML (the one that browser receives) consists of a prefix and the real site
.html
file.Prefix
Prefix is a "magic" HTML code that sets up an analogue of what was a wrapper by creating a shadow DOM node. This ensures that the sidebar and notifications are shown correctly, independent of main site styles, and that it doesn't affect the site itself.
Gate
A gate is an iframe that acts like a gate between UiWebsocket and its user. The gate handler uses the referrer to check what permissions the websocket must have.
Fixed issues / added features
2165, #2057 - All features are now safe;
2154 - No need for NOSANDBOX anymore;
2086 - The only interactive content is simple notifications (i.e. buttons);
2058 - Pull-to-refresh can now be controlled by the site;
2003 - No need to be top frame anymore;
1736 - Opening in a new tab made easy;
1707, #565 - Embedding made possible (requires a separate proposal);
1667 - Controlled by the site now;
1403 - Scrolling is fixed;
1399, #1054, #29 - localStorage, IndexedDB, etc. made possible (requires a separate proposal);
1237 - Fullscreen API without ZeroFrame;
1236, #1262 - SecurityError's fixed;
1010 - Web Workers by path should work now;
469 - History API without ZeroFrame.
Implementation status
ETA: at most 1 week if no major issues are found