How to combine advantages of localStorage and sessionStorage
Asked Answered
D

3

12

I want to share my authentication token between browser tabs which would suggest to use HTML5 localStorage. But I don't want anything related to authentication to stay in my storage when the browser is closed which would suggest using HTML5 sessionStorage.

Reference 1 related to this topic (click):

That means that you can't share between tabs, for this you should use localStorage

Reference 2 related to this topic (click):

Therefore, it's recommended not to store any sensitive information in local storage

How can I combine the sharing of my authentication token between tabs and still make sure that when the browser closes nothing stays behind in storage?

How do other websites solve this seemingly simple problem.

Derision answered 3/8, 2015 at 16:30 Comment(1)
Oddly enough, 3 years have passed , still this crucial features is rarely mentioned/ discussed /cared on the internet or any relating communityPardner
G
7

We use cookies.

Storages have their uses. But cookies meet all your requirements.

  • Cookies are shared across all same origin tab. You can even specify their paths, but they are shared by default.

  • Cookies are automatically deleted by browser when it is closed, you need to do nothing; this is again the default behaviour.

  • Cookies can be easily made as secure or more secure than storage.

Sometime the best solution is the simplest solution. No need to reinvent the wheel.

Cookie Security

Session cookie is stored on disk like sessionStorage (in case the browser crash and need to recover). So they are about equally secure in term of local disk read/write.

Both cookie and storage processing script may be acquired or even stolen during http transfer, so you must use HTTPS for both cases. (This is the least you should do.)

Cookie can be configured to be HTTP only, preventing JavaScript from accessing it, thus making immune from XSS script and greasemonkey hijacking.

In case when an auth token is stolen, we also associate each token with the machine's user agent and ip. This prevent the token from being used by someone from external network. If you want, you can add more content negotiation headers to the mix - not all robots copy all headers.

For another level of security, you can add client side fingerprinting. These fingerprints must be captured on client side and transferred over network, so they are not bulletproof, but they will force internal attackers (attackers on same network with the user) to jump through another hoop.

At this point, they will usually switch to easier attacks outside your control.

Gook answered 20/10, 2015 at 15:7 Comment(4)
"Your main concern seems to be self-destruction rather than security, so I'll stop here" By all means, continue if you have something to add :)Derision
I like the cookie option, it's easier and simpler than mine. The only thing would be to specify that this is for session cookies, and that the behavior may depend on the browser settingsIthnan
@Derision As you wish, I elaborated a bit on cookie token security. Hope it'll be enough. Security is a deep topic :pGook
Thanks. I agree with @AlvaroMontoro that your answer is nice and simple, so I will accept yours. But I think these security issues are certainly not to be underestimated. Cookies seem easier accessible then local storage.Derision
I
5

One option to achieve this could be to clean the localStorage when the page is about to be closed/unloaded, so nothing stays behind in storage. That way the localStorage would behave like the sessionStorage in the sense that it would be removed once the page is closed, but it would still have the advantage from the localStorage of sharing data across tabs.

For that you could use the onbeforeunload event:

The beforeunload event is fired when the window, the document and its resources are about to be unloaded.

Something like this:

// code here
...

// you have the data in the localStorage
localStorage.setItem("data", "My Data");

// more code here
...

// when the page is about to be unloaded, remove the localStorage data
window.addEventListener("beforeunload", function() {
    localStorage.removeItem("data");
});

But that solution is way too simple, and there is a catch: when you close one tab, the localStorage data is deleted, even if you have other tabs open! (leaving them without any data). To prevent removing the data when one of the tabs is closed, you could keep track of the number of tabs, and only clean the localStorage when the number is zero.

The pseudo-code for that would be:

  1. On page load:
    1. If the localStorage has a count of tabs
      1. Increment the tab count by 1
    2. Or else (no count of tabs)
      1. Create a variable in the localStorage that will contain the number of tabs.
      2. Set that variable to 1 (the current tab).
  2. On page unload:
    1. Decrease the tab count by 1.
    2. If the tab count is zero (this was the last tab open)
      1. Delete the content of the localStorage

And here is a really simple demo. I added some console.log messages so you can open the console and see what is happening with each load/unload:

<!doctype html>
<html>
    <head>
        <title>Test LocalStorage</title>
    </head>
    <body>
        <h1>Hello World!</h1>
        <div id="tabs">Open tabs = 0</div>
        <script>
            window.addEventListener("load", function() {
                if (!localStorage.tabs) {
                    console.log("Create the tab count");
                    localStorage.setItem("tabs", 1);
                    // restore data in case of refresh, or set to initial value in case of new window
                    localStorage.setItem("data", sessionStorage.getItem("data") || "Initial");
                } else {
                    console.log("Add one tab to the count");
                    localStorage.tabs++;
                }

                document.getElementById("tabs").innerHTML = "Open tabs = " + localStorage.tabs;
            });

            window.addEventListener("beforeunload", function() {
                if (parseInt(localStorage.tabs) == 1) {
                    console.log("Last tab: remove localStorage");
                    // save the data temporarily in the sessionStorage (in case of reload)
                    sessionStorage.setItem("data", localStorage.getItem("data")); 
                    localStorage.removeItem("tabs");
                    localStorage.removeItem("data");
                } else {
                    console.log("There are more tabs open, decrease count by 1");
                    localStorage.tabs--;
                }
            });
        </script>
    </body>
</html>

Copy that code into a page, and open it in several tabs, you'll see how the number of tabs increases (and updates if you reload one of the open tabs). Then close them and with the last one, the "data" item in the localStorage will be cleared.

Ithnan answered 17/10, 2015 at 23:6 Comment(5)
Thanks, I though about this, but it does not work for browsers or tabs crashing, does it? Then you will end up with uncleaned or invalid count in storage.Derision
How about adding some type of expiration timestamp that is updated with a timer? When the page loads, if there's a timer but it's expired then clean the local storage?Ithnan
This is a really solid solution with one painful caveat: If the user only has one tab open and they refresh the page, the storage gets cleared. If there was some way around that, this would be 100% awesome (like, W3C-standard worthy for some middle ground between local and session).Redware
@CodyS You could go around that issue by copying the value of localStorage.data to sessionStorage.data right before removing it from the localStorage, then on load if there are no tabs, check if sessionStorage.data exists and initialize it to that value. The sessionStorage is preserved in a refresh but not on a window/tab close. I will update the code to show an example.Ithnan
Trying to find a way around the browser/tab crashing. I can only think about the timestamp checker right now: the data would still be in the localStorage but it would be deleted on load if it has been more than x time since the last update.Ithnan
U
0

Suppose your application retrieves most of its data using REST calls, and suppose at least some of that data comes from one or more servers that do not have the same origin as the page was served from. Finally, suppose that the user has disabled third-party cookies on the browser.

Eight years after the OP, this is not farfetched as Safari has phased out third-party cookies by default and other browsers let those be disabled as an option. In that context, cookies don't work. Besides, it's more secure to keep data in sessionStorage than to pass it back and forth in a cookie.

A solution has emerged that uses events and localStorage to synchronize sessionStorage across all tabs. Several blogs and SO posts explain how. This is a good example.

Untruth answered 23/10, 2023 at 6:8 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.