i'm working at PHP application but i have a trouble, in fact when a user logged out and press after logging out the back button of the browser he can see the previous page as if the session has not been destroyed :(( i have tried all that i find here and on the web but it doesn't work :'(
Can I disable the back button?
http://blog.priyakant.com/2014/09/23/browser-back-button-prevent/
Summary:
Browser back button – Prevent displaying of previous pages after logout – Cookie based approach
Posted on September 23, 2014 by Priyakant Patel — Leave a comment
Prevent displaying of previous pages after logout
Client browser application caches page for performance reason. In this case when user clicks on back (browser back button) it shows previous page from cache.
Case 1 : User is still logged in.
it is OK to display content of previous page.
Case 2 : User is logged out.
Potentially next user can click on browser back button and can see content(s) of previous page(s).
This could be big problem in many applications. In financial application next user potential can see financial data. Or Medical / Patient related application this could be HIPAA violation and company can face big penalties.
So let’s get back to the point, How can solve this problem?
I am proposing HTTP Cookie based approach.
Steps:
Create HTTP Cookie from server side with sliding expiration. Which can be accessed from Client JavaScript (Note: Browser clears this Cookie upon expiration).
Clear this cookie upon logout
If you don’t find this Cookie, reload the page. In this case server re-authenticates page and if necessary it will redirect to the login page
That’s it, Done!
Here is my implementation using ASP.NET. Implementation will varies based on server technology but idea stays same.
(Server Side). Create HTTP Cookie from server side with sliding expiration
Response.SetCookie(new HttpCookie(“TimeoutCookieName”, "1") { Expires = DateTime.UtcNow.AddMinutes(10) });
//NOTE 10 == Session Timeout. This will be same as your application login session timeout.
(Server Side). Clear this cookie upon logout
Response.SetCookie(new HttpCookie(“TimeoutCookieName”, "1") { Expires = DateTime.UtcNow});
(Client Side) : (Following script must exists immediately after BODY tag)
window.preventBackButton = function () {
try {
if (document && (!document.cookie || document.cookie.indexOf('_tc=1') < 0)) {
window.document.body.style.display = 'none'; window.location = window.location;
}
} catch (e) { }
};
window.preventBackButton(); //Call immediately after body tag
Please find ASP.NET implementation as follow:
////C# Helper class - Start
using System;
using System.Web;
namespace MyHelpers {
public static class MyHtmlHelper {
public const string TimeoutCookieName = "_tc";
public static HtmlString PreventBackButtonScript(HttpResponseBase response) {
response.SetCookie(new HttpCookie(TimeoutCookieName, "1") { Expires = DateTime.UtcNow.AddMinutes(10) });
var clientScript = "window.-reventBackButton = function() {
try {
if(document && (!document.cookie || document.cookie.indexOf('" + TimeoutCookieName + "=1') < 0)) {
window.document.body.style.display='none'; window.location = window.location;
}
} catch(e) {}
};
window.preventBackButton();";
return new HtmlString(clientScript);
}
public static void SafeUnSetTimeoutCookie(this HttpResponseBase response) {
response.SetCookie(new HttpCookie(TimeoutCookieName, "0") { Expires = DateTime.UtcNow.AddYears(-5) });
}
}
}
////C# Helper class - End
//Shared\_Layout.cshtml
//Make sure not to include after logout OR login page
<html>
<body>
#MyHelpers.MyHtmlHelper.PreventBackButtonScript(Response)
.
.
<⁄body>
<⁄html>
You cannot disable the back button. If you can see the previously logged out user's page then your session checking script fails somewhere. Use a process script when you submit the logout form then redirect the currently logged out user to the main page (if applicable).
You can't. Browsers cache pages so they don't have to request it from a web server every time they load a page. When you hit the back button it loads the last page without asking the server.
It's probably more to do with the caching headers you're sending back on each page request. You have content that is only valid for a short time so you need to make sure you send headers back when you generate the page telling the browser not to cache it locally.
Example of disabling the page caching here http://www.w3schools.com/php/func_http_header.asp:
// Date in the past
header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");
header("Cache-Control: no-cache");
header("Pragma: no-cache");
Are you clearing out the cache/session of the user? Even if they hit back I don't think it should keep them logged in if you clear their session on log out.
Edit: Prior to editing - by someone other than OP - this question asked if it is possible to disable the browser's back button. My original answer to that question is below. Also, I feel I need to clarify - the below approaches for essentially "breaking" the back button are not approaches I recommend or like. You should design your application to react sensibly when using basic browser features like the back button rather than try to prevent their use.
You cannot disable the back button on a user's browser. It's a fundamental feature of browsers which can't be overridden.
You can make it so that your application breaks (displays an error message, requiring the user to start over or re-submit a request) if the user goes back. It's a bad idea to do this, because it is really an admission that you didn't take the back button into account when you designed the application. Every application, even order forms, shopping carts etc, if designed correctly should be able to use the back button.
One approach I have seen for breaking on back button use is to pass a token on every URL within the application, and within every form. The token is regenerated on every page, and once the user loads a new page any tokens from previous pages are invalidated.
When the user loads a page, the page will only show if the correct token (which was given to all links/forms on the previous page) was passed to it.
The online banking application my bank provides is like this. If you use the back button at all, no more links will work and no more page reloads can be made - instead you see a notice telling you that you cannot go back, and you have to start over.
That said, I should remind you that making it so your application breaks when the user goes back is a bad idea and shows a poor application design.
Related
My today goal is to make URL unavailable to other registered users if one of the user opened that URL and make it available when this user leaves it. Right now I have only idea how to block it - using cache, but problem is with unblocking as user can go everywhere or even just close the browser. I know that there is option for set cache timer, but in my case this is NOT an option - system must know if there is some user in that URL. Can someone help me with some ideas how to achieve this?
Only thing I can personally think of is using JWT or sessions to check if a logged in user is on that page, then a condition to check if the current user is not equal user that is trying to get on the page, and if it doesn't match just redirect that user to somewhere else.
export const blockAccess = expressAsyncHandler(async (req, res, next) => {
//check if the user is currently on the page
if (req.session.promoPage) {
//if they are, redirect new user's that're trying to access the page to the home page
if (req.session.promoPage.userId !== req.user.id) {
res.redirect('/')
}
}
next() //next here is a function that tells the program to move on to the next middleware function or route etc....
})
I need to somehow detect that the user has pressed a browsers back button and reload the page with refresh (reloading the content and CSS) using jquery.
How to detect such action via jquery?
Because right now some elements are not reloaded if I use the back button in a browser. But if I use links in the website everything is refreshed and showed correctly.
IMPORTANT!
Some people have probably misunderstood what I want. I don't want to refresh the current page. I want to refresh the page that is loaded after I press the back button. here is what I mean in a more detailed way:
user is visiting page1.
while on page1 - he clicks on a link to page2.
he is redirected to the page2
now (Important part!) he clicks on the back button in browser because he wants to go back to page1
he is back on the page1 - and now the page1 is being reloaded and something is alerted like "You are back!"
You can use pageshow event to handle situation when browser navigates to your page through history traversal:
window.addEventListener( "pageshow", function ( event ) {
var historyTraversal = event.persisted ||
( typeof window.performance != "undefined" &&
window.performance.navigation.type === 2 );
if ( historyTraversal ) {
// Handle page restore.
window.location.reload();
}
});
Note that HTTP cache may be involved too. You need to set proper cache related HTTP headers on server to cache only those resources that need to be cached. You can also do forced reload to instuct browser to ignore HTTP cache: window.location.reload( true ). But I don't think that it is best solution.
For more information check:
Working with BFCache article on MDN
WebKit Page Cache II – The unload Event by Brady Eidson
pageshow event reference on MDN
Ajax, back button and DOM updates question
JavaScript - bfcache/pageshow event - event.persisted always set to false? question
Back/Forward Cache
It's been a while since this was posted but I found a more elegant solution if you are not needing to support old browsers.
You can do a check with
performance.navigation.type
Documentation including browser support is here: https://developer.mozilla.org/en-US/docs/Web/API/Performance/navigation
So to see if the page was loaded from history using back you can do
if(performance.navigation.type == 2){
location.reload(true);
}
The 2 indicates the page was accessed by navigating into the history. Other possibilities are-
0:The page was accessed by following a link, a bookmark, a form submission, or a script, or by typing the URL in the address bar.
1:The page was accessed by clicking the Reload button or via the Location.reload() method.
255: Any other way
These are detailed here: https://developer.mozilla.org/en-US/docs/Web/API/PerformanceNavigation
Note Performance.navigation.type is now deprecated in favour of PerformanceNavigationTiming.type which returns 'navigate' / 'reload' / 'back_forward' / 'prerender': https://developer.mozilla.org/en-US/docs/Web/API/PerformanceNavigationTiming/type
Since performance.navigation is now deprecated, you can try this:
var perfEntries = performance.getEntriesByType("navigation");
if (perfEntries[0].type === "back_forward") {
location.reload();
}
jQuery( document ).ready(function( $ ) {
//Use this inside your document ready jQuery
$(window).on('popstate', function() {
location.reload(true);
});
});
This will work 100% when back or forward button has been clicked; also if using ajax.
If it doesn't -- there must be a misconfiguration in a different part of the script.
For example: it might not reload the page if some page (in the previous post) is setting the state to:
window.history.pushState('', null, './');`
so when you do use history.pushState();
make sure you use it properly!
In most cases you will use:
history.pushState(url, '', url);
Not window.history ... and make sure url is defined.
An alternative that solved the problem to me is to disable cache for the page. That make the browser to get the page from the server instead of using a cached version:
Response.AppendHeader("Cache-Control","no-cache, no-store, must-revalidate");
Response.AppendHeader("Pragma", "no-cache");
Response.AppendHeader("Expires", "0");
Currently this is the most up to date way reload page if the user clicks the back button.
const [entry] = performance.getEntriesByType("navigation");
// Show it in a nice table in the developer console
console.table(entry.toJSON());
if (entry["type"] === "back_forward")
location.reload();
See here for source
You should use a hidden input as a refresh indicator, with a value of "no":
<input type="hidden" id="refresh" value="no">
Now using jQuery, you can check its value:
$(document).ready(function(e) {
var $input = $('#refresh');
$input.val() == 'yes' ? location.reload(true) : $input.val('yes');
});
When you click on the back button, the values in hidden fields retain the same value as when you originally left the page.
So the first time you load the page, the input's value would be "no". When you return to the page, it'll be "yes" and your JavaScript code will trigger a refresh.
I tried all the solutions from the previous answers. No one worked.
Finally I found this solution, which did worked:
(function () {
window.onpageshow = function(event) {
if (event.persisted) {
window.location.reload();
}
};
})();
JS Solution That Works On Most Browsers
None of the many other approaches on this page worked for me, perhaps because the "bfcache" is preventing anything from happening when the user navigates back to the page. However, I found that registering a window.onbeforeunload handler works for me in most browsers, and I believe it works because it implicitly invalidates the "bfcache". Here's the code:
window.onbeforeunload = function() {
window.location.reload(true);
}
This event may be triggered in other cases than "back" button navigation, but it my case that doesn't matter. I tested this on the following platforms on recent versions of the listed browsers in August 2021:
Linux: works in Chrome and Firefox.
Android: works in Chrome, Firefox, and Opera.
OS X: works in Chrome and Safari.
iOS: doesn't work in Safari.
In my case I don't really care about mobile. I do care about IE, but don't have access to IE, so I couldn't test it. If someone tries this on IE and can report the result in the comments that would be helpful.
Server Side Response Headers That Fix iOS Safari
I found that iOS Safari also works if I invalidate the browser cache using Cache-Control response header. I.e. sending
Cache-Control: no-store, must-revalidate
fixes iOS Safari. See this SO answer for how to set the Cache-Control response header on various platforms.
Reload is easy. You should use:
location.reload(true);
And detecting back is :
window.history.pushState('', null, './');
$(window).on('popstate', function() {
location.reload(true);
});
I had the same problem, back-button would update the url shown in location field but page-content did not change.
As pointed out by others it is possible to detect whether a change in document.location is caused by back-button or something else, by catching the 'pageshow' -event.
But my problem was that 'pageshow' did not trigger at all when I clicked the back-button. Only thing that happened was the url in location-field changed (like it should) but page-content did not change. Why?
I found the key to understanding what was causing this from: https://developer.mozilla.org/en-US/docs/Web/API/Window/pageshow_event .
It says 'pageshow' -event is caused among other things by "Navigating to the page from another page in the same window or tab" or by "Returning to the page using the browser's forward or back buttons"
That made me ask: "Am I returning to the page, really?". "What identifies a page?". If my back-button did something else than "returning to the page" then of course 'showpage' would not trigger at all. So was I really "returning to a page"? OR was I perhaps staying on the same "page" all the time? What is a "page"? How is a page identified? By a URL?
Turns out me clicking the back-button did NOT "change the page" I was on. It just changed the HASH (the # + something) that was part of my url. Seems the browser does not consider it a different page when the only thing that changes in the URL is the hash.
So I modified the code that manipulates my urls upon clicking of my buttons. In addition to changing the hash I also added a query parameter for which I gave the same value as the hash, without the '#'. So my new URLs look like:
/someUrl?id=something#something
Every page that my app considers to be a "different page" now has a different value for its query-string parameter 'id'. As far as the browser is concerned they are different "pages". This solved the problem. 'Pageshow' -event started triggering and back-button working.
This works in Nov 21 in latest Firefox and Chrome.
window.addEventListener( "pageshow", function ( event ) {
var perfEntries = performance.getEntriesByType("navigation");
if (perfEntries[0].type === "back_forward") {
location.reload();
}
});
Use following meta tag in your html header file, This works for me.
<meta http-equiv="Pragma" content="no-cache">
In Chrome 96 perfEntries[0].type is 'reload', when you use the back button
Here is a version that detects for Safari, and if detected executes the older code that is officially deprecated (but is still in Safari).
let isSafari = navigator.vendor && navigator.vendor.indexOf('Apple') > -1 &&
navigator.userAgent &&
navigator.userAgent.indexOf('CriOS') == -1 &&
navigator.userAgent.indexOf('FxiOS') == -1;
if(isSafari) {
window.addEventListener( "pageshow", function ( event ) {
let historyTraversal = event.persisted ||
( typeof window.performance != "undefined" &&
window.performance.navigation.type === 2 );
if (historyTraversal) {
// Handle page restore.
window.location.reload();
}
});
} else {
let perfEntries = performance.getEntriesByType("navigation")
if (perfEntries[0].type === "back_forward") {
window.location.reload(true);
}
}
I found the best answer and it is working perfectly for me
just use this simple script in your link
next page
or the button click event
<INPUT TYPE="button" onClick="history.go(0)" VALUE="next page">
when you use this, you refresh your page first and then go to next page,
when you return back it will be having the last refreshed state.
I have used it in a CAS login and gives me what I want.
Hope it helps .......
details found from here
I have a web page, let's call it main.php which displays an image of football field and some players distributed on the field. However, that page uses list.php as a right side frame that loads a list of players.
What happens is, when the user clicks on a player on the field (main.php), let's say on the image of the Goal Keeper (GK), a list of GKs from world wide teams will load in right list fram (list.php). This is by using ajax.
So far we are good.
The current situation is, when session times out and the user clicks on a player from the field, the list on the right does not load, instead, list of players disappears from the list and a message says "Please login" is displayed on the right side frame (list.php)
The objective is, when session times out I want the whole website to redirect to the main page index.php
The problem is, I already put the redirecting code just before the code that is responsible of displaying the message "Please login". But what happened is, the redirection happens from within the frame, so i ended up having main.php displaying the field, and list.php displaying the main page!
Here's the code I added.
$user_id = NSession::get('user_id');
if (!isset($user_id))
{
NSession::removeall();
General::Redirect('index.php');
}
They are using Smarty. and btw, I added the same code to top of main.php, and now if user tries to access main.php without logging in, it will redirect him to the main page, so the code works!
n.b. The project is not mine, it belongs to the company I work in.
And I don't know which code is checking the session, all what I know is, if the user click on a player from the field after the session timeout, the "Please Login" message will be shown in the frame.
I'm guessing the redirect is essentially the same as using a header() function. It isn't possible to specify a target using a php redirect as it is server-side - specifying the target is client-side.
You would need to print something like this to the screen:
<script type="text/javascript">window.open('index.php','_parent');</script>
And that will redirect the user to the index.
Using frames for such purpose is... well... so 80ish...
Anyway, the frames are probably named in such a scenario. This means you can address them, but also that you have to address them. Just loading an url inside the "current" frame does exactly that, which is why your approach won't work.
If you really have to go with that frame based approach, then you will have to use javascript to address all known frames and redirect them.
Maybe you can use some javascript inside of your frame like so :
<script type="text/javascript">
window.top.location = 'YourPage.html';
</script>
Hope this helps
The issue was that the session expires while I'm on main.php. Therefore, any subsequent Ajax requested will fail since all requests requires session to be active.
the problem was that the Ajax request being sent from the IFrame (the IFrame is inside main.php and points to list.php thru Ajax calls) is failing due to session expiry.
So I've fixed this issue by adding another two session checks, one on main.php, list.php using PHP (check for session, if it's there, redirect). And in the main container, main.php, I check for the session via JS, interval Ajax requests to check the session, if session has ended, then use redirect using JS.
PHP:
$user_id = NSession::get('user_id');
if (isset($_POST["checklogin"]))//check loging
{
die(isset($user_id) ? "true" : "false");
}
if (!isset($user_id) || $user_id == "")
{
NSession::removeall();
General::Redirect('login.php');
}
JavaScript:
jQuery(document).ready(function($) {
$(window).focus(function() {
checkSession();
});
});
function checkSession()
{
$.ajax({
type: "POST",
data: {"checklogin": "cl"},
url: "list_players.php",
success: function(result) {
if (result === "false")
{
if (FIELD.showMessage === false)
{
FIELD.showMessage = true;
alert("Your session has been closed\nYou will be redirected to login page now. ");
window.location.href = ("login.php");//incase user clicks OK
}
}
}
});
}
So I've got a Backbone application + web homepage. Right now, if you login to my website, I create a global object with your user details from the database. However, you can still just hit one of the routes in the application directly.
How should I handle users who are not "logged in" and redirect them to a "you must login page"?
Is this a standard operation? Basically, I have a REST url setup that returns just
{ sessionId: [php-session-id-here] }
If they are logged in, it would return something more like this:
{
sessionId: [php-sess-id],
userId: [user-id-from-db],
firstName: [f-name],
lastName: [l-name]
}
Ideas? Thanks!
What I've done in the past is to include on every page along with jQuery (actually, added to the jQuery file) an extension on the AJAX method to check for a custom code that I send when a user isn't logged in. When that value was seen it redirected the user to the login page regardless of what was going down.
This was because that site had a time out on login, so a user could get logged out while sitting on a page and then the AJAX request would just fail. If you don't have a timeout on the login the odds of ever seeing this issue are slim. Just ignore requests that come from users that aren't logged in.
If you need help coding this, start here: Extending Ajax: Prefilters, Converters, and Transports.
Really shouldn't require anything as complex as pseudo-code:
JS needs to do some AJAX, so JS talks to server
PHP checks for login if needed
If not logged in, send back the abort message (I used a converter to catch a "notLoggedIn" dataType. However this could also be done with a transport, they are just more complex.)
JS sees the abort message and does a window.location redirect rather than return AJAX message.
If you want, you could load a lightbox with a login form and send that via AJAX to PHP where a re-login can take place, if you remember the AJAX attempt that failed you can send it again after login. Then the user doesn't even need to leave the page to log back in.
If you're using jQuery, you can set a global ajaxSetting that allows you to do certain things upon certain http codes. Some pages I read recommend adding to your JSON a url field to point to where to login, but I figure that's just up to you. So the only modifications you'd need to implement what I've mentioned is 1. change the http code to something reasonable like 401 (unauthorized) and implement the http code handler. But I wouldn't call this standard, I'd just say that's what several people have done (including myself).
<?php
function IsLoggedIn()
{
if(isset($_SESSION['id'])) // Change that to what you want
{
return 1;
}
else
{
return 0;
}
}
?>
Then in your code, you could use something like:
if(isLogged()){ header('Location: http://google.com'); }
Is there anyway for me to make it work so anyone who presses F5 or any refresh button will be moved to a different page, instead of it refreshing the page the user wants?
Something like :
If (refresh){
goto "link to hopme page"
}
If not is there anyway for me to not allow refreshing on a certain page?
I have some people that are are just refreshing non stop and it is killing my bandwidth. It is a game site so I don't want to ban the ip's.
Perhaps you should be focusing your effort instead on reducing the bandwidth your page is using. Explore the areas of image compression, page optimization and caching.
session_start();
if($_SESSION['hasbeenhere'] == 1)
{
// Page refreshed
}
else
{
$_SESSION['hasbeenhere'] = 1;
}
If the person doesn't have cookies enabled, this will fail. If someone goes to another page and comes back, it will shown as refreshed.
Overall, you can't do this in a way that is surefire, but this is 1 way to prevent someone from seeing the same page twice.
Because of your comment, if you want to stop people from pressing F5 200 times, try this.
$page = $_SERVER['REQUEST_URI'];
// Defaults
if(!isset($_SESSION[$page]['count']))
{
$_SESSION[$page]['count'] = 1;
$_SESSION[$page]['first_hit'] = time();
$_SESSION[$page]['banned'] = false;
}
else
{
$_SESSION[$page]['count']++; // Increase the counter
}
// If person is banned, end script
if($_SESSION[$page]['banned'] == true)
{
die();
}
if($_SESSION[$page]['first_hit'] < time() - 30)
{
$_SESSION[$page]['count'] = 1; // Reset every 30 seconds
}
if($_SESSION[$page]['count'] > 100)
{
$_SESSION[$page]['banned'] = true;
// Ban if they hit over 100 times in 30 seconds.
}
Why would you want to?
But no, there's no way that'll work consistently that can stop this.
In your PHP code, do the following:
if(isset($_SESSION["pagename-LAST_VIEWED"])) {
v = $_SESSION["pagename-LAST_VIEWED"])
if(time() - v < 15) {
// user is refreshing more than once per 15 seconds
// send them something else and die
}
}
$_SESSION["pagename-LAST_VIEWED"] = time();
Please ignore my crummy pseudo-PHP, it's not my daily language.
This will prevent both a page refresh (F5) and the user just clicking the bookmark again or pressing Enter in the address bar again.
You could also enable some aggressive caching meta tags and HTTP headers, which will prevent some refreshes from ever hitting your server, but the above code should be pretty scalable.
Another thing to consider: the root problem is your other code, not your users. Consider rewriting your page so it auto-updates the part they want to see via AJAX on a timed delay. This will give them incentive not to use Refresh, and will help your server cope by only having to refresh the small bit of data they want to see updated.
I have no idea if this would work, but could you listen for keystrokes with javascript, and on F5 keypress, do what you want.
I guess you could do this:
session_start();
if (isset($_SESSION['visited'])) {
header("Location: http://the.page/you/want");
} else {
$_SESSION['visited'] = true;
}
In order to stop F5 from refreshing, you'd have to not allow refreshing of any type. As a hack, you could try putting a timestamp in the querystring. On load, you can check the value of the timestamp and if it is in the past, redirect to another page. This would work if cookies are disabled.
The site itself does this with Javascript, so when you're editing something, you don't loose it by refreshing, going back, etc. There's probably some event defined by JQuery that lets you do this, so look into that, and on that event, redirect to the page you want (probably has to be within the same domain).
It seems to me that there is no good technical solution to this problem (the other answers covered that pretty well). If the users are constantly hitting refresh on this page, I'm guessing it's because they expect the page to change and they want up-to-the-second information. Perhaps you should take away their reason for wanting to refresh the page. The easiest way to do this would probably be making the page update itself (likely via AJAX). Thus the user can sit there and updates will just roll in -- no more hammering F5, which will cut down on your bandwidth, not to mention be a better user experience.
I just find out a very simple solution.
For me after i process all the $_POST information and save the ones i need into a $_SESSION var i do this:
$_SESSION['admin']['PID']=session_id();
$_SESSION['admin']['user_user']=$_POST['user'];
$_SESSION['admin']['user_pass']=$_POST['pass'];
$_SESSION['admin']['last_access_time']=time();
if($_SERVER['REQUEST_METHOD']==="POST")
{
header("LOCATION:".$_SERVER['PHP_SELF']."?p=redirected");
}
In my case i was trying to validate the user, and i have the problem that when someone logout and and go back and refresh the site load again, and with this simple if i finally solved it.
PD: ."?p=redirected" is only for testing porpouse
It's not answering your question directly but you can probably solve your issue with caching can't you?
If you send a header
Cache-Control: public,max-age=120
That should instruct the browser that it doesn't need to request the page again for 2 minutes and can just use the copy from it's cache. If course they can use control-f5 to force a refresh but it ought to help a lot?