In one of my websites I use $_SERVER['REQUEST_URI'] to establish whether a non-registered user can see the content of a page or not.
In the manual there is written about $_SERVER['REQUEST_URI']:
The URI which was given in order to access this page; for instance,
'/index.html'.
My question is, is it in any client side way possible for a client to access eg. index.php even though $_SERVER['REQUEST_URI'] contains a different value?
I know that $_SERVER['REQUEST_URI'] contains the page that the client asked and that the server returns but if I don't ask myself these kind of questions once in a while I'm not happy
Also is it considered good practice to use $_SERVER['REQUEST_URI'] in this way?
EDIT: I included the script I use as it was too generic
list($c_page) = explode('.',substr($_SERVER['REQUEST_URI'],1));
define('C_PAGE',$c_page ?: 'index');
define('LOGGED',$_SESSION['user']['id'] ?: 0);
if(in_array(C_PAGE,array('page_1','page_2','page_3')) && !LOGGED){ header('Location: login.html'); exit; }
You should really track their entrance with a session variable (or a cookie). Either of these could be blocked...but they are closer to "foolproof". That said, anything can be forged...so use a combination and/or strong unique strings if security is of the utmost importance.
Depending on the serverside softwarestack you use this variable is set by the webserver or the fastcgi wrapper.
URL rewriting and non-transparent proxies in your hardware/software stack can influence the value you see within your script.
e.g. Nginx could rewrite the URL from /test.html to /index.php?action=test, then pass it to your webserver. User would have called /test.html while your application sees /index.php?action=test
Conclusion: REQUEST_URI is the URI passed to the webserver and can be used as a reference for URL-based access controls.
EDIT:
just to avoid confusion, because I've seen the other responses...
Your question as I understood:
You want to check wether your currently requesting and already password-authorized user has enough permissions to access specific URLs. Again, yes you can use the request uri as a reference value
Personally, I find it more reliable to declare one or more groups (classes) of users that should have access to a file, and then include a page that returns a 401 error if the logged in user if not in any of those groups. e.g.
session_start();
...
$access = 'admin';
include 'inc/guard.php';
Sounds like in your case you want 'public' and 'logged-in' which is slightly different, but is a case also covered by my guard script. There, I simply check that the $_SESSION variable is empty (I insert stuff into it upon log-in):
if($access != 'public' && empty($_SESSION)) {
header('HTTP/1.1 401 Unauthorized', true);
include 'inc/login.php';
exit;
}
Related
How can I check whether a request being received is sent from the same server??
Say, I've my domain at www.domain.com. Now I've php processing files which will process forms hosted through this domain. This processes will be executed only if the requests are sent from within the domain ie. www.domain.com and any other requests sent from other domains will be discarded.
Basically : you cannot.
With the HTTP protocol, each request is independent from the others.
A first idea would be to check the Referer HTTP header, but note that :
It can be faked (it's sent by the browser)
It is not always present.
So : not a reliable solution.
A possible, and far better than the Referer idea, solution could be to use a nonce :
When displaying the form, put a hidden input field in it, containing a random value
At the same time, store that random value into the session that correspond to the user.
When the form is submitted, check that the hidden field has the same value as the one that's stored in session.
If those two values are not the same, refuse to use the submitted data.
Note : this idea is often used to help fight against CSRF -- and integrated in the "Form" component of some Frameworks (Zend Framework, for instance).
this will check if there is a referer, then it will compare it with current domain, if different then it is from outside referer
if ((isset($_SERVER['HTTP_REFERER']) && !empty($_SERVER['HTTP_REFERER']))) {
if (strtolower(parse_url($_SERVER['HTTP_REFERER'], PHP_URL_HOST)) != strtolower($_SERVER['HTTP_HOST'])) {
// referer not from the same domain
}
}
I know this is an old thread, but some one else can probably find it relevant.
The answer is: Yes you can. But it depends if your Apache/nginx server is set to populate the $_SERVER variable with the required information. Most the server are, so probably you can use this approach.
What you need to do is to extract the HTTP_REFERER from the $_SERVER variable and compare with your domain.
<?php
function requestedByTheSameDomain() {
$myDomain = $_SERVER['SCRIPT_URI'];
$requestsSource = $_SERVER['HTTP_REFERER'];
return parse_url($myDomain, PHP_URL_HOST) === parse_url($requestsSource, PHP_URL_HOST);
}
Say, I have "index.htm" and "routines.php".
"index.htm" will call eventually call "routines.php" using JS (AJAX).
So, my question is, how can "routines.php" verify that the request came from the same local server and not outside? Is there a global variable I can check at PHP level or HTTP level?
Edit 1: Using AJAX
You may forget about the Ajax part as it's not really part of the problem. You should read about Cross Site Request Forgeries (CSRF) and CSRF tokens. Some links:
http://en.wikipedia.org/wiki/Cross-site_request_forgery
http://shiflett.org/articles/cross-site-request-forgeries
To answer your question with another question: how would you invoke getdata() using a browser?
(So: no need to worry.)
If the call is made in JavaScript (i.e., on the client), you really can't do anything to definitely prevent someone from simulating a request from index.htm, even if you check the Referer (sic) header.
If the request is made on the server side, you could use some kind of key.
You can of course generate a key on the client side too, but this security measure can be imitated by anyone to simulate a request from index.htm.
you could use a session key:
index.htm
<?php
$_SESSION['safe_key'] = true;
?>
javascript code here
routines.php
<?php
if (!isset($_SESSION['safe_key'])) {
die('from outside');
}
function getdata() { ... }
?>
Basically what happens is when index.htm is called a session safe key is created. Sessions are serverside only. In routines.php if the safe key does not exist, the code was not called from index.htm. If it does exist, run code.
As others pointed out, it would be pretty difficult given your original specification. However, if you can change the index.htm to be index.php and have it output the same content as index.htm, you can also put in additional tokens for session management (e.g. Cookies - yes I know they are easy to spoof too :) and reject the call to getdata() if the tokens don't match.
Use the HTTP_REFERER server variable:
echo $_SERVER['HTTP_REFERER']
With this you can know if the request comes from the server you want.
I am really new to online web application. I am using php, I got this code:
if(isset($_GET['return']) && !empty($_GET['return'])){
return = $_GET['return'];
header("Location: ./index.php?" . $return);
} else {
header("Location: ./index.php");
}
the $return variable is URL variable which can be easily changed by hacker.
E.g i get the $return variable from this : www.web.com/verify.php?return=profile.php
Is there anything I should take care? Should I use htmlentities in this line:
header("Location: ./index.php?" . htmlentities($return));
Is it vulnerable to attack by hacker?
What should i do to prevent hacking?
Apart from that typo on line 2 (should be $return = $_GET['return'];) you should do $return = urlencode($return) to make sure that $return is a valid QueryString as it's passed as parameter to index.php.
index.php should then verify that return is a valid URL that the user has access to. I do not know how your index.php works, but if it simply displays a page then you could end up with someting like index.php?/etc/passwd or similar, which could indeed be a security problem.
Edit: What security hole do you get? There are two possible problems that I could see, depending how index.php uses the return value:
If index.php redirects the user to the target page, then I could use your site as a relay to redirect the user to a site I control. This could be either used for phishing (I make a site that looks exactly like yours and asks the user for username/password) or simply for advertising.
For example, http://yoursite/index.php?return?http%3A%2F%2Fwww.example.com looks like the user accesses YourSite, but then gets redirected to www.example.com. As I can encode any character using the %xx notation, this may not even be obvious to the user.
If index.php displays the file from the return-parameter, I could try to pass in the name of some system file like /etc/passwd and get a list of all users. Or I could pass something like ../config.php and get your database connection
I don't think that's the case here, but this is such a common security hole I'd still like to point it out.
As said, you want to make sure that the URL passed in through the querystring is valid. Some ways to do that could be:
$newurl = "http://yoursite/" . $return;
this could ensure that you are always only on your domain and never redirect to any other domain
$valid = file_exists($return)
This works if $return is always a page that exists on the hard drive. By checking that return indeed points to a valid file you can filter out bogus entries
If return would accept querystrings (i.e. return=profile.php?step=2) then you would need to parse out the "profile.php" path
Have a list of valid values for $return and compare against it
this is usually impractical, unless you really designed your application so that index.php can only return t a given set of pages
There are many ways to skin this cat, but generally you want to somehow validate that $return points to a valid target. What those valid targets are depends on your specification.
If you're running an older version of both PHP 4 or 5, then I think you will be vulnerable to header injection - someone can set return to a URL, followed by a line return, followed by any other headers they want to make your server send.
You could avoid this by sanitising the string first. It might be enough to strip line returns but it would be better to have an allowed list of characters - this might be impractical.
4.4.2 and 5.1.2: This function now prevents more than one header to be
sent at once as a protection against
header injection attacks.
http://php.net/manual/en/function.header.php
What would happen if you put in a page that didn't exist. For example:
return=blowup.php
or
return=http://www.google.co.uk
or
return=http%3A%2F%2Fwww.google.co.uk%2F
You could obfuscate the reference by not including the .php in the variable. You could then append it in the code-behind and check for the existence of the file in your directory / use a switch statement of allowable values before redirecting to it.
In this case, it more depends on what's done with that part of the query string on index.php. If it's being sent to a database query, output, eval(), or exec() yes, its a very common security hole. Most other situations will be safe unfiltered, but its best to write your own general purpose sanitizing function which converts quotes of all varieties to their HTML entity, as well as equals symbols.
The things I would do are:
Define, what type of return values are allowed?
Write down all types of possible return values.
Then, make conclusions: what characters are not allowed, what is the maximum url length, what domains are allowed, etc.
Finally: make a filter function according to above conclusions.
I Thing hacker can do this
you will redirect if $_GET['return'] Contain any thing
the hacker can use it as xss
redirect to virus or any thing like it
but there is no ability to make any thing else
I've got this code on my page:
header("Location: $page");
$page is passed to the script as a GET variable, do I need any security? (if so what)
I was going to just use addslashes() but that would stuff up the URL...
I could forward your users anywhere I like if I get them to click a link, which is definitely a big security flaw (Please login on www.yoursite.com?page=badsite.com). Now think of a scenario where badsite.com looks exactly like your site, except that it catches your user's credentials.
You're better off defining a $urls array in your code and passing only the index to an entry in that array, for example:
$urls = array(
'pageName1' => '/link/to/page/number/1',
'pageNumber2' => '/link/to/page/number/2',
'fancyPageName3' => '/link/to/page/number/3',
);
# Now your URL can look like this:
# www.yoursite.com?page=pageName1
This is a code injection vulnerability by the book. The user can enter any value he wants and your script will obey without any complaints.
But one of the most important rules – if even not the most important rule – is:
Never trust the user data!
So you should check what value has been passed and validate it. Even though a header injection vulnerability was fixed with PHP 4.4.2 and 5.1.2 respectivly, you can still enter any valid URI and the user who calls it would be redirected to it. Even such cryptic like ?page=%68%74%74%70%3a%2f%2f%65%76%69%6c%2e%65%78%61%6d%70%6c%65%2e%63%6f%6d%2f what’s URL encoded for ?page=http://evil.example.com/.
Yes, you do. Just because you or I can't immediately think of a way to take advantage of that little bit of code doesn't mean a more clever person can't. What you want to do is make sure that the redirect is going to a page that you deem accessible. Even this simple validation could work:
$safe_pages = array('index.php', 'login.php', 'signup.php');
if (in_array($page, $safe_pages)) {
header("Location: $page");
}
else {
echo 'That page is not accessible.';
}
Or, at the very least, define a whitelist of allowed URLs, and only forward the user if the URL they supplied is in the GET variable is in the list.
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
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.
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.
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.
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"
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;
}
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.
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...