<?php
session_start();
// After user logged in
session_regenerate_id();
$_SESSION['logged_in'] = 1;
$_SESSION['ip'] = $_SERVER['REMOTE_ADDR'];
$_SESSION['agent'] = $_SERVER['HTTP_USER_AGENT'];
// Session Checking
function session_check(){
if(isset($_SESSION['logged_in']) && !empty($_SESSION['logged_in'])){
if(isset($_SESSION['ip']) && !empty($_SESSION['ip']) && ($_SESSION['ip'] == $_SERVER['REMOTE_ADDR'])){
if(isset($_SESSION['agent']) && !empty($_SESSION['agent']) && ($_SESSION['agent'] == $_SERVER['HTTP_USER_AGENT'])){
return true;
} else {
echo "Not allowed to view this page. Error no: 3. You will be redrected to login page in few seconds";
header('Refresh: 3; url=./login.php');
}
} else {
echo "Not allowed to view this page. Error no: 2. You will be redirected to login page in few seconds";
header('Refresh: 3; url=./login.php');
}
} else {
echo "You are not allowed to view this page. Error no: 1. You will be redirected to login page in few seconds";
header('Refresh: 3; url=./login.php');
return false;
}
}
And I keep getting error no2 when I run:
if(session_check()){ echo "something";}
Is it because I am using dynamic IP?
Is my code good enough to protect session hijacking?
If I exclude the ($_SESSION['ip'] != $_SERVER['REMOTE_ADDR']), it works perfectly.
Important Question:
What are your anti session hijacking methods? Can share with us? Using IP-checking, user-agent checking or probably other methods??
Yes, a dynamic IP address would cause you to get logged out as a user of this code as soon as your IP address changes. You shouldn't be using the IP address to check for session security. The user agent check you already have should be enough on its own.
Here is a great article on session security: http://phpsec.org/projects/guide/4.html. Near the bottom it shows how you can make the user agent check even more secure using md5 hashing. Also here is an excerpt concerning IP addresses:
It is unwise to rely on anything at the TCP/IP level, such as IP address, because these are lower level protocols that are not intended to accommodate activities taking place at the HTTP level. A single user can potentially have a different IP address for each request, and multiple users can potentially have the same IP address.
I'm assuming there is more happening between the setting of the variables and the checking. That is probably what is causing the problem, but it is difficult for us to say what could be causing it when we don't see any error messages or any code that might be causing it. Try echoing out what session[ip] actually is and post it here.
You cannot echo anything before issuing a write to header unless you use output buffering. I suggest you return a status code instead, instead of putting the header inside the session_check function. After all, it is named session_check, not session_check_redirect() :D
From the PHP manual on header()
Remember that header() must be called
before any actual output is sent,
either by normal HTML tags, blank
lines in a file, or from PHP. It is a
very common error to read code with
include(), or require(), functions, or
another file access function, and have
spaces or empty lines that are output
before header() is called. The same
problem exists when using a single
PHP/HTML file.
I don't see what's wrong with your code. As suggested, try var_dumping the content of both $_SERVER and $_SESSION at the beginning of session_check() to see what they contain.
Even if you use a dynamic IP, it should not change between two requests (it usually changes when you unplug your network cable or disconnect your wifi card).
Your method may help against session hijacking, but would not work when the attacker is behind the same public IP address as the user.
I suggest reading OWASP recommendations for best practice in web security.
A agree with Marius, there's probably more going on.
I've taken the liberty of making your if..else logic more readable:
function session_check(){
if (empty($_SESSION['logged_in'])){
echo "Error no: 1.";
return false;
}
if (empty($_SESSION['ip']) || ($_SESSION['ip'] != $_SERVER['REMOTE_ADDR'])){
echo "Error no: 2.";
return false;
}
if (empty($_SESSION['agent']) || ($_SESSION['agent'] != $_SERVER['HTTP_USER_AGENT'])){
echo "Error no: 3.";
return false;
}
return true;
}
Related
I'm attempting to include some code in the head.inc file that will check if the page is available in the user's location language and if so it'll redirect to that page. Obviously this isn't a great for UX but alas my client is keen.
The only issue I need to overcome is if the user has already been redirected then don't do it for every page... store the fact that the user was redirected and don't do it again until the session has ended. Something like that.
I have written the code below but a) I'm unsure this is the best way to do it and b) it seems to get stuck in a redirect loop (which I thought I'd avoided by my second session check).
I'm using the ipapi.com to check the user's location. I’m also using ProcessWire’s ‘$session’ which is effectively the same as PHP session.
if (!$session->get("lucentLightingRegion")) {
$session->set("lucentLightingSessionRedirected", "false");
if ($page->ipapiGetUserLocation()['continent_code'] === 'NA') {
$session->set("lucentLightingRegion", "NA");
if ($page->viewable('na')) {
$url = $page->localUrl('na');
$session->redirect($url);
}
} else {
$session->set("lucentLightingRegion", "INT");
if ($page->viewable('default')) {
$url = $page->localUrl('default');
$session->redirect($url);
}
}
} else {
$sessionRegion = $session->get("lucentLightingRegion");
bd($sessionRegion);
if ($page->viewable($sessionRegion) && $session->get("lucentLightingSessionRedirected") == "false") {
$url = $page->localUrl($sessionRegion);
$session->redirect($url);
$session->set("lucentLightingSessionRedirected", "true");
}
}
Calling $session->redirect($url); before $session->set("lucentLightingSessionRedirected", "true"); appears to be your issue.
Looking at the source code of $session->redirect() it executes:
header("Location: $url");
exit(0);
Which is preventing $session->set("lucentLightingSessionRedirected", "true"); from being called, resulting in the redirect loop, as lucentLightingSessionRedirected was never set to "true".
To resolve the issue, you should be able to change the order of operations.
if ($page->viewable($sessionRegion) && $session->get("lucentLightingSessionRedirected") == "false") {
$session->set("lucentLightingSessionRedirected", "true");
$url = $page->localUrl($sessionRegion);
$session->redirect($url);
}
Do Keep in mind that Wire::__call() is being used to issue Session::__redirect()
However it appears that you also have a logic flaw with the sessions and regional redirects, that may result in an undesired state. Since $page->localUrl() is using lowercase values but lucentLightingRegion is uppercase, I am not aware of how your application is designed to handle them. Additionally the default region of INT loading the default page, which may not work as desired when redirecting to $sessionRegion = 'INT'. I would need more details on how this is expected to work and if there is indeed different states for NA, na, INT and default and what is supposed to happen when none of the specified values are a viewable() page when lucentLightingRegion is set. Currently if lucentLightingRegion is set and is not viewable() or lucentLightingSessionRedirected is "true", none of the conditions will be met.
I am in the process of making a secure Web application on a remote server. Is there a industry standard of preventing users from accessing web pages that are only available to users who have posted their login details.
So far authenticated user pages are protected using simple header redirects if the user do sent hold a session variable.
Is this industry standard? Are there better ways to implement such a method.
pysedo code
Session code dosent equal value or session is null {
header redirect to index
}
Like I said in my comment, redirect headers are not a security feature in their own. After you have sent a redirect header, you should make sure that normal execution can't happen.
For example:
if($username != "foo" && $password != "bar")
{
header('Location: http://domain.org/index.php');
}
echo "Authorized page";
Even if the username and password are not correct, the contents of this page can still be accessed by just ignoring the Location header.
A simple fix would be:
if($username != "foo" && $password != "bar")
{
header('Location: http://domain.org/index.php');
die("Redirecting to home page");
}
echo "Authorized page";
Furthermore, it's important that authentication doesn't rely on manual checks on each page, because these checks can easily be forgotten by developers. Try to automate these kind of things as much as possible.
Not really sure if I understand the question correct.
However maybe this helps:
Since the web server handles authentication in a pretty good manner (provided it is configured correct) there is only hotlinking left. You can try to catch that issue by additional redirect rules in the server configuration: if the HTTP_REFERER is not a previously authenticated page, then redirect to wherever.
There is nothing more I can think of.
The important thing is to get authentication right and safe at the beginning.
Not sure if it is a good solution, but I simply keep the login status in session variables:
if ($_SESSION[loggedIn] == "yes" && $_SESSION["LevelTwoPermissions"] == "yes")
{
// Grant access to allowed content...
}
else
{
// Redirect to login page or ask for login in a pop-up
}
It could theoretically be hacked by stealing session cookie from an other user, but it's a reasonable level of security for my purposes.
In an article on preventing PHP form resubmissions, I read the following:
(Not quoting) This could be the page that receives the form data, for example called "form.php":
<form action="submit.php">
<input type="text" name="user" required />
<input type="password" name="pass" required />
<input type="submit" value="Log in" />
</form>
The page that would process the POST data would therefore be called "submit.php". If the login went correctly, this code would run:
header('Location: /login/form.php?success=true');
However, couldn't a user just navigate to the URL above? Also, what is the purpose of the GET variable? Couldn't I just have a script at form.php that checks if the user is logged in?
At submit.php, should I save the logged in username as $_SESSION['username'], and then check if isset() at form.php? Also, since a URL with "success" in it isn't really pretty, is it economical to redirect the user once again? Should I use PHP header() or Javascript window.location.href? As you see, I'm sort of confused.
Thanks for any help.
However, couldn't a user just navigate to the URL above?
Yes, he can. This will not cause anything bad though.
Also, what is the purpose of the GET variable?
To have some flag that represents the fact that the form has been processed successfully and you need to congratulate user.
Couldn't I just have a script at form.php that checks if the user is logged in?
Uhm, you can keep your code in the way you like. There is no any strong requirements
At submit.php, should I save the logged in username as $_SESSION['username'], and then check if isset() at form.php?
If you need to persist it across the current session - yes, do so.
Also, since a URL with "success" in it isn't really pretty, is it economical to redirect the user once again?
Redirect where. Redirection is pretty cheap thing.
Should I use PHP header() or Javascript window.location.href?
You definitely should do that in php, otherwise you'll get the troubles you're trying to avoid following PRG-way.
PRG or Post/Redirect/Get is just a pattern you can use to prevent the message boxes. How you use it in detail (and the article does only a generic suggestion) depends on your needs.
If you want to flag the success flash message inside a cookie or a session or a get variable, that's totally up to you. A second redirect won't help btw, you'll learn that if you play around with it.
The only important part is, that after you have received the POST request, you do the redirect. The user then can still move back and forward in history w/o being asked to re-submit POST data.
The pattern works and is a fine thing. Just two days ago I did it again and a step-by-step weppapp installer was much nicer to navigate with the browser interface.
About your redirect
This code is wrong:
header('Location:/login/form.php?success=true');
First of all, you need to have a space after the colon:
header('Location: /login/form.php?success=true');
Then the address must be an absolute URI, it must contain the full URL:
header('Location: http://example.com/login/form.php?success=true');
Next to the header(), you should provide a message body as per RFC, many so called "web-developers" don't even know:
$url = 'http://example.com/login/form.php?success=true';
header(sprintf('Location: %s', $url));
printf('Moved.', $url);
exit;
Don't forget the exit. Sure, that's pretty much re-enventing the wheel, instead install the http extension of PHP and just do this line:
http_redirect('/login/form.php?success=true');
You find that nifty helper here.
To recap: Important is that you do the redirect after post. Everything else, like passing a variable is totally up to you how you would like to do it.
Yes, you should never rely on a GET variable (or even a hidden POST variable) to say, "sure, let me in, I'm a valid user!".
Personally, I would strip the GET information from the link and rely solely on session variables. Remember to place a 'session_start();' as the first line of code if you are using PHP to activate the session.
For submit.php:
<?php
session_start();
if ($_POST['user'] && $_POST['pass']) { // Make sure both variable are set
if (your_method) {
// Code to check if the user and pass are valid however you plan
$_SESSION['user'] = $_POST['user'];
$_SESSION['loggedin'] = time();
}
}
header('Location: form.php'); // Either way, pass or fail, return to form.php
exit();
?>
Then in form.php:
<?php
session_start();
$activeuser = false;
if ($_SESSION['user'] && $_SESSION['loggedin'] < (time()+600)) {
// Check if the user exists and the last access was with in 10 minutes.
$_SESSION['loggedin'] = time(); // If so, keep them up to date!
$activeuser = true;
}
if ($activeuser) {
// whatever should show to someone logged in
} else {
// Show log in form
}
?>
Also, you may already know this, but the default method of transferring is GET, so be sure to specify method="post" in the form tag.
It's normally best to use header() to redirect if needed as Javascript is client-side and can be avoided which can break your intent for the functioning of your site.
The main idea behind POST/REDIRECT/GET, as the article you linked to points out, is to avoid users resubmitting data (most of the time). Generally, you don't want the same POST (with the exact same data) to happen twice -- indeed, in some situations, it could end up performing some action (like charging a credit card) a second time, which would be bad.
Most of what you ask about in your question are implementation details (like sending the ?success request parameter in the redirect).
In practice, what usually happens is that your redirect on success. If, for example, the user's input fails validation, you don't redirect, and instead, redisplay the form, along with relevant error messages.
Here's a basic example, all in one script. I've tried to include only what's important, with as little extraneous stuff as possible.
login.php
<?php
/**
* ensure user supplied both username & password
* #return mixed true or an array of error messages
*/
function validate_login_values($vars){
$errors = array();
if (empty($vars['username'])) $errors[] = 'You must supply a username, genius.';
if (empty($vars['password'])) $errors[] = 'You must supply a password, dummy.';
if (empty($errors)) return true;
return $errors; // $errors must be an array.
}
if (! empty($_POST)){
$validationResults = validate_login_values($_POST);
if ($validationResults === true){
// assume here that authenticate properly escapes it's arguments before sending them
// to the database.
if (authenticate($_POST['username'],$_POST['password'])){
//GREAT SUCCESS! The user is now logged in. Redirect to home page
header("Location: /");
die();
}
$errors[] = 'Invalid username/password. Try again, slim";
}else{
$errors = $validationResults; // validate_login_values created errors.
}
}
?>
<h1>Log In, Friend!</h1>]
<?php
//display errors, if there were any
if (! empty($errors)): ?>
<div class="errors">Something went horribly wrong:
<ul><?php foreach($errors as $e) echo "<li>$e</li>"; ?></ul>
<div>
<?php endif; ?>
<form method="POST">
<!-- //username, password, and submit -->
</form>
I have a resetpwd.php page that is shown when a user's password is reset in the database. However, this page can be accessed by users whose password is not reset by manually changing the URL to "/resetpwd.php". How can I prevent this?
It's not really possible to prevent changing the URL, because the HTTP server does not really care if the URL was clicked on a page that you provided to the user, or typed in the browser's location bar.
Instead, add some code to resetpwd.php to check if the user accessing it is allowed to do so, i.e. if his password is reset in the database. If not, just exit the script or redirect the user to a different page.
You can't simply prevent people from accessing it by typing in the URL. The best you could do would be to set a flag when a user's password is reset and redirect users whose accounts aren't flagged.
It may be worth considering why you need to "hide" this page from users. For example, you can navigate directly to https://stackoverflow.com/error. Looking at that page doesn't mean an actual server error occurred, but you should be intuitively aware of this if you intentionally type in that URL.
Negative answer is a legitimate answer too. Leave it alone.
Noway.
Just leave it alone. there is no reason in preventing access to this page.
depends how the user gets to that page.
On a part of mine I had this idea:
if($_SESSION['userid'] != $_GET['id'])
{
//check if user if signed in
if($_SESSION['signed_in'] == true)
{
//unset all variables
$_SESSION['signed_in'] = NULL;
$_SESSION['user_name'] = NULL;
$_SESSION['user_id'] = NULL;
echo 'Error';
}
else
{
echo 'Por favor, log in again. Gracias.';
}
echo '<script type="text/javascript">self.close();</script>';
die();
}
if someone wants to get to this page, which contains private data, I just compare the userid with the session user_id and if they are not equal (an user is trying to get into thru browser side) I log him off and close the window with a javascript line and abort everything else.
I am looking for some code that will return me values if the user has JavaScript enabled or disabled, as well as cookies.
I know this is probably easy to do, but my time constraints are so tight, it hurts. There has to be something out there using php that does this. Ideally I would love to find code that has a page setup with all the possible values that could affect my scripts.
EDIT: Obviously JavaScript may be disabled, but, I am hoping that I can find something out there to test the two cases.
My Solution
For anoyone else looking for code to detect if the users has cookie enabled or disabled, here is what I ended up coming up with from the posts below... you can just drop this at teh top of any page and it works...
<?php
// do a cookie test
if (!isset($_SESSION['cookie_check']))
{
if (!isset($_GET['cc']))
{
// drop a cookie in their bag
setcookie("cookiecheck", "ok", time()+3600);
header("Location: ".$common->selfURL()."?cc=1");
exit(0);
}
else
{
// do we have a problem?
if (#$_COOKIE['cookiecheck'] != "ok")
{
// we have a problem
header("Location: /site-diag.php");
exit(0);
}
else
{
$_SESSION['cookie_check'] = true;
}
}
}
?>
You could use the jQuery cookie plugin to write a cookie and then see if you can read it back again. That would tell you if cookies were enabled in the client's browser or not.
For checking Javascript, either they have it or they don't. If not, you can use <noscript> tags to display a message asking them to turn it on, put a meta redirect inside, etc. That is the extent of your testing ability.
As for cookies, just try setting a cookie then reading it back! Since you're concerned about Javascript's ability to handle cookies, I assume you already have a cookie library that you are using, meaning that you can just use the set function for a test cookie then the get function to read it back. If the test cookie can't be read back, cookies are off.
Here is one for checking cookies
http://techpatterns.com/downloads/javascript_check_cookies.php
if javascript is disabled then you can't use jquery or prototype.
write a function that writes a cookie, then tries to read it.
and secondly puts out some js code to the screen that makes a ajax call to a basic php script.
you can use a database to set the boolean results of both tests on the visitor table if there is one.
This is the way I check if cookies and JavaScript are enabled:
if($_SESSION['JSexe']) { // 3rd check js
if($_COOKIE['JS']) {
setcookie('JS','JS',time()-1); // check on every page load
}
else {
header('Location: js.html');
}
}
// 2nd so far it's been server-side scripting. Client-side scripting must be executed once to set second cookie.
// Without JSexe, user with cookies and js enabled would be sent to js.html the first page load.
elseif($_COOKIE['PHP']) {
$_SESSION['JSexe'] = true;
}
else { //1st check cookies
if($_GET['cookie']) {
header('Location: cookies.html');
}
else{
setcookie('PHP','PHP');
header('Location: '.$_SERVER['REQUEST_URI'].'?cookie=1');
}
}
Explained in detail here: http://asdlog.com/Check_if_cookies_and_javascript_are_enable
First, realize that you can't use JavaScript to check for cookies if JavaScript is turned off. The usual check for cookies being on is to write one and then read it.
Do you care about the case when cookies are on but JavaScript is off? What are you going to do based on the information?
I found this code here for checking for a cookie via PHP. Doesn't rely on JavaScript. Is PHP your server language?
<?php
class cookieCheck
{
public function check()
{
if (setcookie("test", "test", time() + 100))
{
//COOKIE IS SET
if (isset ($_COOKIE['test']))
{
return "Cookies are enabled on your browser";
}
else
{
return "Cookies are <b>NOT</b> enabled on your browser";
}
}
}
}
?>