I'm replacing cookies with localStorage on browsers that can support it (anyone but IE). The problem is site.example and www.site.example store their own separate localStorage objects. I believe www is considered a subdomain (a stupid decision if you ask me). If a user was originally on site.example and decides to type in www.site.example on her next visit, all her personal data will be inaccessible. How do I get all my "subdomains" to share the same localStorage as the main domain?
This is how I use it across domains...
Use an iframe from your parent domain - say parent.example
Then on each child.example domain, just do a postMessage to your parent.example iframe
All you need to do is setup a protocol of how to interpret your postMessage messages to talk to the parent.example iframe.
If you're using the iframe and postMessage solution just for this particular problem, I think it might be less work (both code-wise and computation-wise) to just store the data in a subdomain-less cookie and, if it's not already in localStorage on load, grab it from the cookie.
Pros:
Doesn't need the extra iframe and postMessage set up.
Cons:
Will make the data available across all subdomains (not just www) so if you don't trust all the subdomains it may not work for you.
Will send the data to the server on each request. Not great, but depending on your scenario, maybe still less work than the iframe/postMessage solution.
If you're doing this, why not just use the cookies directly? Depends on your context.
4K max cookie size, total across all cookies for the domain (Thanks to Blake for pointing this out in comments)
I agree with other commenters though, this seems like it should be a specifiable option for localStorage so work-arounds aren't required.
I suggest making site.example redirect to www.site.example for both consistency and for avoiding issues like this.
Also, consider using a cross-browser solution like PersistJS that can use each browser native storage.
Set to cookie in the main domain:
document.cookie = "key=value;domain=.mydomain.example"
and then take the data from any main domain or sub domain and set it on the localStorage
This is how:
[November 2020 Update: This solution relies on being able to set document.domain. The ability to do that has now been deprecated, unfortunately. NOTE ALSO that doing so removes the "firewall" between domains and subdomains for vulnerability to XSS attacks or other malicious script, and has further security implications for shared hosting, as described on the MDN page. September 2022 Update: From Chrome v109, setiing document.domain will only be possible on pages that also send an Origin-Agent-Cluster: ?0 header.]
For sharing between subdomains of a given superdomain (e.g. example.com), there's a technique you can use in that situation. It can be applied to localStorage, IndexedDB, SharedWorker, BroadcastChannel, etc, all of which offer shared functionality between same-origin pages, but for some reason don't respect any modification to document.domain that would let them use the superdomain as their origin directly.
(1) Pick one "main" domain to for the data to belong to: i.e. either https://example.com or https://www.example.com will hold your localStorage data. Let's say you pick https://example.com.
(2) Use localStorage normally for that chosen domain's pages.
(3) On all https://www.example.com pages (the other domain), use javascript to set document.domain = "example.com";. Then also create a hidden <iframe>, and navigate it to some page on the chosen https://example.com domain (It doesn't matter what page, as long as you can insert a very little snippet of javascript on there. If you're creating the site, just make an empty page specifically for this purpose. If you're writing an extension or a Greasemonkey-style userscript and so don't have any control over pages on the example.com server, just pick the most lightweight page you can find and insert your script into it. Some kind of "not found" page would probably be fine).
(4) The script on the hidden iframe page need only (a) set document.domain = "example.com";, and (b) notify the parent window when this is done. After that, the parent window can access the iframe window and all its objects without restriction! So the minimal iframe page is something like:
<!doctype html>
<html>
<head>
<script>
document.domain = "example.com";
window.parent.iframeReady(); // function defined & called on parent window
</script>
</head>
<body></body>
</html>
If writing a userscript, you might not want to add externally-accessible functions such as iframeReady() to your unsafeWindow, so instead a better way to notify the main window userscript might be to use a custom event:
window.parent.dispatchEvent(new CustomEvent("iframeReady"));
Which you'd detect by adding a listener for the custom "iframeReady" event to your main page's window.
(NOTE: You need to set document.domain = "example.com" even if the iframe's domain is already example.com: Assigning a value to document.domain implicitly sets the origin's port to null, and both ports must match for the iframe and its parent to be considered same-origin. See the note here: https://developer.mozilla.org/en-US/docs/Web/Security/Same-origin_policy#Changing_origin)
(5) Once the hidden iframe has informed its parent window that it's ready, script in the parent window can just use iframe.contentWindow.localStorage, iframe.contentWindow.indexedDB, iframe.contentWindow.BroadcastChannel, iframe.contentWindow.SharedWorker instead of window.localStorage, window.indexedDB, etc. ...and all these objects will be scoped to the chosen https://example.com origin - so they'll have the this same shared origin for all of your pages!
The most awkward part of this technique is that you have to wait for the iframe to load before proceeding. So you can't just blithely start using localStorage in your DOMContentLoaded handler, for example. Also you might want to add some error handling to detect if the hidden iframe fails to load correctly.
Obviously, you should also make sure the hidden iframe is not removed or navigated during the lifetime of your page... OTOH I don't know what the result of that would be, but very likely bad things would happen.
And, a caveat: setting/changing document.domain can be blocked using the Feature-Policy header, in which case this technique will not be usable as described.
However, there is a significantly more-complicated generalization of this technique, that can't be blocked by Feature-Policy, and that also allows entirely unrelated domains to share data, communications, and shared workers (i.e. not just subdomains off a common superdomain). #Mayank Jain already described it in their answer, namely:
The general idea is that, just as above, you create a hidden iframe to provide the correct origin for access; but instead of then just grabbing the iframe window's properties directly, you use script inside the iframe to do all of the work, and you communicate between the iframe and your main window only using postMessage() and addEventListener("message",...).
This works because postMessage() can be used even between different-origin windows. But it's also significantly more complicated because you have to pass everything through some kind of messaging infrastructure that you create between the iframe and the main window, rather than just using the localStorage, IndexedDB, etc. APIs directly in your main window's code.
I'm using xdLocalStorage, this is a lightweight js library which implements LocalStorage interface and support cross domain storage by using iframe post message communication.( angularJS support )
https://github.com/ofirdagan/cross-domain-local-storage
this kind of solution causes many problems like this. for consistency and SEO considerations
redirect on the main domain is the best solution.
do it redirection at the server level
How To Redirect www to Non-www with Nginx
https://www.digitalocean.com/community/tutorials/how-to-redirect-www-to-non-www-with-nginx-on-centos-7
or
any other level like route 53 if are using
This is how I solved it for my website. I redirected all the pages without www to www.site.example. This way, it will always take localstorage of www.site.example
Add the following to your .htaccess, (create one if you already don't have it) in root directory
RewriteEngine On
RewriteCond %{HTTP_HOST} !^www\. [NC]
RewriteRule ^(.*)$ http://www.%{HTTP_HOST}/$1 [R=301,L]
Related
I use electron 0.36.0 and I have a cookie for a page and this page has a nested frame with a different domain.
I need to somehow keep a cookie alive when going to this frame directly (different domain) and ideally, I want to avoid upgrades of electron (otherwise I would have to rewrite my code).
What's an ideal solution for my situation (it can be also a small workaround)?
Thank you!
Cookies can now be shared with different domains so you will have to find a different way of doing what your trying to do.
For cross domain cookies alcuadrado has described a work around to do this in his post like below:
centralize all cookies in a single domain, let's say cookiemaker.com
when the user makes a request to example.com you redirect him to cookiemaker.com
cookiemaker.com redirects him back to example.com with the information you need
check this answer and this.
I'm maintaining an application that goes sort of like this:
There is a Page A with a Frame that shows Page B. Now page B is part of a completely different product in a separate domain.
Now, they want that when an option in B is clicked, the WHOLE page is redirected to another page in A. The problem is that the url of A is something like www.client.A.com/Order/Details/123, and when we click in be it should redirect to something like www.client.A.com/Order/Edit/123 but B doesn't know anything about A. It doesn't know which order # is currently selected or anything about A. Page A who has the frame B does know it.
For now my solution has been to just redirect to the AllOrders so something like client.MyCompany/Orders
but since B doesn't know which client is calling it (its a multi-tenant app), I'll add it in the webconfig. (so each client has its own webconfig with a different value).
I dont find this solution optimal but I can't think of anything else! I already tried putting the needed url in page A in a hidden Div (since A does know all the info) and then trying to read the whole DOM of the page from B to find it.... unfortunately I can only get access to Frame B's DOM... (I tried with jquery).
I know frames are evil, but this is how it is written... any ideas?
Thanks!
If the parent page A and the iframe page B are in different domains, you will not be able to access methods or fields via B's parent property, nor will script in A be able to reach into B's content, nor will you be able to share global variables between A and B. This boundary placed between page A and page B is a key part of the browser security model. It's what prevents evil.com from wrapping your online bank web page and stealing your account info just by reading the internal variables of the javascript of the bank's web page.
If you have the luxury of requiring the latest generation of browsers, you can use the postmessage technique mentioned in one of the other answers here. If you need to support older browsers, you may be able to pass small amounts of information using cross-domain client scripting techniques in the browser. One example of this is to use iframes to communicate info between the outer page A and the inner page B. It's not easy and there are many steps involved, but it can be done. I wrote an article on this awhile ago.
You will not be able to monitor clicks in B's iframe from the parent page A. That's a violation of browser security policies at multiple levels. (Click hijacking, for one) You won't be able to see when B's URL changes - A can write to the iframe.src property to change the URL, but once the iframe.src points to a different domain than A's domain, A can no longer read the iframe.src property.
If A and B are in different subdomains of the same root domain, you may have an opportunity to "lower" the domain to a common root. For example, if the outer page A is hosted in subdomain A.foo.bar.com, and B is hosted in subdomain foo.bar.com, then you can lower the domain in page A to foo.bar.com (by assigning window.domain = "foo.bar.com" in A's script). Page A will then behave as a peer of page B and the two can then access each other's data as needed, even though A is technically being served from a different domain than B. I wrote an article on domain lowering, too.
Domain lowering can only peel off innermost subdomains to operate in the context of a root domain. You can't change A.foo.bar.com to abc.com.
There is also a slight risk in lowering domains to a common root domain. When you operate your page in its own subdomain, your html and script are segregated from the other subdomains off the common root domain. If a server in one of the other subdomains is compromised, it doesn't really affect your html page.
If you lower your page's domain to the common root domain, you are exposing your internals to script running on the common root domain and to script from other subdomains that has also lowered its domain to the common root. If a server in one of the other subdomains is compromised, it will have access to your script's internals and therefore it may have compromised your subdomain as well.
in case the page & frame are not on the same domain, you'll have to use postmessage as the same-domain policy prohibits normal javascript-communication between pages/frames of different domains because of security concerns.
postmessage is part of html5 and works in all modern browsers (including IE8). if you need support for older browsers (specifally IE6/7), you could use the jQuery postmessage plugin (which transparently falls back to some nice hash-tag trickery for older browsers).
and as a sidenote: not sure if frames are evil, there are some problems (usability, SEO, ...) related to them, but i did some research and most of these can be tackled i think.
If you want to communicate between frames in javascript you can use 'parent':
If frame A has a variable value, eg:
var orderNo = 2;
For frame B to read it it would refer to
var frameA_orderNo = parent.frames[0].orderNo;
(assuming that frame A is the first frame declared)
So you can set up global variables within each frame that the other frame can read and therefore you can get the order # in old fashioned javascript (never tried it in jquery).
Wow frames - never thought I'd think about them again.
I am trying to load a a page into an iframe. When that page is loaded i wish to edit the contents of the "pre" tag which is inside the loaded document. The loaded doc is from another domain. I am using : resultframe is the iframe
var atag= document.getElementById("resultframe").contentWindow.document.getElementsByTagName('pre');
atag[0].innerHTML="done";
to access the tag.
problem: there seems to be no effect of this statement. I need to know the correct syntax and also that can i access the elements of pages loaded from different domain. I got the syntax from the web and also some variation of it.
Please suggest.
While JavaScript is limited by cross-domain policies that prevent interaction with another domain, there is one potential workaround as long as you can live with certain limitations.
By using something like PHP and it's cURL library you can grab the contents of a page from just about anywhere (even a secure page or one that requires a login, as long as you have credentials). You can then parse the page, edit what you need to, and display it within your own site. It's important to realize, though, that this is simply your own local copy of the page. You won't have the luxury of actually changing the contents of the page itself.
Another possibility, which would require access to all domains you wish to edit, would be to employ a web service that would accept edits in the form of a PUT request. You can achieve a lot more with a web service, but it would have to be available on all target domains that you wish to make changes to.
In the near future, XMLHttpRequest Level 2 might become a reality and will bring Cross-Origin Resource Sharing (CORS) with it. CORS will allow web applications on one domain to make cross domain AJAX requests to another domain. The target domain will have a header giving express permission to allow requests from another. Potentially, this could be used to send edits to another site.
You can't. Browsers have cross-domain policies, for security reasons.
What if I included the facebook page in an iframe, and I can get all your information because you're always connected to it?
I am creating a bookmarklet that is to be used across a wide range of domains. I wanted to set some cookies to store temporary settings for this bookmarklet, so I assumed that setting a cookie from this script would assign the cookie to the domain of the script's origin.
This was not the case, the bookmarklet is able to assign cookies to the domain of the current site being viewed. This is not suitable for my needs (this would remember settings per domain, rather than for the bookmarklet across all domains).
My question is, is this somehow breaking the cross domain policy? And a follow up question, how can I store cookies for the bookmarklet rather than the correct domain it is used on.
Bookmarklets are running in the context of the current page so that is the security context they run in and thus this doesn't break cross domain policy. You can only set cookies on the current page's domain. Because of this your bookmarklet can't have it's own cookies.
This is the same as scripts that are loaded into a given page from a variety of domains. The origin of the page is what matters, not the origin of the script.
The only way I know of for you to save settings once for your script across all domains would be to use cross domain JSONP and store the settings on your server, but you still may have difficulty identifying a unique user.
It sounds like what you're trying to do would be much more suited to a browser plug-in which has local storage for the plug-in.
It does not break cross domain policy, since it is in fact run on a separate domain (that's the point behind a bookmarklet).
If you want to store cookie information, either make use of a 3rd party service (as in, have your own server with code that accepts cookie changes).
Note that this can be a security issue since every domain would be able to get cookies for your user, unless you make your service write-only (which I doubt).
Then there's another alternative - don't save settings in a cookie. Use a different storage medium instead.
I'm replacing cookies with localStorage on browsers that can support it (anyone but IE). The problem is site.example and www.site.example store their own separate localStorage objects. I believe www is considered a subdomain (a stupid decision if you ask me). If a user was originally on site.example and decides to type in www.site.example on her next visit, all her personal data will be inaccessible. How do I get all my "subdomains" to share the same localStorage as the main domain?
This is how I use it across domains...
Use an iframe from your parent domain - say parent.example
Then on each child.example domain, just do a postMessage to your parent.example iframe
All you need to do is setup a protocol of how to interpret your postMessage messages to talk to the parent.example iframe.
If you're using the iframe and postMessage solution just for this particular problem, I think it might be less work (both code-wise and computation-wise) to just store the data in a subdomain-less cookie and, if it's not already in localStorage on load, grab it from the cookie.
Pros:
Doesn't need the extra iframe and postMessage set up.
Cons:
Will make the data available across all subdomains (not just www) so if you don't trust all the subdomains it may not work for you.
Will send the data to the server on each request. Not great, but depending on your scenario, maybe still less work than the iframe/postMessage solution.
If you're doing this, why not just use the cookies directly? Depends on your context.
4K max cookie size, total across all cookies for the domain (Thanks to Blake for pointing this out in comments)
I agree with other commenters though, this seems like it should be a specifiable option for localStorage so work-arounds aren't required.
I suggest making site.example redirect to www.site.example for both consistency and for avoiding issues like this.
Also, consider using a cross-browser solution like PersistJS that can use each browser native storage.
Set to cookie in the main domain:
document.cookie = "key=value;domain=.mydomain.example"
and then take the data from any main domain or sub domain and set it on the localStorage
This is how:
[November 2020 Update: This solution relies on being able to set document.domain. The ability to do that has now been deprecated, unfortunately. NOTE ALSO that doing so removes the "firewall" between domains and subdomains for vulnerability to XSS attacks or other malicious script, and has further security implications for shared hosting, as described on the MDN page. September 2022 Update: From Chrome v109, setiing document.domain will only be possible on pages that also send an Origin-Agent-Cluster: ?0 header.]
For sharing between subdomains of a given superdomain (e.g. example.com), there's a technique you can use in that situation. It can be applied to localStorage, IndexedDB, SharedWorker, BroadcastChannel, etc, all of which offer shared functionality between same-origin pages, but for some reason don't respect any modification to document.domain that would let them use the superdomain as their origin directly.
(1) Pick one "main" domain to for the data to belong to: i.e. either https://example.com or https://www.example.com will hold your localStorage data. Let's say you pick https://example.com.
(2) Use localStorage normally for that chosen domain's pages.
(3) On all https://www.example.com pages (the other domain), use javascript to set document.domain = "example.com";. Then also create a hidden <iframe>, and navigate it to some page on the chosen https://example.com domain (It doesn't matter what page, as long as you can insert a very little snippet of javascript on there. If you're creating the site, just make an empty page specifically for this purpose. If you're writing an extension or a Greasemonkey-style userscript and so don't have any control over pages on the example.com server, just pick the most lightweight page you can find and insert your script into it. Some kind of "not found" page would probably be fine).
(4) The script on the hidden iframe page need only (a) set document.domain = "example.com";, and (b) notify the parent window when this is done. After that, the parent window can access the iframe window and all its objects without restriction! So the minimal iframe page is something like:
<!doctype html>
<html>
<head>
<script>
document.domain = "example.com";
window.parent.iframeReady(); // function defined & called on parent window
</script>
</head>
<body></body>
</html>
If writing a userscript, you might not want to add externally-accessible functions such as iframeReady() to your unsafeWindow, so instead a better way to notify the main window userscript might be to use a custom event:
window.parent.dispatchEvent(new CustomEvent("iframeReady"));
Which you'd detect by adding a listener for the custom "iframeReady" event to your main page's window.
(NOTE: You need to set document.domain = "example.com" even if the iframe's domain is already example.com: Assigning a value to document.domain implicitly sets the origin's port to null, and both ports must match for the iframe and its parent to be considered same-origin. See the note here: https://developer.mozilla.org/en-US/docs/Web/Security/Same-origin_policy#Changing_origin)
(5) Once the hidden iframe has informed its parent window that it's ready, script in the parent window can just use iframe.contentWindow.localStorage, iframe.contentWindow.indexedDB, iframe.contentWindow.BroadcastChannel, iframe.contentWindow.SharedWorker instead of window.localStorage, window.indexedDB, etc. ...and all these objects will be scoped to the chosen https://example.com origin - so they'll have the this same shared origin for all of your pages!
The most awkward part of this technique is that you have to wait for the iframe to load before proceeding. So you can't just blithely start using localStorage in your DOMContentLoaded handler, for example. Also you might want to add some error handling to detect if the hidden iframe fails to load correctly.
Obviously, you should also make sure the hidden iframe is not removed or navigated during the lifetime of your page... OTOH I don't know what the result of that would be, but very likely bad things would happen.
And, a caveat: setting/changing document.domain can be blocked using the Feature-Policy header, in which case this technique will not be usable as described.
However, there is a significantly more-complicated generalization of this technique, that can't be blocked by Feature-Policy, and that also allows entirely unrelated domains to share data, communications, and shared workers (i.e. not just subdomains off a common superdomain). #Mayank Jain already described it in their answer, namely:
The general idea is that, just as above, you create a hidden iframe to provide the correct origin for access; but instead of then just grabbing the iframe window's properties directly, you use script inside the iframe to do all of the work, and you communicate between the iframe and your main window only using postMessage() and addEventListener("message",...).
This works because postMessage() can be used even between different-origin windows. But it's also significantly more complicated because you have to pass everything through some kind of messaging infrastructure that you create between the iframe and the main window, rather than just using the localStorage, IndexedDB, etc. APIs directly in your main window's code.
I'm using xdLocalStorage, this is a lightweight js library which implements LocalStorage interface and support cross domain storage by using iframe post message communication.( angularJS support )
https://github.com/ofirdagan/cross-domain-local-storage
this kind of solution causes many problems like this. for consistency and SEO considerations
redirect on the main domain is the best solution.
do it redirection at the server level
How To Redirect www to Non-www with Nginx
https://www.digitalocean.com/community/tutorials/how-to-redirect-www-to-non-www-with-nginx-on-centos-7
or
any other level like route 53 if are using
This is how I solved it for my website. I redirected all the pages without www to www.site.example. This way, it will always take localstorage of www.site.example
Add the following to your .htaccess, (create one if you already don't have it) in root directory
RewriteEngine On
RewriteCond %{HTTP_HOST} !^www\. [NC]
RewriteRule ^(.*)$ http://www.%{HTTP_HOST}/$1 [R=301,L]