views:

186

answers:

7

Hi

I'm developing a website, and due to user-input or by other reason, I need to show some error messages. For this, I have a page named error.php, and I get the error number using $_GET. All error messages are stored in a array.

Example:

header( 'Location: error.php?n=11' );

But I don't want the users to the enter the error code in the URL and see all the other error messages. For preventing that, I thought I could whitelist the referer page, and only show the error message if the referer is found in my whitelist.

It should be fair similar to this (haven't tested yet ;) )

$accept = false;
$allowedReferer = array (0=>'page1.php', 'page2.php');
if (in_array($_SERVER['HTTP_REFERER'], $allowedReferer )) {$accept = true;}
if ($accept) { $n=$_GET['n'];echo "Error: " . $errorList[$n];}

Is this method good enough to avoid the spy-users?

I'm doing this with PHP5

Thanks

+8  A: 

No, it isn't remotely secure: the HTTP Referer header is trivial to spoof, and is not a required header either. I suggest you read this article for an example of exploiting code (written in PHP), or download this add-on for Firefox to do it yourself from the comfort of your own browser.

In addition, your $allowedReferer array should contain full URL's, not just the script name, otherwise the code will also be exploitable from remote referrals, e.g. from

http://www.example.org/page1.php

To summarise: you cannot restrict access to any public network resource without requiring authentication.

David Grant
+1  A: 

HTTP_REFERER can be spoofed trivially by those with enough incentives (telnet is the tool of choice there), and shouldn't be trusted.

Error messages should never reveal anything critical anyhow, so I'd suggest you to design your error messages in such a way that they can be showed to anyone.

That, or use random hashes to identify errors (instead of 11, use 98d1ud109j2, etc), that would be stored in a central place in an associative array somewhere:

$errors[A_VERY_FATAL_ERROR] => "308dj10ijd"
Henrik Paul
+4  A: 

You shouldn't use an external redirect to get to an error page. How I structure my PHP is like this:

I have a common file that's included in every page, with common functions: handle login/logout, set up constants and the like. Have an error() function there you can pass error information to that will show an error page and exit. An alternative is to use the index.php?include=pagename.php idiom to achieve common functionality but I find this far more flaky and error prone.

If you externally redirect the client (which you obviously need to do sometimes) never rely on the information passed via that mechanism. Like all user input it's inherently untrustworthy and should be sanitized and treated with extreme caution. Don't use cookies either (same problem). Use sessions if you need to persist information between requests.

cletus
A: 

Instead of redirecting to an error page why not include an error page. You can restrict access to a directory containing the php files that contain the error content with .htaccess:

RedirectMatch 404 ^error-pages/*$

and inside the error-pages you can have include-able pages which display errors.

With this method you can be sure that no one can directly access the pages in the error-pages directory yet you can still include them within scripts that are publicly accessible.

Stacey Richards
+5  A: 

Rather than redirect, you could simply display the error "in place" - e.g. something as simple as adapting your present code with something like

if ($error_condition)
{
    $_GET['n']=11;
    include "/path/to/error.php";
    exit;
}

In practice it might be a little more sophisticated, but the idea is the same - the user is presented with an error message without redirecting. Make sure you output some kind of error header, e.g. header("HTTP/1.0 401 Bad Request") to tell the browser that it's not really seeing the requested page.

If you do want to redirect, then you could create a "tamperproof" URL by including a hash of the error number with a salt known only to your code, e.g.

$n=11;
$secret="foobar";
$hash=md5($n.$secret);
$url="http://{$_SERVER['HTTP_HOST']}/error.php?n={$n}&hash={$hash}";

Now your error.php can check whether the supplied hash was correctly created. If it was, then in all likelihood it was created by your code, and not the user.

Paul Dixon
+1 for the salted hash.
David Grant
+1 for including instead of redirecting.
Gumbo
A: 

If you handle errors before sending headers, you can easily create a function that outputs a basic html page with content, and exit right after it. That way there is no specific need for any other page (apart from the functions page, I guess).

It's as simple as checking if there's a problem, and if there is a problem, just call the function.

I use a function like this that even writes data away when it is called, so I have my own error logs...

Jasper
+1  A: 

Why don’t you just include the error message script? And to get rid of previous output data, use the output control to buffer it and clear it on error:

if ($error) {
    ob_clear();
    $errorCode = 11;
    include 'error.php';
    exit;
}
Gumbo