I've tried to be as specific as possible, but I'm sorry that the subject of my question may be broad.
I got used to a habit of sending variables using the $_GET['variable'], for instance, let's say I'm using ajax to get some information from a database, I would probably do something like this:
xmlhttp.open("GET","read.php?option=someoption",true);
And then I would set the PHP page in a way that behave differently according to the $_GET['option'] it would receive.
I then realised that any logged-in user could type the URL and directly modify the database, so I've set some additional $_SESSION['redirect'] variables before each redirection to help prevent access to php pages from URL. By doing a quick ajax call to a "prevent.php" page that would do something like so:
$_SESSION['redirect'] = "true";
header("Location: page.php");
And then having it set this way in the page.php for instance:
if ($_SESSION['redirect']==true) {
// access the database
}
else {
// deny access
}
Is this a reliable way of doing things, or is there a more professional way to sort it out?
No it's not a secure way of doing it.
Here's an example of how you could achieve a secure user system in the simplest of forms:
login.php
<?php
session_start();
$user = isset($_POST['username']) ? $_POST['username'] : false;
$pass = isset($_POST['password']) ? $_POST['password'] : false;
# Check credentials
if (validCredentials($user, $pass)) {
# Credentials are valid
# Set isAdmin session for the user 'admin'
# This is hardcoded for simplicity but
# you could read a value from a database
# and set this session dynamically.
if ($user === 'admin') {
$_SESSION['isAdmin'] = true;
}
# Generate CSRF token (see appendix)
# heads up: random_bytes() is a PHP7 function.
$_SESSION['token'] = bin2hex(random_bytes(32));
# Set logged in session for every user
$_SESSION['user'] = $user;
echo 'Successfully logged in!<br />';
echo 'Go to the user page.';
}
admin-page.php:
<?php
session_start();
if (isset($_SESSION['isAdmin']) && $_SESSION['isAdmin'] === true) {
echo 'Only the admin can see this.';
} else {
echo 'You are either not logged in or you don\'t have the permission to view this page.';
}
user-page.php:
<?php
session_start();
if (isset($_SESSION['user'])) {
$token = $_SESSION['token'];
echo 'Only logged in users can see this. <br />';
echo 'Log me out.';
} else {
echo 'You are not logged in.';
}
Appendix:
Make sure that you protect yourself against CSRF attacks:
For example an insecure way of logging an user out would be:
logout.php:
<?php
session_start();
if (isset($_SESSION['user'])) {
session_destroy();
}
Maybe you ask yourself why this is not secure.
The reason is because for every user the logout link is the same (example.com/logout.php).
So it's not hard at all to guess the logout link (well, we don't even have to guess, we already know for sure).
An attacker could disguise the logout link and as soon as you click on it you would be logged out.
It's very important to understand that the logout is just one example.
Think of a more severe action like deleting a user etc.
So this concept applies to every action an authenticated user can do.
To be safe, you can generate a token as soon as the user has logged in.
For every action taken you then check if the token in the request matches the one you generated.
This way the logout link is unique for every user (example.com/loogut.php?token=random_token_generated_at_login) and is only hardly guessable by an attacker.
safe-logout.php:
<?php
session_start();
if (isset($_SESSION['user'])) {
# Check if the user specified token matches ours
$token = isset($_GET['token']) ? $_GET['token'] : false;
if ($_SESSION['token'] === $token) {
session_destroy();
echo 'Successfully logged out!';
} else {
# We dont logout because the token was not valid
}
}
And NO: a POST request is just as susceptible as a GET request.
So make sure you check the token for every action, regardless of the HTTP method used.
Related
I tried to write my own authentication method (school project), and I'm stuck.
Please advise, how to solve a secure authentication:
There is an index.php which contains everything that needs to be "protected". I will copy the relevant parts of my code here.
updated index.php
session_start();
function checkUserAuth(){
$authStatus = false;
if (isset($_SESSION['PHPSESSID'])){
if ($_SESSION['PHPSESSID'] == $_COOKIE['PHPSESSID']){
$authStatus = true;
}
}
return $authStatus;
}
if(!checkUserAuth()){
include_once(dirname(__DIR__).'/admin/authentication/login.php');
exit();
}
If the checkUserAuth() determines, that there is no properly authenticated user, will include the login.php and stop the rest of the script.
updated login.php:
if(array_key_exists($username, $users) && password_verify($password, $users[$username])){
$_SESSION['PHPSESSID'] = $_COOKIE['PHPSESSID'];
$_SESSION['login_user'] = $_POST['user'];
What I imagine that might happen, is that if the login details are correct, the login.php sets a cookie, and refreshes the page. Then the index.php will detect the cookie, and skip the login part.
The login is pretty much figured out, and thanks to Juned, I think it is working now. However I don't know how secure is this?
On a scale from 1 to very, how wrong I am?
There are loads of ways of doing this. The below pseudocode is not the most efficient but should work and I don't think what you've done above will actually work.
Does this help?
login.php pseudocode
<?php
session_start(); // this function checks if there's a session ID already set, if not, sets one.
if(array_key_exists($username, $users) && password_verify($password, $users[$username])){
// do your login details checking here
// if login details correct
// set a flag in the $_SESSION superglobal and whatever else you want to store about the user like their username e.g.
$_SESSION["loggedIn"] = true;
$_SESSION["username"] = "$_POST['user']"; // better practice to fetch a clean version from your database
//else return user to login page
}
?>
index.php pseudocode
<?php
session_start(); // this will fetch the session ID and other variables that you might have set e.g. username, logged in status
function checkUserAuth(){
$authStatus = false;
if (isset($_SESSION['loggedIn']) && $_SESSION['loggedIn'] === true){
$authStatus = true;
}
return $authStatus;
}
if(!checkUserAuth()){
// redirect to login page. e.g.
header('Location: login.php');
exit;
}
?>
first off all my English is not very good. Sorry I do my best. I am developing a CMS system. I need an inlog system for it. If you are not logged in you can't view some of the pages. I created a php session of it but it doesn't work on the other pages... I will include some off the code I wrote.
On the page I check the username and password, I created the session like this. And worked with session to check username and password, so it works on that page.
<?php
session_start();
$_SESSSION['gebruikersnaam'] = $_POST['gebruikersnaam'];
$_SESSSION['wachtwoord'] = $_POST['wachtwoord'];
if(($_SESSSION['gebruikersnaam'] == 'admin')
&& ($_SESSSION['wachtwoord'] == 'admin123')) {
include("adminpanel.php");
} else {
echo "Uw gebruikersnaam of wachtwoord is foutief.";
}
?>
On my other pages I added this to check if the user is logged in. I seem to never get that I am logged in and I can't echo the session out. Here is the code!
if(!empty($_SESSION['gebruikersnaam']['wachtwoord'])) {
echo "not ingelogd";
}
If the conditional statement with the session works, I can redirect the user to the log in page if he is not logged in.
Thanks in advance you would help me a lot!
That's because of a slip of the tongue, its $_SESSION not $_SESSSION and also:
$_SESSION['gebruikersnaam']['wachtwoord'];
Is actually referring to 1 value, not two:
$data = array('gebruikersnaam' => array('wachtwoord' => 'mijnwachtwoord'));
echo $data['gebruikersnaam']['wachtwoord'];
Instead do:
if(!empty($_SESSION['gebruikersnaam']) || !empty($_SESSION['wachtwoord'])){
echo "not ingelogd";
}
However, you should only store a username and id in a session. Storing the password is a potential security breach and is not necessary.
You could also use something like this to scan for required values:
function required_session_fields(array $keys){
foreach($keys as $k){
if(!array_key_exists($k, $_SESSION) && empty($_SESSION[$k])){
return false;
}
}
return true;
}
if(required_session_fields(['gebruikersnaam', 'wachtwoord'])){
echo 'gelukt';
}
If you started the session and included a file, you can still access the $_SESSION variable. On every new server request, make sure the session is started.
On the other pages you must have
session_start();
before
if(!empty($_SESSION['gebruikersnaam']['wachtwoord'])) {
echo "not ingelogd";
}
However this variable $_SESSION['gebruikersnaam']['wachtwoord'] is never created. Currently you have created $_SESSION['gebruikersnaam'] and $_SESSION['wachtwoord']
Perhaps you meant to have something like
if(!empty($_SESSION['gebruikersnaam']) && !empty($_SESSION['wachtwoord'])) {
echo "not ingelogd";
}
I've got a small problem with my smarty project, logout problem to be precise. I have a index.php page which is the "main" page and it gets POST data and directs actions based on current data. There`s checking if the session variables has been set. Now when I login I have function like this:
function login($value)
{
$res = $this->sql->checkLogin($value);
if($res)
{
//checks if user is admin
$isadm = $this->sql->isAdm($value);
if($isadm == true)
{
$_SESSION['user'] = $value['name'];
$_SESSION['adm'] = true;
$message = 'Admin';
$this->tpl->assign('var', $message);
if($_SESSION['adm'] == true)
{
//sets some variables for admin users
$navigation = 'navi';
$this->tpl->assign('navigation', $navigation);
}
$this->tpl->display('maint_main.tpl');
}
//user is not admin
else
{
$_SESSION['user'] = $value['name'];
$_SESSION['adm'] = false;
$message = 'Perus';
$this->tpl->assign('var', $message);
if($_SESSION['adm'] == true)
{
$navigation = 'navi';
$this->tpl->assign('navigation', $navigation);
}
$this->tpl->display('maint_main.tpl');
}
}
//login failes, show login form and info
else
{
$message = 'Login failed';
$this->tpl->assign('var', $message);
$this->tpl->display('login_form.tpl');
}
}
and logout function :
function logout()
{
setcookie(session_name(), '', time()-42000, '/');
session_unset();
session_destroy();
$this->tpl->display('login_form.tpl');
}
These work just about the way they are supposed to but the real problem occurs when I log out and redirect to the login_form.tpl. If I use the back button of the browser the POST data with username and password is retrieved and the login goes through again. This causes that those pages behind login are still viewable. As I am not quite familiar with Smarty yet I couldn`t figure out any way to fix this. So basically how to prevent access to that POST data after logout?
I don't think this has anything to do with smarty. This is a browser/http generic issue. Most browsers will re-post form data after confirmation from the user.
One approach to make re-posts of the form invalid would be to pass along a secret code/token (perhaps a guid or your session id) which is also stored in session data. When the user logs out, clear their session (or at least the secret code you're checking). When the user logs in, check to make sure that the confirmation code matches the one for the current session.
This pattern is often used to manage csrf attacks and is often known as a 'synchronizer token'. This blog post provides a good explanation https://blog.whitehatsec.com/tag/synchronizer-token/
I am reading a lot regarding the best practice to monitor when a user is logged in or not.
Currently i am trying to use a variable in a session like below:
login.php
<?php
session_start();
//I set that variable to false
$_SESSION['LOGGED_IN'] = FALSE;
{follows some code that checks the username and the password provided by the user
in an HTML form with POST request against the records of the database. If a match is
found then it allows the user to proceed with the loggin}
if($statement->rowCount() = 1) //
{
session_regenerate_id(true);
$_SESSION['LOGGED_IN'] = TRUE;
header('Location: mainpage.php');
}
else
{
echo "wrong username or password";
}
?>
mainpage.php
<?php
session_start();
if(($_SESSION['LOGGED_IN'] == TRUE) && isset($_SESSION['LOGGED_IN']))
{
echo "You are now logged in!";
}
else
{
echo "You are not logged in. Please retry.";
}
?>
The problem is that when i use a correct pair of credentials SOMETIMES i log in getting the "You are now logged in!" message, and sometimes using the same credentials i get the "You are not logged in. Please retry.".
I've added that message in the else statement on purpose. Normally there i will insert a redirection to the login page.
I am getting confused because this is an error that i shouldn't have. In the login.php script i am making sure that in order to redirect to the mainpage.php the $_SESSION['LOGGED_IN'] = TRUE. So that value should be transferred to the mainpage.php as TRUE and not FALSE.
What am i missing here?
And a general question regarding loggin:
Is it better to keep the login value (TRUE or FALSE) in a session or use a table in MySQL with a flag indicating when a user is logged in or not?
Thanks!
$loggedin = false;
if ($_SESSION) { //user loggedin
$loggedin = true;
...//get token
}
...
if($loggedin){
echo 'Hi '.$user['name'];
}
else{
echo 'Please log in';
}
...
I suppose the web page will display "please log in" when I log out. But it says "undefined $user variable at /src/myproject/index line 80". And after I refresh the page, it says "please log in".
What is the problem here? Thank you for your help.
From what I can tell from your pseudo code, you have some sort of key in the $_SESSION variable that says the user is logged in.
For demonstration, let's assume you do something like... After the user logs in, you assign $_SESSION['user'] = an array of user information. One of those keys is 'name'.
So, your code should look something like this
$loggedin = false;
if (isset($_SESSION['user'])) {
$loggedin = true;
}
if ($loggedin) {
echo "Hi " . $_SESSION['user']['name'];
}
else {
echo "You are not logged in."
}
Please keep in mind this is just a solution for your code sample you posted. To do this properly, I would suggest the following changes:
create a class that handles authentication
create methods in that class to determine if the user is logged in or not
create methods to return the current logged in user.
This will make your code more extensible, reuseable and easier to follow in the future.
Best of luck.