views:

264

answers:

5

Hi guys. I'm writing an authentication script in PHP, to be called as an API, that needs to return 200 only in the case that it approves the request, and 403 (Forbidden) or 500 otherwise.

The problem I'm running into is that php returns 200 in the case of error conditions, outputting the error as html instead. How can I make absolutely sure that php will return an HTTP 500 code unless I explicitly return the HTTP 200 or HTTP 403 myself? In other words, I want to turn any and all warning or error conditions into 500s, no exceptions, so that the default case is rejecting the authentication request, and the exception is approving it with a 200 code.

I've fiddled with set_error_handler() and error_reporting(), but so far no luck. For example, if the code outputs something before I send the HTTP response code, PHP naturally reports that you can't modify header information after outputting anything. However, this is reported by PHP as a 200 response code with html explaining the problem. I need even this kind of thing to be turned into a 500 code.

Is this possible in PHP? Or do I need to do this at a higher level like using mod_rewrite somehow? If that's the case, any idea how I'd set that up?

Thanks for any help.

  • Jake
+2  A: 

Simply send the status code as a response header():

header('HTTP/1.1 500 Internal Server Error');

Remember that when sending this there must not be any output before it. That means no echo calls and no HTML or whitespace.

BoltClock
Also remember that this won't work if running as CGI, or under IIS.
Ignacio Vazquez-Abrams
Thanks - I know how to send the response code, and I know not to send output before it, as I mentioned in my question. My question is how to prevent PHP from sending a 200 response code with the error as html in the case of errors.
Jake
Send it then output the error message? Sending a status header tells PHP to override the default `200 OK` response code.
BoltClock
BoltClock: How would you know what header to send in case of unhandled exceptions?
Thomas Ahle
A: 
header($_SERVER['SERVER_PROTOCOL'] . ' 403 Forbidden');

You should not use 500, that indicates an internal server error.

This (and other headers) should be sent before any ouput, except if you have output buffering enabled.

Artefacto
Thank you, but that isn't my question. I know how to send the response code. How do I prevent PHP from sending the 200 code in the case of errors?
Jake
@Jake If send the 403 response, it won't send 200. Am I missing something?
Artefacto
The problem is that if it runs into an error condition before I know whether to send a 403 0r 200, it sends a 200 with the error. I want the opposite behavior - I want the default in the case of an error to be 403 or 500 or pretty much anything other than 200, so that only I send a 200 response code.I'll try writing the 403 code right away, and then overwriting that with a 200 if it succeeds. I don't know what the behavior is supposed to be in this case though. Does anyone know? Will it correctly overwrite the 403?
Jake
+1  A: 

Use output buffering to allow you to modify the headers after writing to the body of the page. You can set this in the ini file rather than updating every script.

(Note the header() call will still fail if you explicitly flush the output buffer)

C.

symcbean
+1  A: 

On the php page for set_error_handler() you can find a comment by smp at ncoastsoft dot com posted on 08-Sep-2003 10:28 which exlpains how to even catch fatal errors (which you can normally not catch with a custom error handler. I changed the code for you needs:

error_reporting(E_ALL);
ini_set('display_errors', 'on');

function fatal_error_handler($buffer) {
    header('HTTP/1.1 500 Internal Server Error');
    exit(0);
}

function handle_error ($errno, $errstr, $errfile, $errline){
    header('HTTP/1.1 500 Internal Server Error');
    exit(0);
}

ob_start("fatal_error_handler");
set_error_handler("handle_error");

//would normally cause a fatal error, but instead our output handler will be called allowing us to handle the error.
somefunction();
ob_end_flush();

This shold catch the fatal error of the non existing function. It than returns a 500 and stops the execution of the rest of the script.

Kau-Boy
+1 Great answer. This is surely the correct solution to the problem.
Thomas Ahle
A: 

I checked the PHP docs for header(), and it's simpler than I was making it - if the second parameter is true, it will replace a similar header. the default is true. So the correct behavior is header ('HTTP/1.1 403 Forbidden');, then do the authentication logic, then if it authenticates, do header ('HTTP/1.1 200 OK'). It will replace the 403 response, and will guarantee that 403 is the default.

Jake