Redirect a http request to a website from within an iframe with a Reverse Proxy in front of the site - html

I am using nginx as a Reverse Proxy in front of a website and intercept download/preview requests for the files stored on the site. The download requests come from within an iframe and if the user is not authorised, I redirect them to the logout page. But this does not take the main page (outside of the iframe) to the logout page. Any idea how to go about this?

If the user is not authorised you may want to send a message from the iframe to its parent. On receiving the message you would then redirect the parent window to the logout page. An example implementation is found here.
However this becomes much harder if you are not able to modify the iframe page's source. Since you are using nginx, one solution would be script injection using the ngx_http_sub_module module. This module replaces one string in the response with another. Note that this module is not included by default, you may need to build nginx with the --with-http_sub_module parameter. See the module page for more information, including an example.
The iframe needs the line:
parent.postMessage( "redirect", "http://www.your-domain.com" );
To inject this with nginx you might try:
location / {
sub_filter '</head>' '<script language="javascript">parent.postMessage( "redirect", "http://www.your-domain.com" );</script></head>';
sub_filter_once on;
}
The parent window would need the corresponding code:
var eventMethod = window.addEventListener ? "addEventListener" : "attachEvent";
var eventer = window[ eventMethod ];
var messageEvent = eventMethod == "attachEvent" ? "onmessage" : "message";
// Listen to message from child window
eventer( messageEvent, function( e ) {
// normally if the message was meant to come from your domain
// you would check e.origin to verify that it's not someone
// sending messages you don't want
if ( e.data = "redirect" ) {
window.location.replace( "your-logout-url" );
}
}, false );
A more advanced solution might include the redirect url in the message; you could then handle the iframe redirecting to different locations.

Related

How to make "Pretty" URL after dynamic content load using ajax

I am currently developing a website that will dynamically load the page content using ajax triggered by hash changes.
The code looks like this
$("*").delegate("a", "click", function () {
// Trigger Hash Change
window.location.hash = $(this).attr("href");
return false;
});
$(window).bind('hashchange', function () {
let newHash = window.location.hash.substring(1);
$("#main-content").load(newHash + " #ajax-content", function (responseTxt, statusTxt, xhr) {
}).hide().fadeIn();
});
Basically what I am working on now is making the URL look "Pretty", I have modified the .htaccess file to remove the .html extension
So a URL that looks like this
www.example.com/about.html
will become this
www.example.com/about
If I navigate the index (home) "www.example.com" page of the website and then navigate from there to the about page, the URL looks fine. "www.example.com#about" since the server does not display the "index" in the URL.
However, if I navigate straight to the about page like this www.example.com/about, then from the about page to another page, for example, the contact page. I get a URL that looks like this www.example.com/about#contact. When it should look like this www.example.com#contact.
My question is what is the best way to handle this? should I use jquery to redirect all to the index page and then add the hash to load the correct content? or is there some way I can not display the unnecessary part of the URL?
I hope my question was clear, I'm new to the server-side stuff involving the .htaccess file. FOr the ajax stuff I was following this tutorial from CSS tricks
https://css-tricks.com/video-screencasts/85-best-practices-dynamic-content/
You can use history.pushState
window.history.pushState("object or string", "Title", "/new-url");
The url will be www.example.com/new-url
in fact you can get history.state after use this method.
console.log(window.history.state)
output should be "object or string"
You can see the docs here.
Remember to use / to override the entire path.
To do what i think that you want, you can just override the url to / and set the hash.
This is probably not the best way to do this, but I have managed to redirect any page to the home page and then replace the / with the hash value so that the site wont end up wit "messy" URLs.
if(window.location.pathname != "/home.html")
{
window.location.replace("home.html" + window.location.pathname.replace("/", "#"));
}
what happens id the user navigates to "*www.example.com/about*" they will actually be sent to the homepage with the #about.html. So the never end up like this "*www.example.com/about#about*"

How can I pass a parameter to a GET web request without it being visible in the URL?

I am building a web application Java Spring MVC, JSPs, and jQuery.
I have a URL like this:
http://myserver/myapp/showuser/55
Which shows the "view user" page (for user id '55').
I also have an "edit user" page and after successful edit, I redirect to the "view user" page by going here:
http://myserver/myapp/showuser/55?successMsg=Successfully edited User.
The "success message" is parsed from the URL parameters and displayed on the screen.
It works well, but ...
QUESTION: How can I pass the "successMsg" value to the GET without making it part of the URL itself?
You can use btoa() and atob() to convert to and from base64 encoding.
For Plain JavaScript Web solution:
You can use History pushState method to do that.
History's state is similar to params but it is hidden from the URL.
To do that, simply:
const state = { successMsg: 'Successfully edited User'};
const title = '';
const url = 'myserver/myapp/showuser/';
history.pushState(state, title, url);
In your Show User page, access the state like
console.log(history.state) // { successMsg: 'Successfully edited User'}

