I launch a new browser window from a parent window with:
window.open(url, "MyAppTitle", "width=300, height=600, menubar=no, scrollbars=no, resizable=no");
The child window needs a token (like a password), to establish a connection to a web service. To send the token from the parent to the child I use:
window.tokenString = "mytoken";
where mytoken
is the token that needs to be transferred to the other window.
Then, in the other window, I retrieve the token with:
var retrievedToken = window.opener.tokenString;
Everything works fine in desktop browsers, but in mobile browsers it doesn’t work. To make it work in mobile browsers I can save the token to localStorage with:
localStorage.setItem("tokenString", "mytoken");
then get the value with:
var retrievedToken = localStorage.getItem("tokenString");
The problem with this approach is that it is less safe. Even if I delete the password (with localStorage.remove("tokenString")
) immediately after it is used to make the connection, there are still a few seconds where it is saved to the browser’s storage.
I also tried other suggestions like:
var newWindow = window.open(url, "MyAppTitle", "width=300, height=600, menubar=no, scrollbars=no, resizable=no");
newWindow.tokenString = "mytoken";
Then, in the new window, retrieve it with:
window.tokenString
or
var newWindow = window.open(url, "MyAppTitle", "width=300, height=600, menubar=no, scrollbars=no, resizable=no");
newWindow["tokenString"] = "mytoken";
Then, in the new window, retrieve it with:
window["tokenString"]
but they don’t work in mobile browsers. The only thing that works there is to use localStorage to store the token and then delete it after it has been used to establish the connection. Yet, this doesn’t seem very safe. I know that passing the data with the first method isn’t very safe either, but in that case it doesn’t get saved to the browser’s storage. Passing the data as a query parameter added to the URL is also not safe. My question is: what would be the best way to pass the sensitive string from the parent window to the child window, so that it works in desktop and mobile browsers ?
Thank you.
Later Edit: I changed the wording a bit to make the post clearer.
2
Answers
Thank you "Mike 'Pomax' Kamermans" for your message. Indeed, the generic solution for this problem is to use the postMessage() method:
On the parent window add:
then, on the child window add:
In my case both windows are on the same domain. This is why before getting the token I verified if the base of the popup's URL (window.location.origin) is the same as that of the window who opened the popup (event.origin).
However, nowadays many users have their browsers configured to block popups. When the browser blocks popups, the
windpop
variable from above is null and it remains null even after the new window is loaded completely. So,windpop.postMessage()
is never triggered and the child window doesn't get any data from the parent window.I’ll disagree with the comments saying that there is no secure content in front-end: It all depends on the threat model.
If we talk about an adversary having physical access to the computer or even just with dev-tools level clearance (e.g. a browser extension), then indeed you can’t consider anything secure. We need to assume they can just intercept anything in memory / displayed on screen / going through the network. But thinking this way is futile. Would you leave your home’s doors wide open at all time just because someone could anyway dig a hole in the walls?
If your threat is an external page being able to read your data, then it’s secure. Even the fact of having it as a global on your opener is also secure. Cross-origin pages won’t be able to access your page’s globals.
If the adversary has access to your page and its globals, then they can also access all of your page’s resources and thus to the very sensible data you’re trying to protect. I.E. you’re already doomed. Using
postMessage
is no more secure in such a case, they can override it and grab all the data you pass to it. Server-side session isn’t much more secure either. Since they could access your page’s global, they can also access the popup’s global (e.g. they can overridewindow.open()
if needed), and they’ll get access to all the data fetched from that popup. They can even impersonate both your pages since they have access to yourfetch()
method, from your origin. Your server would have no mean to know that it’s not talking to you.So instead of trying to hide your data, prevent your adversaries to gain access to your page: avoid untrusted scripts to run on your page, and sandbox all your iframes.
Now, globals are bad for other reasons, so you may want to use another mean of accessing that key. Server-side sessions seem to be a decent solution for the case as you’ve presented it. Another way to pass data between an opener and a same-origin popup is to declare a function in the popup and call it from the opener (somehow I personally find it cleaner to have functions as globals rather than values). And for cross-origin pages you have
postMessage()
.