Angular window.localStorage different subdomain [duplicate] - javascript

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

X-frame origins block, is there an alternative to embedding? VUE JS [duplicate]

I am developing a web page that needs to display, in an iframe, a report served by another company's SharePoint server. They are fine with this.
The page we're trying to render in the iframe is giving us X-Frame-Options: SAMEORIGIN which causes the browser (at least IE8) to refuse to render the content in a frame.
First, is this something they can control or is it something SharePoint just does by default? If I ask them to turn this off, could they even do it?
Second, can I do something to tell the browser to ignore this http header and just render the frame?
If the 2nd company is happy for you to access their content in an IFrame then they need to take the restriction off - they can do this fairly easily in the IIS config.
There's nothing you can do to circumvent it and anything that does work should get patched quickly in a security hotfix. You can't tell the browser to just render the frame if the source content header says not allowed in frames. That would make it easier for session hijacking.
If the content is GET only you don't post data back then you could get the page server side and proxy the content without the header, but then any post back should get invalidated.
UPDATE: 2019-12-30
It seem that this tool is no longer working! [Request for update!]
UPDATE 2019-01-06: You can bypass X-Frame-Options in an <iframe> using my X-Frame-Bypass Web Component. It extends the IFrame element by using multiple CORS proxies and it was tested in the latest Firefox and Chrome.
You can use it as follows:
(Optional) Include the Custom Elements with Built-in Extends polyfill for Safari:
<script src="https://unpkg.com/#ungap/custom-elements-builtin"></script>
Include the X-Frame-Bypass JS module:
<script type="module" src="x-frame-bypass.js"></script>
Insert the X-Frame-Bypass Custom Element:
<iframe is="x-frame-bypass" src="https://example.org/"></iframe>
The X-Frame-Options header is a security feature enforced at the browser level.
If you have control over your user base (IT dept for corp app), you could try something like a greasemonkey script (if you can a) deploy greasemonkey across everyone and b) deploy your script in a shared way)...
Alternatively, you can proxy their result. Create an endpoint on your server, and have that endpoint open a connection to the target endpoint, and simply funnel traffic backwards.
Yes Fiddler is an option for me:
Open Fiddler menu > Rules > Customize Rules (this effectively edits CustomRules.js).
Find the function OnBeforeResponse
Add the following lines:
oSession.oResponse.headers.Remove("X-Frame-Options");
oSession.oResponse.headers.Add("Access-Control-Allow-Origin", "*");
Remember to save the script!
As for second question - you can use Fiddler filters to set response X-Frame-Options header manually to something like ALLOW-FROM *. But, of course, this trick will work only for you - other users still won't be able to see iframe content(if they not do the same).

How can I set data to localStorage for subdomain? [duplicate]

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]

HTML5 localStorage Showing Different Data in Different Frames (Same Domain)