How to intercept request in Puppeteer before current page is left?

Usecase:
We need to capture all outbound routes from a page. Some of them may not be implemented using link elements <a src="..."> but via some javascript code or as GET/POST forms.
PhantomJS:
In Phantom we did this using onNavigationRequested callback. We simply clicked at all the elements defined by some selector and used onNavigationRequested to capture the target url and possibly method or POST data in a case of form and then canceled that navigation event.
Puppeteer:
I tried request interception but at the moment request gets intercepted the current page is already lost so I would have to go back.
Is there a way how to capture the navigation event when the browser is still at the page that triggered the event and to stop it?
Thank you.
You can do the following.
await page.setRequestInterception(true);
page.on('request', request => {
if (request.resourceType() === 'image')
request.abort();
else
request.continue();
});
Example here:
https://github.com/GoogleChrome/puppeteer/blob/master/examples/block-images.js
Available resource types are listed here:
https://github.com/GoogleChrome/puppeteer/blob/master/docs/api.md#requestresourcetype
So I finally discovered the solution that doesn't require browser extension and therefore works in a headless mode:
Thx to this guy: https://github.com/GoogleChrome/puppeteer/issues/823#issuecomment-467408640
page.on('request', req => {
if (req.isNavigationRequest() && req.frame() === page.mainFrame() && req.url() !== url) {
// no redirect chain means the navigation is caused by setting `location.href`
req.respond(req.redirectChain().length
? { body: '' } // prevent 301/302 redirect
: { status: 204 } // prevent navigation by js
)
} else {
req.continue()
}
})
EDIT: We have added helper function to Apify SDK that implements this - https://sdk.apify.com/docs/api/puppeteer#puppeteer.enqueueLinksByClickingElements
Here is whole source code:
https://github.com/apifytech/apify-js/blob/master/src/enqueue_links/click_elements.js
It's slightly more complicated as it does not only need to intercept requests but additionally also catch newly opened windows, etc.
I met the same problems.Puppeteer doesn't support the feature now, actually it's chrome devtool that doesn't support it. But I found another way to solve it, using chrome extension. Related issue: https://github.com/GoogleChrome/puppeteer/issues/823
The author of the issue shared a solution
here. https://gist.github.com/GuilloOme/2bd651e5154407d2d2165278d5cd7cdb
As the doc says, we can use chrome.webRequest.onBeforeRequest.addListener to intercept all request from the page and block it if you wanna do.
Don't forget to add the following command to the puppeteer launch options:
--load-extension=./your_ext/ --disable-extensions-except=./your_ext/
page.setRequestInterception(true); The documentation has a really thorough example here: https://github.com/GoogleChrome/puppeteer/blob/master/docs/api.md#pagesetrequestinterceptionvalue.
Make sure to add some logic like in the example (and below) they avoid image requests. You would capture it and then abort each request.
page.on('request', interceptedRequest => {
if (interceptedRequest.url.endsWith('.png') ||
interceptedRequest.url.endsWith('.jpg'))
interceptedRequest.abort();
else
interceptedRequest.continue();
});

detect iframe load error

