HelloZeroNet / ZeroNet

ZeroNet - Decentralized websites using Bitcoin crypto and BitTorrent network
https://zeronet.io
Other
18.38k stars 2.27k forks source link

Allow zite loading outside iframe (for zite owners) #1262

Open antilibrary opened 6 years ago

antilibrary commented 6 years ago

Would it be possible to add a flag that would allow a zite to be loaded directly instead of loading it inside an iframe? The problem is that for tools like vuejs-devtools to work the vue.js script must be present in the page itself. If the script is inside the iframe the tool won't detect the script. I could use that tool by loading the file directly on file:// but then the zeronet api will not work. Maybe the flag can be set only if the person is the site owner.

Thanks

HelloZeroNet commented 6 years ago

I got the barrier when tried to get grapejs work. I will check what's the best way to remove the limitations of sandboxed iframe.

Snarkly commented 6 years ago

Thanks for trying to get grapejs to work on Zeronet. I hope to someday be able to create a nice site on Zeronet without needing to code anything.

HelloZeroNet commented 6 years ago

Possible security problems of removing sandboxing (by default):

Accessing global variables of the frame: wrapper_key, websocket connection

Solution: delete from global context before injecting the iframe

localStorage / cookie access

Solution: delete localStorage then replace it with a object that only allow access to site's storage

Change url by replace/push state

Solution: Replace it with an object that only allow to change to url within the site's address

No hidden DOM: Read entered private key from sidebar/notification

Solution: Put the sidebar to sandboxed iframe?

Other possible security issues?

...

purplesyringa commented 6 years ago

delete localStorage will only delete window.localStorage. It is possible that localStorage object can be accessed another way, so delete localStorage won't help.

HelloZeroNet commented 6 years ago

@imachug I have not found other way to access localStorage, so proof-of-concept:

class RestrictedLocalStorage {
    constructor (ls) {
        Object.assign(this, {
            setItem(key, val) {
                console.log("set " + key + " = " + val)
                return ls.setItem(key, val)
            }
        })

        Object.assign(this, {
            getItem(key) {
                console.log("get " + key)
                return ls.getItem(key)
            }
        })
    }
}

restricted_localstorage_handler = {
    get (target, key) {
        console.log("proxy get", key)
        if (key == "getItem" || key == "setItem") {
            return target[key]
        }
        return target.getItem(key)
    },
    set (target, key, val) {
        console.log("proxy set", key, val)
        return target.setItem(key, val)
    }
}

localStorage.setItem("test", "hello")
console.log("test:", localStorage.getItem("test"))

rs_ls = new RestrictedLocalStorage(window.localStorage)
rs_ls_proxy = new Proxy(rs_ls, restricted_localstorage_handler)
delete window.localStorage
window.localStorage = rs_ls_proxy

console.log("ls object:", localStorage.ls)

console.log("test:", localStorage.getItem("test"))

localStorage.setItem("test2", "hello2")
console.log("test2:", localStorage.getItem("test2"))

localStorage["test3"] = "hello3"
console.log("array-like test3:", localStorage["test3"])

via Managing the private data of ES6 classes

purplesyringa commented 6 years ago

Do you know if localStorage.set/getItem is read-only? If it isn't, it would be more secure to use localStorage.set/getItem = ... instead.

HelloZeroNet commented 6 years ago

Yes it's possible (I have only tested all of this in Chrome yet), but it's also possible to use localStorage["anything"] = "other" which I have not found a way to override