I am seeing an issue that, by my reading, shouldn't be happening. In short, I have a web application with nested iframes. The frame containership is as shown below:
http://mysite.doh
https://othersite.duh
https://mysite.doh/Panel.html?urlparam1=x
https://mysite.doh/Panel.html?urlparam2=y
Note: I have been careful about indicating the http vs. https, since protocol is part of what is considered for the same origin policy. These are indeed HTTPS iframes inside of an HTTP main page. When the Panel.html opens, it attempts to record a couple of query parameters so that its twin panel(s) also knows them. So there is code like:
urlparam1 = $.urlParam('urlparam1 ');
if (urlparam1 == null || urlparam1 == ''){
urlparam1 = localStorage.getItem("urlparam1");
} else {
localStorage.setItem("urlparam1", urlparam1);
}
My goal is to transfer the value of urlparam1 to all versions of Panel.html. This is actually the case any time a panel opens on the browser, and each version of the Panel has a polling function to look for changed data in the localStorage.
However, localStorage seems to not be getting the job done correctly. Changes to localStorage on one panel are not reflected in its counterpart. This seems mighty odd, since it is my understanding that localStorage is to be shared by all pages with the same origin. The two panels are definitely the same origin: they're literally the same URL, just with different query parameters.
Anyone know why this might be the case? I have looked at the localStorage dictionaries, and they have completely different data inside of them (e.g., one has urlparam1, the other urlparam2- they should both have the same data, with both values available). I can think of no reason why this might be the case. The origins are identical (including the protocol). The browsers I have been testing with are FireFox and Chrome (mostly FireFox).
EDIT: As an update, it seems like it might be due to the settings for the iframe built by otherside.duh. They are allowing scripts, but may be disallowing allow-same-origin. (Source: https://readable-email.org/list/whatwg/topic/cross-origin-iframe-and-sandbox-allow-same-origin) This appears to be advised on many older sites, since it indicates that if both allow scripts and allow-same-origin are enabled, the iframe can then remove its own sandbox property. Is that still true (sources were 2013)? Because that sounds like an incredibly bad design decision, if so. I can't imagine why anyone would want that to be desired behavior. To clarify what allow-same-origin does, it is
If you don't have allow-same-origin, the content ends up in a unique origin, not its "real" origin.
So it's as if your frame runs on some arbitrary site, rather than the one that you actually use.

Eliminate: ISP Injects Pages with Iframe Script for Ads

So my ISP (Smartfren; Indonesia) has decided to start injecting all non-SSL pages with an iframing script that allows them to insert ads into pages. Here's what's happening:
My browser sends a request to the server. ISP intercepts it and instead returns a javascript that loads the requested page inside an iframe.
Aside being annoying in principle, this injection also breaks any number of standard page functionality; and presents possible security hazards.
What I've tried to do so far:
Using a GreaseMonkey script to nix away the injected code and redirect to the original URL. Result: Breaks some legitimate iframes. Also, the ISP's code gets executed, because GreaseMonkey only kicks in after the page is loaded.
Using Privoxy for a local proxy and setting up a filter to clean up the injection and replace it with a plain javascript redirect to the original URL. Result: Breaks some legitimate iframes. ISP's code never gets to the browser.
You can view the GreaseMonkey and Privoxy fixes I've been working on at the following paste: http://pastebin.com/sKQTvgY2 ... along with a sample of the ISP's injection.
Ideally I could configure Privoxy to immediately resend the request when the alteration is detected, instead of filtering out the injected JS and replacing it with a JS redirection to the original URL. (The ISP-injection gets switched off when the same request is resent without delay.) I'm yet to figure out how to accomplish that. I believe it'd fix the iframe-breaking problem.
I know I could switch to a VPN or use the Tor browser. (Or change the ISP.) I'm hoping there's another way around. Any suggestions on how to eliminate this nuisance?
Actually now I have a solution:
The ISP proxy react on the Accept: header that the browser sends.
So this is the default for firefox:
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,/;q=0.8
Now we are going to change this default:
And set it to: Accept: */*
Here is how to setup header hacker for google chrome
Set the title to anything you like:NO IFRAME
Append/replace select replace with
String */*
And Match string to .* and then click add.
In the permanent header switches
Set domain to .* and select the rule you just created
PS: changing it in the firefox settings does not work 100% because some request like ajax seem to bypass it so a plugin is the only way as it literally intercepts every outgoing browser request
That's it no more iframes!!!
Hope this helps!
UPDATE: Use DNSCrypt is the best solution 😁
OLD ANSWER
Im using this method
Find resource that contain iframe code (use chrome dev tool)
Block the url with proxy or host file
I'm using linux, so i edited my hosts file on
/etc/hosts
Example :
127.0.0.1 ibnads.xl.co.id

Javascript frame killer that still allows frames from own domain?

We are implementing clickjacking protection using the X-Frame-Options header and possibly the JS/CSS setup described on this page:
https://www.owasp.org/index.php/Clickjacking_Defense_Cheat_Sheet
For reference, the JS/CSS solution looks like this:
<style id="antiClickjack">body{display:none !important;}</style>
<script type="text/javascript">
if (self === top) {
var antiClickjack = document.getElementById("antiClickjack");
antiClickjack.parentNode.removeChild(antiClickjack);
} else {
top.location = self.location;
}
</script>
Given the widespread support of the X-Frame-Options header in all modern browsers, I'm wondering if we should even keep the JS frame killer in place. Maybe just for good measure? If we do, we have the caveat that we use colorbox to generate iframe popups from within our own domain. This can be accommodated in the X-Frame-Options header by setting it to SAMEORIGIN, but I'm at a loss as to how I would modify the JS to allow frames from the same domain without it being easily circumvented.
As it is, the script compares self === top and removes the display:none; from the body style if they are the same. I can modify the condition to also check for the domain like so:
if (self === top || this.top.location.hostname === 'www.example.com')
But this uses the location object which can be changed from the framing page, effectively bypassing the script. Basically, I'm looking for something similar to this downvoted suggestion here:
https://stackoverflow.com/a/21900420/998048
So, 2 questions:
1.) Is it still considered best practice or even necessary to implement frame killing with JS?
2.) If so, is there a better, more secure way to keep the JS in place but still allow frames from our own domain?
I still use the Javascript method because my setup doesn't allow me to use server side languages.
But this uses the location object which can be changed from the
framing page
How so? If the location object is changed, your browser will redirect you to the new location object. If location.hostname was changed in the top window, it would redirect to your website. However I'd recommend using parent rather than top - otherwise all frames within other frames may redirect to themselves instead.
If the user has JS disabled, however, none of this will happen, so it may be a good idea to keep using X-Frame-Options. But for browsers that don't care about the X-Frame-Options header, keep the JS solution around I'd say.

Categories

Resources