I am loading a user-selected page into an iframe using the src property. If the load fails, I would like to report the problem in terms that will make sense to the user. iframe does not, in general, support onerror according to http://www.w3schools.com/jsref/dom_obj_frame.asp.
The page may come from the user's domain, not mine, so I cannot view the content of the iframe.
I can set a timeout and cancel it from my onload handler if the load is successful, but it would need to be a long timeout to avoid false error reports, and meanwhile Safari on my iPhone has displayed an alert that may confuse the user. Even this does not work for the Kindle Fire browser - it delivers a load event to my handler regardless of whether the load was successful.
Is there any event I can use to detect failure? Is there any way to suppress the default Safari behavior? Any way I can tell whether the load attempt has failed? (If I could do that, I could use a shorter timeout and poll until the load attempt is resolved).
I can require the use of up to date browsers, but would like a solution that is portable among as many smartphones and tablets as possible.
I have tested the AJAX Get idea, and it unfortunately does not work. A cross-domain AJAX Get to an arbitrary URI results in an exception, regardless of whether the target exists and can be loaded into the iframe or not.
You could set your iframe and/or ajax request to always call a page you control (ie: loader.php), sending loader.php the user's requested page via get. From loader.php, use curl or even just file_get_contents to fetch the external page. If the request fails to come back to loader.php, you can check the error there, and return whatever you want your iframe to display.
While my example references the use of php, curl is supported in a variety of scripting languages. It is likely more complicated than other solutions you might have, but would give you access to the response headers as well for troubleshooting why a page load failed.
As you've hinted, you'll face same-origin-policy type restrictions when you try to query anything inside the iframe if it's on a separate domain.
You could make an AJAX GET request to the iframe's URL before you pass it into the src of the frame. If you don't get an HTTP 200 response back from the AJAX call, then the site won't be able to load inside the frame either.
This will add overhead to the whole process, and is only useful if you're checking whether the iframe's document is a real URL that works. It won't help if you need to know when the iframe document has fully loaded.
If you need to know when the iframe has loaded, and it's on an external domain, then I believe you have no other option but to ask for some code to be added to those external sites to notify the parent page that they've loaded successfully.
Or, if it makes sense to do so, ask the end user to click a link to flag up that the content isn't loading correctly.
Late to the party, but I've managed to crack it:
At first, I thought to do an AJAX call like everyone else, except that it didn't work for me initially, as I had used jQuery. It works perfectly if you do a XMLHttpRequest:
var url = http://url_to_test.com/
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status != 200) {
console.log("iframe failed to load");
}
};
xhttp.open("GET", url, true);
xhttp.send();
Edit:
So this method works ok, except that it has a lot of false negatives (picks up a lot of stuff that would display in an iframe) due to cross-origin malarky. The way that I got around this was to do a CURL/Web request on a server, and then check the response headers for a) if the website exists, and b) if the headers had set x-frame-options.
This isn't a problem if you run your own webserver, as you can make your own api call for it.
My implementation in node.js:
app.get('/iframetest',function(req,res){ //Call using /iframetest?url=url - needs to be stripped of http:// or https://
var url = req.query.url;
var request = require('https').request({host: url}, function(response){ //This does an https request - require('http') if you want to do a http request
var headers = response.headers;
if (typeof headers["x-frame-options"] != 'undefined') {
res.send(false); //Headers don't allow iframe
} else {
res.send(true); //Headers don't disallow iframe
}
});
request.on('error',function(e){
res.send(false); //website unavailable
});
request.end();
});

Appcache for dynamic site

I am trying to use HTML5 Appcache to speed up my web-mobile app by caching images and css/JS files. The app is based on dynamic web pages.
As already known – when using Appcache the calling html page is always cached -> bad for dynamic websites.
My solution - Create a first static page and in this page call the manifest file (manifest="cache.appcache") and load all my cached content. Then when the user is redirected to another dynamic page the resources will already be available. (Of course this second dynamic page will not have the manifest tag).
The problem is that if the second page is refreshed by the user, the resources are not loaded from the cache; they are loaded directly from the server!
This solution is very similar to using an Iframe on the first dynamic file. I found that the Iframe solution have the exact same problem.
Is there any solution for that? Can Appcache really be used with dynamic content?
Thanks
Yes appcache can be used for dynamic content if you handle you url parameters differently.
I solved this by using local storage (I used the jquery localstorage plugin to help with this).
The process is
Internally from the page when you would normally href from an anchor or redirect, instead call a function to redirects for you. This function stores the parameters from the url to localstorage, and then only redirects to the url without the parameters.
On the receiving target page. Get the parameters from localstorage.
Redirect code
function redirectTo(url) {
if (url.indexOf('?') === -1) {
document.location = url;
} else {
var params = url.split('?')[1];
$.localStorage.set("pageparams", params);
document.location = url.split('?')[0];
};
}
Target page code
var myParams = GetPageParamsAsJson();
var page = myParams.page;
function GetPageParamsAsJson() {
return convertUrlParamsToJson($.localStorage.get('pageparams'));
}
function convertUrlParamsToJson(params) {
if (params) {
var json = '{"' + decodeURI(params).replace(/"/g, '\\"').replace(/&/g, '","').replace(/=/g, '":"') + '"}';
return JSON.parse(json);
}
return [];
}
I had a hell of a time figuring out how to cache dynamic pages accessed by a URI scheme like this:
domain.com/admin/page/1
domain.com/admin/page/2
domain.com/admin/page/3
Now the problem is that the appcache won't cache each individual admin/page/... unless you visit it.
What I did was use the offline page to represent these pages that you may want to allow a user to access offline.
The JS in the offline page looks up the URI and scrapes it to find out which page it should show and fetches the data from localStorage which was populated with all the page data when the user visited the admin dashboard before being presented with the links to each individual page.
I'm open to other solutions but this is all I could figure out to bring a bunch of separate pages offline with only visiting the single admin page.