I currently have a PHP Login System which logins by authenticating the Organisation Code the user enters, thus the database queried will be different.
includes.php
<?php
mysql_connect("mysql.example.com", $dbconn, "MySecurePassword");
mysql_select_db($dbconn);
?>
login.php
// $org is the Organisation Code, will be set when user clicks Login
$dbconn = $org;
include "includes.php";
// Omitted the $userid & $pw variables, assume there is no error, and that MySQL Injection is prevented already
$query = "SELECT * FROM `Login` WHERE `userid`=TRIM('$userid') AND `password`=TRIM('$pw' )";
$result = mysql_query($query);
if(mysql_num_rows($result)>0){
session_start();
$_SESSION['logged_in'] = $username;
header("Location: loggedinpage.php");
}
loggedinpage.php
<?php
session_start();
// As there is no fixed database, I've omitted the DB Connection
define('DS', TRUE); // used to protect includes
define('USERNAME', $_SESSION['logged_in']);
define('SELF', $_SERVER['PHP_SELF'] );
// Checks if user is logged in
if (!USERNAME) {
header("Location: login.php");
}
?>
Security Measure Taken
Passwords are hashed using SHA-512 and not stored in plaintext.
MySQL injection is prevented using mysql_real_escape_string()
I've omitted some code for ease to read, may I know if this way of checking if user is logged in is secure? If not, how can I improve it?
Thanks in advance!
Updated question to reflect the updates in comments
Assuming that your query works as intended and only returns a row when the match is exactly correct (e.g. no weird fuzzy matching through collate rules, but pure bin comparison), the authentication part is pretty much fine.
(You have been warned about SQL injection plenty, you're on your own there.)
Your security then boils down to this:
$_SESSION['logged_in'] = $username;
and the subsequent:
define('USERNAME', $_SESSION['logged_in']);
if (!USERNAME) {
header("Location: login.php");
}
And I suppose your question is about this part.
Then the answer is: the session part is fine, the blocking is not.
That's how sessions are used, yes, and they're reasonably safe by default; a user won't be able to somehow set the $_SESSION['logged_in'] value themselves, the value can only be set by your server, and presumably you're doing so only on successful authentication. Do read up about session hijacking, this is the only real vulnerability to the whole scheme.
The real problem is:
if (!USERNAME) {
header("Location: login.php");
}
Setting a header does not terminate the current page. If you're outputting sensitive information after this line, it will be sent to the client! You need to explicitly exit after setting the header.
Having said all this, we cannot tell you whether your system is "secure" because there may be any number of facepalm backdoors you have created which we're not seeing. In general I'd start with the following:
stop using mysql, use PDO or mysqli
bind your parameters, don't mysql_real_escape_string them; there are security pitfalls there
use password_hash password hashing, not SHA; especially if you're only doing a single SHA pass
becareful SQL Injection :
If you type in password field :
''=''
The password's rule will be true, because Password = TRIM(''='') is true. You have to control the password's string :
Minimum length
No white space (thanks to Trim function)
And you don't have to store a password like this, you must make a password's hash
Related
New to security and wondering how secure this type of login is? I am not protecting any bank/financial data by any means but trying to secure somewhat sensitive data that shouldn't be exposed to the general public. I only require a password - no official logins are done.
This is in a file called access.php which houses a password input field.
<?php
session_start();
if (!isset($_SESSION['loggedIn'])) {
$_SESSION['loggedIn'] = false;
}
// sha256() password
$password = '13d249f2cb4127b40cfa757866850278793f814ded3c587fe5889e889a7a9f6c';
if (isset($_POST['password'])) {
if (hash('sha256',$_POST['password']) == $password) {
$_SESSION['loggedIn'] = true;
} else {
die ('That is the incorrect password - Please leave now');
}
}
if (!$_SESSION['loggedIn']):
?>
Then my index.php requires access.php at page load. Should access live outside the public directory? Am I missing anything else I should be considering?
New to security and wondering how secure this type of login is?
SHA-256: You're using the entirely wrong tool for the job. Use password_hash() and password_verify():
How to safely store a password
Cryptography terms explained for non-experts
Additionally, SHA-256 is vulnerable to length-extension attacks.
Using == to compare hashes has two vulnerabilities:
Timing attacks
Magic hash comparison (the more pressing concern)
So, to answer your question: Not very. The problem your code is trying to solve is well-known among security experts, and they've gone out of their way to make it simple for others to solve it. That's why password_hash()and password_verify() exist. Use them.
That said, welcome to software security. If you need some additional resources to aid your self-education, check out this application security reading list on Github.
I have a basic site I'm using to test my programming method and I want to get a semi-decent secure way of keeping people logged in. Here is the code for register.php.
$username = $_POST["username"]; //username stored plaintext
$passhashed = crypt($_POST["password"], $username); //hashed password salted with username
$rnum = rand(1000,9999); //assign random 4-digit number
$authkey = crypt($rnum, $passhashed); //unique authentication key per user, hashed and salted with hashed password
//insert into SQL
When they log-in, $username, $passhashed, and $authkey is stored in $_SESSION data.
At the top of every single page I have the following snippet of code:
if(isset($_SESSION["username"])
&& isset($_SESSION["password"])
&& isset($_SESSION["authkey"])) {
$verifyuser = $db->prepare("
SELECT *
FROM users
WHERE username = :user
AND password = :password
AND authkey = :authkey
");
$verifyuser->execute(array(
':user' => $_SESSION["username"],
':password' => $_SESSION["password"],
':authkey' => $_SESSION["authkey"]));
if($verifyuser->rowCount() != 1) {
unset($_SESSION["username"]);
unset($_SESSION["password"]);
unset($_SESSION["authkey"]);
}
}
Basically on any given page, it performs a check that each piece store in $_SESSION clears with SQL, and if not (if any of the checks fail, will give a rowCount of not 1), it drops the session.
I'll be the first to admit I'm not too familiar with contemporary security measures to evade session hijacking (in fact, I only have a loose command of how it is even done). That being said, how is this for a beginner programmer? What can I do different to make it more secure? Assign a second authentication key at login, temporarily store it in SQL and make the same checks (new key per login)?
The crypt function is somewhat out-of-date. You'd be better off using bcrypt, which is provided in PHP using password_hash and password_verify. Additionally, using those functions, the salt (what you call $authkey) is integrated into the string, so you don't need to store it separately.
I notice you're storing the username and password in $_SESSION. $_SESSION cannot be directly modified by the client, so you might be better off just storing the user's ID there.
As you mentioned, I too have a basic understanding of session hijacking.
However I think if these 3 were hijacked, this still may not prevent account hijacking, although does make it harder.
When reading preventing session hijacking, I saw an example as simple as - If the current IP doesn't match the session, log the user out.
<?php
if($_SESSION['ip'] != $_SERVER['REMOTE_ADDR'])
{
session_destroy();
}
?>
Typical: I can no longer find said website...
Some links that may help you:
Preventing session hijacking
Proper session hijacking prevention in PHP
if (strtolower($userDetail["username"]) == strtolower($username) &&
$userDetail["password"] == hash("sha256", $password . $userDetail["salt"])) {
if ($remember == "true") { // Remember Me
setcookie("logged", "$username", time()+60*60*24*365); // 1 Year
} else {
setcookie("logged", "$username", time()+43200); // 12 Hours
}
header("Location: " . getenv("HTTP_REFERER"));
die();
} else {
echo "Invalid login.";
}
I'm trying to make the best possible login I possibly can. The major problem I'm seeing here is cookies. I'm no expert when it comes to this, so here are my main questions:
What should I be setting my cookie as so someone can not easily duplicate the cookie?
Should I be including the salt into the cookie?
I've heard about tokens in addition to salts and having them change all the time. How is this supposed to work?
And I'm wondering if this call for my cookie above is even valid? What's the right way to be doing this?
$loginCheck = $_COOKIE["logged"];
if (isset($loginCheck)) {
// logged in
}
I like to leave the username intact, and have another cookie set as sha1(salt . user . pass)
For instance:
user=Truth
uid=6cb8d1e35e5982a8ef738a8583a5d98d6fe2c484
And then compare using the first cookie, against the database and the known hash.
The way you're doing it at the moment, anyone can copy a cookie and log in with anyone without even knowing their passwords. Just set
logged=Admin expires Jan 1st 5000
And you're good to go. Once you have this second cookie to compare against, you're much safer.
As for the third question, yes, you can even shorten it to:
if (isset($_COOKIE["logged"])) {
But as I stated, you should move to the more secure format I've shown.
See this Absolutely EPIC Tutorial on net.tutsplus.com for more on the subject.
I would recommend using a uniqid which is stored on the customer record and then when you validate that the customer has successfully logged in set the cookie with the uniqid from their customer record. This way no one should be able to replicate the cookie. You could also prefix the cookie value with something if you want to ensure it's safe.
I think incorporating the user's password into the cookie is a dangerous idea as people tend to use the same password on other sites and even though it's encrypted in my opinion it's still dangerous. Using a value unrelated to the customer's information I feel is safer.
<?php
/* A uniqid, like: 4b3403665fea6 */
printf("uniqid(): %s\r\n", uniqid());
?>
A reference on how to use uiqid: http://php.net/manual/en/function.uniqid.php
I found the following code in a previous question on SO. In following code, if the username and password supplied by the user is correct, the user_id and username is stored in session to keep it logged. My question is, why there is need to keep user_id in the session? Isnt only one thing (for example, username) enough to store in session?
If the remember is enabled, then a cookie is set, only with username. Now my question is, Is Only username cookie enough? Can't anyone just edit or add the cookie in the browser and log in the system?
Thanks for your replies.
<?
public function login($username, $pass, $remember) {
// check username and password with db
$result = $conn->query("select * from login where
username='".$username."' and
password=sha1('".$pass."')");
if (!$result) {
throw new depException('Incorrect username and password combination. Please try again.');
}
if ($result->num_rows>0) {
$row = $result->fetch_assoc();
$_SESSION['user_id'] = $row[user_id];
$_SESSION['username'] = $username;
// start rememberMe
$cookie_name = 'db_auth';
$cookie_time = (3600 * 24 * 30);*/ // 30 days
// check to see if user checked box
if ($remember) {
setcookie ($cookie_name, 'username='.$username, time()+$cookie_time);
}
// If all goes well redirect user to their homepage.
header('Location: http://localhost/v6/home/index.php');
} else {
throw new depException('Could not log you in.');
}
}
?>
THIS CODE IS NOT SECURE! (Sorry for the caps, but its for the emphasis). The SQL statement is susceptible to SQL injection. Also storing the username in the cookie is a bad idea because anyone can forge the cookie to gain authentication.
My answer to the question if this is secure: no.
You need to sanitize your code. What happens if someone enters 'test OR 1=1 ' as username?
I do not really know where to start. This code is really unsafe.
You should sanitize with
mysql_real_escape_string() (or mysqli function, or even better: use PDO for any database connection and use prepared statements) the
username and the password and be sure
that $remember is either a boolean
or an integer.
The sha1 is something like broken,
so i'd suggest using md5 instead.
Cookies can be rewritten by the user
that could add username=admin to
the cookie and login as admin.
Your code is not secure.
Your data is open to SQL injection via the initial query, where depending on the access level of the database user, you could have anyone logging in. You need to sanitise your input.
Secondly, the access to the website via the cookie, and the username in it related to the access level and privilege they get? If so in it's current form the session can be easily hijacked.
Here's A Code I use To Make Sure Everything Is Safe .. It may not be the safest but I also use other measures to verify a safe login. But this code will protect u against SQL injections.
function secure($data) {
$data = trim(htmlentities(strip_tags($data)));
if (get_magic_quotes_gpc())
$data = stripslashes($data);
$data = mysql_real_escape_string($data);
return $data;
}
It's usage
secure($username);
for example
foreach($_POST as $key => $value) {
$get[$key] = secure($value);
}
This tells PHP for each of the POST values secure it.
You Can also use it for post by using $_GET instead of $_POST but lets face it .. it would be really stupid to have your login using GET commands
I have a session that I gave to users that has matching password = stored password, like a simple login system :
// Checks Password and Username
if ($pSys->checkPassword($AccountData['password'], $StoredData['password'])) {
$_SESSION['login'] = true;
}
The question is: is this secure enough?
// put this on every header page that needs to be loggedin.
function loginCheck(){
if ( empty( $_SESSION['login'] )) {
header( 'location:index.php' );
die();
}
}
Is there a difference between die() and exit()? Second, some say that I should add session_regenerate_id()? (Is that an overkill?) Anyway the real question is said above.
addon*
I have read PHP Session Security but it seems it doesn't match my problem here (that link is just to general).
Here is the checkPassword() method
function checkPassword($password, $storedpassword) {
if($password == $storedpassword){
return true;
}
}
Answering the first part: empty and die are not comparable:
empty is to check if a variable does not exists or has a value equal to false (see also this type comparison table).
die is an alias of exit and is used to immediately abort the execution of the current script with an optional message.
Now to your authentication example: Yes, you should use session_regenerate_id to generate a new session ID and revoke the old session ID by setting the optional parameter for session_regenerate_id to true:
if (!sizeof($ErrorAccount)) { // Checks Password and Username
session_regenerate_id(true);
$_SESSION['login'] = true;
}
The purpose of session_regenerate_id is to avoid session fixation attacks. This will not be necessary if the server only allows session ids to be sent via cookies, but since PHP by default allows them in URL, you're strongly recommended to regenerate the id.
Since you are looking for answers about security, also don't keep the stored password in plain text. At the very least, salt and hash your passwords, then store the hash. Rehash and compare hashes, not plain text.
You could added a token (hash) to the form and then validate the token to make sure that the token which was submitted via the form is still valid. This helps to prevent CSRF attacks.
You could also store the IP address and browser together with the token in a database for additional validation, however you need to be aware that some ISP's change the clients IP address fairly often and could cause the validation to fail incorrectly.
More Info