For example, here:
<?php
session_start();
if (!isset($_SESSION['is_logged_in'])) {
header("Location: login.php");
die();
}
?>
<Some HTML content>
Is die() really necessary here ?
Is die() really necessary here ?
It is: Otherwise, the client will still get the HTML code in the response body. The header asks the client to terminate and go to the new page, but it can't force it.
The client can always continue listening to the response, and receive everything output afterwards, which is a fatal security hole e.g. when protecting sensitive data in a login area.
Yes, die() is necessary. A call to header("Location: some-location.php") sends the specified header (a 302 redirect in this case) to the browser; but it DOES NOT terminate the script. It becomes more important if the lines after the redirect statement contains PHP code which may execute unintentionally. So if want to send the redirect header and abort any further processing you must call die, exit, return or any other similar construct.
Note that it is possible to perform further processing after sending the redirect header.
Yes. Simply generating a header, even the Location header, does not terminate the current script. The HTML output will be visible in e.g. a packet sniffer.
I found that: http://www.figured-it-out.com/figured-out.php?sid=181
So according to this it seems that some browsers just stop receiving the html content and redirect directly to the new page where other browsers like IE still wait untill the loading of the page is ready.
Related
I need to redirect users if I don't want them to be able to access a certain page. How reliable is header('Location: ../acc/login.php'); for example? Can browsers ignore 302 errors, and is this the right way? Thanks in advance!
It depends a lot what you're trying to do. Technically spoken, header() is somewhat reliable. Only somewhat, because many PHP users have problems with it and to not get it to work.
PHP will prevent it from working if output has been already send to the browser. A drastic example:
<protected page content here>
<?php
header('Location: login-first.php');
exit();
?>
This would not work at all. You would eventually see even an error message with a warning.
Headers - by design - need to be send out before any other content (response body). They can not be send any longer if the response body has already started and PHP can't help you then in that case.
However, if you send headers before the response body, that function will work. Also the risk obviously to mess something up is not that drastic any longer, too:
<?php
header('Location: login-first.php');
exit();
?>
<protected page content here>
You can rely on header(), but make sure you called die(), exit() or return after that. Otherwise, script will continue its execution, which is potential security issue.
The browser can ignore header('Location: '); forwarding.
That is why you should always return after a call to a header() forward so the rest of your code does not execute should the browser not honor the forwarding.
It is the correct way to do things tho.
I would send the header command and then the exit command "exit()" (to stop running the php code on the server) before displaying the rest of the page. This way the user would never be sent the page content even if they ignored the 302 redirection.
And yes the user can ignore the 302 redirection:
http://www.webmasterworld.com/html/3604591.htm
header is 100% reliable.
However header('Location: ../acc/login.php') will be evaluated in the browser to a real location on your website, and ../acc/login.php wil not form a url that is valid!
I have two PHP scripts which both have an "include_once('authentication.inc');" script near the top. Both scripts reference the same authentication file. That authentication file currently performs a header redirect (like "header('Location: index.php');") if the user is not signed in.
In one file (A.php) the immediate next line of code after the include of the authentication file is:
if(isset($_GET['delete']))
mysql_query("DELETE FROM table WHERE index=".$_GET['delete']);
In the other file (B.php) there are several other includes which occur before the same "delete code" listed above.
So the authenticate.inc file looks like:
if(!valid_credentials($username,$password))
header('Location: index.php');
And file A.php looks like:
include_once('authenticate.inc');
if(isset($_GET['delete']))
mysql_query("DELETE FROM table WHERE index=".$_GET['delete']);
And file B.php looks like:
include_once('authenticate.inc');
include_once('other.php');
include_once('file2.php');
include_once('onemore.php');
if(isset($_GET['delete']))
mysql_query("DELETE FROM table WHERE index=".$_GET['delete']);
Yet when I call A.php?delete=5, that record is deleted from the database while when I call B.php?delete=8 that record is not.
I have checked the 3 intermediary includes and do not see any die() statements, nor any other header redirects.
So while it's clear that A.php is continuing to execute after the header is sent, why isn't B.php doing the same thing? Is the header being sent before the next set of imports?
**
Also: I know to add the die() or exit command after the headers are sent. I'm working on someone else's code and trying to explain behavior, not writing this myself.
**
No way to tell. If the starts are aligned properly, the header coud be sent to the client browser immediately and the bowser will start closing the current connection and request the new URL immediately. This'll cause the current PHP script to start shutting down.
On the other hand, if the caches are slow and the network glitchy, the client browser may not get the redirect header for seconds/minutes/hours, and the script could continue executing indefinitely.
In general you should assume that the moment you've issued a header redirect that the script is basically "walking dead" and should not do any further work.
The sole exception to this rule is that you CAN use ignore_user_abort(TRUE), which tells PHP to NOT shut down when the remote user disconnects. That'd allow you to continue on working even though the browser has shut down the connection and moved on to the new page.
Update your authenticate.inc file to die() after the redirect. This will prevent any other code from executing.
if(!valid_credentials($username,$password)) {
header('Location: index.php');
die();
}
Without it, and depending upon your server configuration, the rest of the PHP code will be executed on the server even after the headers are transmitted back to the client. Until the client closes the connection, the code will run.
Just put an exit() after the header redirect. It will stop all execution after the redirect.
There is probably some output in either of the included files, with echo or other outputting functions. If the browser by then has followed the redirect and aborted the connection, the PHP script will by default exit. You can change this behaviour with ignore_user_abort(true);. You should however use die(); after the Location header. If the query execution is wanted, just put that query before the Location header. Don't forget to use proper escaping for the input, otherwise the script could be a target for a mysql injection attack.
To answer your question, it seems that the browser will wait until your script finished execution and only then will request another location.
Please note that you shouldn't use GET method to delete records.
As for the not deleting id=8 - just debug it. Not a big deal.
A good var_dump() is always better than some vague ideas about headers.
<?
echo "lalala";
header("Location: http://www.google.com/");
If i put this in a plain php file and deliver over a standard apache2 server with mod-php (PHP Version 5.3.2-1ubuntu4.10) the redirect to google works.
<?
echo "lalala";
flush();
header("Location: http://www.google.com/");
this code does obviously not produce a working redirect.
My question is how the first code is beeing processed and why it works. Because I remember times when things like this were not possible. Is mod-php or apache intelligent enough to buffer the whole request and arrange headers before content?
And:
Can I rely on this if I make sure I don't flush the output manually? Because it would make my application much easier...
Output buffering is probably enabled by default. You should enable it manually if you want to rely on this functionality.
http://php.net/manual/en/function.ob-start.php
The header function ADDS an http common header to the HTTP response. So, the redirect is setted and the browser gets the 302 message before showing you the output.
flush orders php to send the http response already prepared at the point it is called. That's why the second code won't set the header (it must be setted before sending ANY output).
And, the PHP should not output a single thing until:
The script is processed (even if an error stops the parsing)
you set it to send the output somewhere in the script with flush()
Finally, check this on output control http://www.php.net/manual/en/intro.outcontrol.php
So I know the general rule of thumb is after doing a header redirect in PHP, you should call exit() to avoid having extra code running, but I want to know if you put code after the redirect header, if it will always run?
I was doing some research on various ways of tracking referrals in Google Analytics and came across this post: Google Analytics Tips & Tricks – Tracking 301 Redirects in Google Analytics
It recommends doing something like this:
<?
Header( “HTTP/1.1 301 Moved Permanently” );
Header( “Location: http://www.new-url.com” );
?>
<script type=”text/javascript”>
var gaJsHost = ((“https:” == document.location.protocol) ? “https://ssl.” : “http://www.”);
document.write(unescape(“%3Cscript src=’” + gaJsHost + “google-analytics.com/ga.js’ type=’text/javascript’%3E%3C/script%3E”));
</script>
<script type=”text/javascript”>
try {
var pageTracker = _gat._getTracker(“UA-YOURPROFILE-ID”);
pageTracker._trackPageview();
} catch(err) {}</script>
From the way I've always understood the header() function, it's up to the browser and it can run the redirect whenever it wants to. So there's no guarantee the JavaScript would actually begin or finish executing prior to the redirect occurring.
PHP's documentation for the header() function indicates the reason for exiting after a redirect is to "make sure that code below does not get executed when we redirect." That doesn't sound like they guarantee all following code will run, just that it could happen.
Regardless, I found a different way to actually manage the tracking, but I wanted to see if I could find out how exactly header() worked in this situation..
Thanks for your help.
Using the header function in PHP only adds to the headers of the response returned by the server. It does not immediately send any data and does not immediately terminate the connection. Any code after the header call will be executed.
In particular, it's a good idea to add a response body even after doing a 301 redirect so that clients that do not support the redirect also get some descriptive response. Infact according to the HTTP 1.1 specification Section 10.3.2 -
Unless the request method was HEAD, the entity of the response SHOULD
contain a short hypertext note with a hyperlink to the new URI(s). If
the 301 status code is received in response to a request other than
GET or HEAD, the user agent MUST NOT automatically redirect the
request unless it can be confirmed by the user, since this might
change the conditions under which the request was issued.
It's a race condition. Once the redirect header is sent to the browser, the browser will close the current connection and open a new one for the redirect URL. Until that original connection is closed and Apache shuts down the script, your code will continue to execute as before.
In theory, if there was a sufficiently fast connection between the client/server, and there was no buffering anywhere in the pipeline, issuing the header would cause the script to be terminated immediately. In reality, it can be anywhere between "now" and "never" for the shutdown to be initiated.
The HTML after your Location line doesn't run inside PHP; it would run in the browser. It's up to the browser whether or not to execute the Javascript that you've included on that page; PHP has nothing to do with it.
To me, the PHP docs imply that any PHP below the header() when you send a redirect will continue to run. But it 'runs' in the PHP interpreter, dumping JS to the browser. There's no relation between what it says in the PHP docs and whether or not the JS gets run by the browser.
EDIT:
Well, as Anupam Jain pointed out, looks like that browsers do not terminate connection without getting the response body and it sounds sensible. So i rethinked my answer
That doesn't sound like they guarantee all following code will run
Exactly More likely it's a warning in case there is some sensible code that shouldn't be executed. A private page contents for example. So, beside sending header you have to also make sure that no sensitive content were sent and exit looks like quite robust solution. So, I'd phrase it as "make sure that sensible code below does not get executed when we redirect."
So there's no guarantee the JavaScript would actually begin or finish executing prior to the redirect occurring.
Exactly It seems it has nothing to do with script execution but rather with browser's will to execute anything after getting 3xx response. I think I'm gonna test it, but you can test it as well too.
I have noticed that the code does still execute and multiple headers based on if statements can cause a "redirect loop error". i made it a habit to now add in die("Redirecting..."); after every header redirect and have not see the problem persist.
Quick question, I noticed that on some of my header directors I was getting some lag while the header processed. Is using return standard after using headers? Also if you use a header on pages you don't want directly accessed, such as processing pages will return; stop that processing even if the page is not directly accessed? IF return is a good idea would it be better to use exit()?
header("Location: ......"); exit; is a fairly common pattern.
You do not need to supply return; after calling header but I know some people use the convention of supply exit; after header call to ensure the code below will not execute during a redirect.
Keep in mind you can use header() for other things besides Location: redirects:
header("Content-type: image/jpeg"); // for example
The reason you would exit after a header redirect is, any content output after a header() redirect, will (most likely) not be seen by the browser.
More importantly you wouldn't want any code to be executed after a header() redirect, so calling exit() after a redirect is good practice.
When you send the header, it is but a mere advisory to the client(the browser) that you think they should request another url instead. However, nothing can stop them from not following your recommendation. They can continue reading more data from the current url, if your server keeps feeding it to them. This is why you generally see php code that calls exit() after sending a redirect header, because if you stop outputting more data, there is nothing for them to read.
Aside from keeping them from reading unintended data, there's other reasons:
Maybe it's just plain senseless for the rest of the script to continue executing, wasting resources.
Maybe runtime errors would occur if the script were to continue(ex, there were missing variables, or a db connection failed).
Maybe logic errors would occur if the script were to continue(ex, user input validation/authentication failed).
It's up to the client to determine what to do after an header("Location: ...").
Any code after header() will be executed regardless. Putting an exit(); just after the header is a safeguard and is required for securing your site.
If you have some candy after header("Location: ..."), the only thing the browser have to do is to ignore the request. Then it'll be clear as day. With exit(); you're stopping execution of the page and hopefully there are no other attack vectors to your app!