I have updated the poc code with array-like object access support using a Proxy (not sure if it's possible without it)

purplesyringa commented 6 years ago

Looks interesting. Though I think some other ops like clear should be allowed, but otherwise it's ready.

HelloZeroNet commented 6 years ago

Unfortunately I found a way to recover the original localstorage object by creating an sub-iframe. So probably we can't protect cross-site localstorage access.

HelloZeroNet commented 6 years ago

I have added a new NOSANDBOX permission in Rev3335 that should remove the sandboxed iframe restrictions.

As for example I created a demo for grapejs online html editor that is using iframes, so it did not works under sandboxed iframe restrictions: http://127.0.0.1:43110/1Grapes1bMdkcoM8CtjboJtPS1kvbtqMUj/editor.html

tangdou1 commented 6 years ago

How can I use this NOSANDBOX permission. There is no sign in the content.json. Thanks!

ghost commented 6 years ago

I believe your zite has to ask for the permission to be added, then the user must accept that permission. I don't know the specific command to call to ask for the permission, but it's either in the ZeroFrame API Reference or will be.

HelloZeroNet commented 6 years ago

You have to use the wrapperPermissionAdd command:

    page.cmd("wrapperPermissionAdd", "NOSANDBOX", function(res) {
        if (!res.error) page.cmd("wrapperReload")
    })

https://zeronet.readthedocs.io/en/latest/site_development/zeroframe_api_reference/#wrapperpermissionadd-permission

antilibrary commented 6 years ago

I'm not sure if I understand how to use this. I've added to the zite and I grant the permission but when the page reloads the zite is still inside an iframe. I'm on rev 3354. The same happens with the zite you provided as example. Shouldn't it load the zite html directly instead of using the iframe? Thanks

HelloZeroNet commented 6 years ago

The site will be in the iframe, but with the "allow-same-origin" permission which is disables the sandboxing restrictions. Completely removing the iframe would be also possible, but then reaching the zeronet api would not be possible as it handled by the wrapper. So I recommend to expose the required variable using window.top after you granted the NOSANDBOX permission.

0polar commented 5 years ago

Completely removing the iframe will much easier for prerendering.

github-zeronet commented 5 years ago

Thanks, @HelloZeroNet , Angular apps can now load (only tested static so far, no AJAX/WebSockets or use of DB).

Can you list the security concerns for NOSANDBOX, and why each visitor to the site has to Grant the permission after being prompted with a message suggesting it is "Dangerous!"? If you produce sample code for testing, can you please share that as well to help us test?

I saw a few concerns in this thread, but that was before you introduced this solution, so it isn't clear if they are still concerns.

I figured out what this permission does before finding your description here, sadly. lol At least I had fun learning.

purplesyringa commented 5 years ago

Can you list the security concerns for NOSANDBOX, and why each visitor to the site has to Grant the permission after being prompted with a message suggesting it is "Dangerous!"? If you produce sample code for testing, can you please share that as well to help us test?

PoC:

// Get NOSANDBOX permission, then:

window.parent.zeroframe.cmd("configSet", ["open_browser", "cmd && rem %s"]);
window.parent.zeroframe.cmd("serverShutdown", [true]);

Wow, you now have a shell in ZeroNet console, which means that an attacker can possibly run any code on your computer, e.g. still keys or setup a cryptolocker.

github-zeronet commented 5 years ago

TYVM, imachug! I really appreciate your help here.

I'm going to look deeper into this for possible solutions. Please feel free to share ideas you've looked at and thoughts on why they could or could not work.

The goal here is the ability to securely run common front-ends like Angular, Vue and React to help build the ZN community by attracting the best site developers with the ability to port investment they have made in both code and skills.

purplesyringa commented 5 years ago

I do have some ideas. Instead of embedding site content into an iframe, you can insert an iframe into your site. @HelloZeroNet I can implement this (I have done that for my Alt-ZeroNet implementation already), but I wanted to check whether you're fine with it.

github-zeronet commented 5 years ago

Do you have the latest py3 in Alt-ZeroNet? I'd like to test it.

Hopefully we can develop a security regression test suite at some point with this. For now, that just means coming up with tests and scenarios.

purplesyringa commented 5 years ago

Do you have the latest py3 in Alt-ZeroNet? I'd like to test it.

No of course. Alt-ZeroNet is a completely different implementation. It uses NodeJS, not Python. It's also Work-In-Progress. I'm planning on speeding up development in September. There's no alpha/beta version yet.

What I was talking about is that I have implemented a part of ZeroNet already; more specifically, the ZeroFrame gate, and I'm currently porting it to the official implementation.

github-zeronet commented 5 years ago

...you can insert an iframe into your site.

If the issue is exposure of commands reserved for the wrapper, how do you secure it if the IFrame is inside the user's pages? Seems either way, need to reduce commands that can be executed if a site has NOSANDBOX. No reason such a site needs "serverShutdown" or "configSet", for instance.

If we go this route, can we leave NOSANDBOX as is and create a new permission, because NOSANDBOX is useful for quick diagnosis. Once a zite dev determines that resolves their immediate issues, they can then compare a permission that restricts server-side commands, but doesn't require the site user to be prompted, to determine if the restrictions create new issues.

There could be a lot I don't understand, yet. I'm about to review the request handling code.

purplesyringa commented 5 years ago

@github-zeronet

Uh... so there are commands that can be executed by the site and commands that can be executed by the wrapper. Everything that can be used by the site can also be used by the wrapper, but not vice versa. Take a look at ZeroTalk. It can read files, write files, sign & publish content, choose certificate provider, and... that's it. The wrapper (more specifically, the sidebar) need to do more stuff: change site size limit, list peer location, reload & rebuild database, pause, update and delete the site, and a few more things. Enabling NOSANDBOX means allowing the site execute those commands, so the site could set its size limit to 1 TB and fill up your drive.

A lot better idea would be to make it such that there's no reason to use NOSANDBOX at all -- i.e. moving the content out of iframe. My idea is to make a so-called iframe gate. Here's a small overview on it:

Notice that Referer [sic] is used here to check what site is using the websocket. However, Referer header is not set for websockets, that's why we're using iframe.

github-zeronet commented 5 years ago

That's a very well thought out design. What would be the ETA before we could test it?

HelloZeroNet commented 5 years ago

Yeah it's an interesting design, but not sure if this way it's possible to do permission requests, the sidebar and you won't be able to do cookie/localstorage limiting.

purplesyringa commented 5 years ago

@HelloZeroNet

but not sure if this way it's possible to do permission requests

Sure it is; I have little time to explain the details right now, but here's an overview: you can create an iframe with a gate document, set Content-Security-Policy: sandbox allow-scripts for that document, and use shadow dom to position that iframe.

the sidebar

Same as above

you won't be able to do cookie

We can use path=/address to limit cookies to a specific site.

localstorage limiting

This is a bit more difficult, but we can use the following trick: we'd store the local storage content in users.json as we do with siteSettings already; if we really want some consistency with localStorage behaviour (i.e. unique to browser), we could fingerprint the browser with User-Agent or something. Then, we'd first put localStorage to the prefix and then use that data to read localStorage, and then we'd use UiWebsocket to modify localStorage.

This means that compromising localStorage (e.g. with an iframe) makes no sense because it's empty.

purplesyringa commented 5 years ago

@github-zeronet No ETA yet, mostly because I can't predict it, but I'll tell you when I'll finish a PoC.

purplesyringa commented 5 years ago

Please see my proposal at #2169. @github-zeronet