I am currently trying to find out why my login statement works with using a username, but not email (or vice versa if switch in query)
The query only seems to accept the first value of the query, even if I've put it in brackets, it doesn't seem to register the second parameter.
Running the query in phpMyAdmin works flawlessly, but breaks in the script
open to suggestions
$sql = "SELECT * FROM db_cms_users WHERE username = ? OR email = ? AND password = ?";
$stmt = $this->connect()->prepare($sql);
if(!$stmt->execute([$userID, $userID, $password])){
$stmt = null;
header("location: index.php?error=failstmt");
exit();
}
if($stmt->rowCount() == 0){
$stmt = null;
header("location: login.php?error=nouser");
exit();
}
I have tried
$sql = "SELECT * FROM db_cms_users WHERE (username = ? OR email = ?) AND password = ?";
rowCount returns true if I input a username, but false if I input a email. statements match SQL Database.
Dumped Variables
SQL: [76] SELECT * FROM db_cms_users WHERE username = ? OR email = ? AND password = ?
Sent SQL: [137] SELECT * FROM db_cms_users WHERE username = 'test#email.com' OR email = 'test#email.com' AND password = 'password'
Params: 3
Key: Position #0: paramno=0 name=[0] "" is_param=1 param_type=2
Key: Position #1: paramno=1 name=[0] "" is_param=1 param_type=2
Key: Position #2: paramno=2 name=[0] "" is_param=1 param_type=2
Database output
Array
(
[0] => Array
(
[id] => 1
[0] => 1
[username] => test
[1] => test
[password] => $2y$10$QNKXEo3pnGPCjUMnfXlV..JJ4OFcSQJ5EVg75xOjlE7p5pL7Dqwau
[2] => $2y$10$QNKXEo3pnGPCjUMnfXlV..JJ4OFcSQJ5EVg75xOjlE7p5pL7Dqwau
[email] => test#email.com
[3] => test#email.com
[status] => 1
[4] => 1
[is_admin] => 1
[5] => 1
[registration] => 2021-11-13 12:21:28
[6] => 2021-11-13 12:21:28
)
)
File: user.class.php
protected function loginUser($userID, $password){
$sql = "SELECT password FROM db_cms_users WHERE username = ? OR email = ?";
$stmt = $this->connect()->prepare($sql);
if(!$stmt->execute([$userID, $userID])){
$stmt = null;
header("location: index.php?error=failstmt");
exit();
}
if($stmt->rowCount() == 0){
$stmt = null;
header("location: login.php?error=loginerror");
exit();
}
$hashedPwd = $stmt->fetchAll();
$checkPwd = password_verify($password, $hashedPwd[0]['password']);
if($checkPwd == false){
$stmt = null;
header("location: index.php?error=wrongpwd");
exit();
}elseif($checkPwd == true){
$sql = "SELECT * FROM db_cms_users WHERE username = ? OR email = ? AND password = ?";
$stmt = $this->connect()->prepare($sql);
if(!$stmt->execute([$userID, $userID, $password])){
#$stmt = null;
header("location: index.php?error=failstmt");
exit();
}
if($stmt->rowCount() == 0){
$stmt = null;
header("location: login.php?error=nouser");
exit();
}
$row = $stmt->fetchAll();
//make session later
//nov 13/21
session_start();
$_SESSION['username'] = $row[0]['username'];
$_SESSION['uid'] = $row[0]['id'];
return true;
}
}
File userContr.class.php
public function login($userID, $password){
$result = $this->loginUser($userID, $password);
return $result;
}
File test.php
<?php
ob_start();
session_start();
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);
include "includes/autoloader.inc.php";
$userObj = new UserView();
$data = $userObj->showUser(1);
echo "<pre>";
print_r ($data[0]);
echo "</pre>";
$userObj = new UserContr();
if(isset($_SESSION['uid'])){
echo "<h1>Welcome back ". $_SESSION['username'] ."!";
echo "<a href='?a=logout'>Logout</a>";
if(isset($_GET['a'])){
$a = $_GET['a'];
if($a == "logout"){
$userObj->logoutUser();
exit();
}
}
}else{
if(isset($_POST['loginUser'])){
$userID = $_POST['userid'];
$password = $_POST['password'];
$result = $userObj->login($userID, $password);
if($result == true){
header("location: index.php");
exit();
}else{
echo "There was a login error";
exit();
}
}else{
echo "<h1>Login</h1>";
echo "<form method='post' action>
<input type='text' name='userid' placeholder='Username/Email'>
<input type='password' name='password' placeholder='password'><br>
<input type='submit' name='loginUser' value='Login'>
</form>";
}
}
ob_end_flush();
SQL: Dump file
-- Table structure for table `db_cms_users`
--
CREATE TABLE `db_cms_users` (
`id` int(11) NOT NULL,
`username` text NOT NULL,
`password` text NOT NULL,
`email` text NOT NULL,
`status` int(11) NOT NULL DEFAULT '0',
`is_admin` int(11) NOT NULL DEFAULT '0',
`registration` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
--
-- Dumping data for table `db_cms_users`
--
INSERT INTO `db_cms_users` (`id`, `username`, `password`, `email`, `status`, `is_admin`, `registration`) VALUES
(1, 'test', '$2y$10$QNKXEo3pnGPCjUMnfXlV..JJ4OFcSQJ5EVg75xOjlE7p5pL7Dqwau', 'test#email.com', 1, 1, '2021-11-13 20:21:28');
Thanks for the big update. The context is very useful. It seems like what you're trying to do isn't logical or necessary.
To begin with, everything starts off well. In your loginUser() function, you're correctly fetching the user details based on the email / username. Then you're verifying the password the right way using password_verify(). That's all fine and sensible.
But the bit you seem to be having trouble with doesn't make a whole lot of sense. It looks like after you've verified the password, you then make another query to get the same user - except that this time you're trying to put the password into the WHERE clause. This makes no sense because
You've already found the user with the first query and verified them, and
the raw password will never match the one in the database because the one in the database is hashed (as it should be, hence why you used password_verify() earlier in the code).
You really don't need that second SELECT query - what are you trying to achieve with it?
If you just change the first query to select more fields, then your problem is solved - you can put those details directly into the Session without needing to run another query:
protected function loginUser($userID, $password) {
$sql = "SELECT username, id, password FROM db_cms_users WHERE username = ? OR email = ?";
$stmt = $this->connect()->prepare($sql);
if(!$stmt->execute([$userID, $userID])) {
$stmt = null;
header("location: index.php?error=failstmt");
exit();
}
if($stmt->rowCount() == 0) {
$stmt = null;
header("location: login.php?error=loginerror");
exit();
}
$user = $stmt->fetchAll();
$checkPwd = password_verify($password, $user[0]['password']);
if($checkPwd == false) {
header("location: index.php?error=wrongpwd");
exit();
}
elseif($checkPwd == true) {
session_start();
$_SESSION['username'] = $user[0]['username'];
$_SESSION['uid'] = $user[0]['id'];
return true;
}
}
P.S. Security best practice recommends that when the credentials are not valid you don't let the user know whether it was the username or password (or both) which was the problem. For example if you disclose that the username was wrong, it indicates to a malicious party that they can discard that username and try another one, and equally if you disclose that only the password is incorrect you are indicating that they should keep attempting to crack the password for that username. You should simply state "invalid credentials" in either case, which then does not give any clues about how to narrow the search for a valid login.
Related
I have three files that are relevant for this part of my login scenario:
/project/index.html
/project/api/user/login.php
/project/api/objects/user.php
The index.html has a simple login form in it, calling the ./api/user/login.php.
In this form I have a checkbox that is an option for the user in order to stay logged in or not.
If the user has selected this option, with every login, I would like to check if the credentials are correct (login function -> stmt1 in user.php) as well as to update the lastlogin (datetime), the identifier and securitytoken if the checkbox was set (login function -> stmt2 in user.php).
The user.php is included_once in the login.php that gets the values out of the index.html form and sends them to the login() function in the user.php.
Depending on the functions return value, the login.php decides if the login was successful or not.
The login itself (stmt1) works, but the update of lastlogin, identifier and securitytoken (stmt2) doesn't.
login.php
session_start();
// include database and object files
include_once '../config/database.php';
include_once '../objects/user.php';
// get database connection
$database = new Database();
$db = $database->getConnection();
// prepare user object
$user = new User($db);
// set ID property of user to be edited
$user->username = isset($_GET['username']) ? $_GET['username'] : die();
$user->password = base64_encode(isset($_GET['password']) ? $_GET['password'] : die());
$user->remember = isset($_GET['remember']) ? $_GET['remember'] : die();
$stmt1 = $user->login();
if($stmt1->rowCount() > 0){
// get retrieved row
$row1 = $stmt1->fetch(PDO::FETCH_ASSOC);
$_SESSION['userid'] = $row1['uid'];
// create array
$user_arr=array(
"status" => true,
"message" => "Login erfolgreich!",
"uid" => $row1['uid'],
"username" => $row1['username']
);
$stmt2 = $user->login();
$row2 = $stmt2->fetch(PDO::FETCH_ASSOC);
print_r($row2);
// create array
$user_arr=array(
"lastlogin" => $row2['lastlogin']
);
}
else{
$user_arr=array(
"status" => false,
"message" => "Benutzername und/oder Passwort nicht korrekt!",
);
}
// make it json format
print_r(json_encode($user_arr));
?>
user.php
function login(){
// select all query
$query1 = "SELECT
`uid`, `username`, `email`, `password`, `created`, `lastlogin`
FROM
" . $this->table_name . "
WHERE
username='".$this->username."' AND password='".$this->password."'";
// prepare query statement
$stmt1 = $this->conn->prepare($query1);
// execute query
$stmt1->execute();
return $stmt1;
// set up the remain logged in function
if(isset($this->remember)) {
$identifier = random_string();
$securitytoken = random_string();
$remember = ",identifier='".$identifier."',securitytoken='".$securitytoken."'";
setcookie("identifier",$identifier,time()+(3600*24*365)); //1 year valid
setcookie("securitytoken",$securitytoken,time()+(3600*24*365)); //1 year valid
} else {
$remember = "";
}
// update last login
$query2 = "UPDATE
" . $this->table_name . "
SET
`lastlogin` = '".date("Y-m-d H:i:s")."'
".$remember."
WHERE
username='".$this->username."' AND password='".$this->password."'";
// prepare query statement
$stmt2 = $this->conn->prepare($query2);
// execute query
$stmt2->execute();
return $stmt2;
}
function random_string(){
if(function_exists('random_bytes')) {
$bytes = random_bytes(16);
$str = bin2hex($bytes);
} else if(function_exists('openssl_random_pseudo_bytes')) {
$bytes = openssl_random_pseudo_bytes(16);
$str = bin2hex($bytes);
} else if(function_exists('mcrypt_create_iv')) {
$bytes = mcrypt_create_iv(16, MCRYPT_DEV_URANDOM);
$str = bin2hex($bytes);
} else {
//secret key should have >12 random chars
$str = md5(uniqid('SECRET KEY', true));
}
return $str;
}
In the user.php after return $stmt1;
The code is returned and the cookies are not set
I would do this... Check login... If true, save cookies with id and token
And then periodically check if token and id correspond... If so... Just UPDATE the last login time.
Note: your prepared statement is vulnerable!! Dont append the parameters with '.' use placeholders instead, and dont encode the password, is better to hash it... Then compare hashes
My login system works fine so far. I can put the correct username and password and it will login but not vice versa. The only thing that does not work correctly is the status check. If the user has a status of '0' it should not login. So I tried to print out the status and nothing showed. Then I did the same with the password_hash and I get the same result (which is weird since password_verify() has no issue). The user entered password does print so this must be a mysqli_result thing. Any help?
include_once($_SERVER['DOCUMENT_ROOT'] . "/php_scripts/db_conx.php");
function CheckLogin($username, $password, $db_conx){
time_nanosleep(0, 100000000);
$userQuery = $db_conx->prepare("SELECT * FROM users WHERE username = ?");
$userQuery->bind_param('s', $username);
$userQuery->execute();
$userResult = $userQuery->get_result();
if($userResult->num_rows == 1){
$password_hash = $userResult->fetch_assoc()['password'];
$status = mysqli_fetch_assoc($userResult)['status']; // $status turns out null
echo($status); // Prints nothing
printf("%s\n", $userResult->fetch_assoc()['password']); // Prints nothing
print_r($userResult->fetch_assoc()); // Prints nothing
print($password); //Prints the password correctly
if(password_verify($password, $password_hash) && $status != '0'){
//password_verify() works like a charm
//Status check is the only thing that does not work
return true;
} else{
return false;
}
}
return false;
}
The user has a status of '0' so it should not login.
You're advancing the cursor 4 positions but probably only have 1 row. Use fetch only once and assign it.
$row = $userResult->fetch_assoc();
then you can assign your variables.
$password_hash = $row['password'];
$status = $row['status'];
and they will be accessible.
You also could just check the status in the query:
SELECT * FROM users WHERE username = ? and status <> 0
or
SELECT * FROM users WHERE username = ? and status = 1
I have code, which should use access levels, but from the database my code is not requesting the access level and I do not get into my "admin.php"
Can you help?
Login page is here:
Login-Page
Credentials: test / test
I want to let users login into my page, which is access for recruiters of me. So I can create users, they see my CV and others. Therefor it is neccessary to set user levels like "1" for admin.php "2" for admin2.php and so on.
Here's the check.php which is a form from the index.php
<?php
session_start();
require_once("inc/config.inc.php");
require_once("inc/functions.inc.php");
$error_msg = "";
if(isset($_POST['uname']) && isset($_POST['pwd'])) {
$email = $_POST['uname'];
$passwort = $_POST['pwd'];
$statement = $pdo->prepare("SELECT * FROM users WHERE email = :uname");
$result = $statement->execute(array('uname' => $email));
$user = $statement->fetch();
//Überprüfung des Passworts
if ($user !== false && password_verify($passwort, $user['pwd'])) {
$_SESSION['userid'] = $user['id'];
$access_level = $user['access_level'];
$_SESSION['access_level'] = $access_level;
//Möchte der Nutzer angemeldet beleiben?
if(isset($_POST['angemeldet_bleiben'])) {
$identifier = random_string();
$securitytoken = random_string();
$insert = $pdo->prepare("INSERT INTO securitytokens (user_id, access_level, identifier, securitytoken) VALUES (:user_id, :access_level, :identifier, :securitytoken)");
$insert->execute(array('user_id' => $user['id'], 'access_level' => $access_level, 'identifier' => $identifier, 'securitytoken' => sha1($securitytoken)));
setcookie("identifier",$identifier,time()+(3600*24*365)); //Valid for 1 year
setcookie("securitytoken",$securitytoken,time()+(3600*24*365)); //Valid for 1 year
}
if ($access_level==0){
header("Location:user.php");
}
else if($access_level==1){
header("Location:admin.php");
}
}
else{
header("Location:index.php?err=1");
}
} else {
$error_msg = "E-Mail oder Passwort war ungültig<br><br>";
}
$email_value = "";
if(isset($_POST['email']))
$email_value = htmlentities($_POST['email']);
?>
The problem was here in line
<?php
$statement = $pdo->prepare("SELECT * FROM users WHERE email = :uname");
there's no table named "email" - so it's not possible to get a true request.
I changed it to:
<?php
$statement = $pdo->prepare("SELECT * FROM users WHERE uname = :uname");
After that, my page gives me the output:
Hello This is admin page.
Thanks at all of you for your help! :)
May be your mistake is here.
$access_level = ['access_level'];.
it should be like
$access_level = $user['access_level'];.
and I don't get your situation.
other mistake is may be here.
if(isset($_POST['angemeldet_bleiben'])) {
$identifier = random_string();
$securitytoken = random_string(); //remaining code....
}
else if ($access_level==0){
header("Location:user.php");
}
else if($access_level==1){
header("Location:admin.php");
}
don't you think it should be like.
if ($access_level==0){ //removed else.
header("Location:user.php");
}
else if($access_level==1){
header("Location:admin.php");
}
because if first if condition worked then it skips all other else if statements.
Did you check print_r($user)? What is returned? What datatype has your column "access_level" in your database?
Maybe you have to check $access_level=='0', if it is type string.
How can i limit the failed logins with this script? If the login fails, i insert it into the sql. (Is it the right way?)
But how can i check at the next login, that the user can now log in? I would take the login limit in 1 hour.
Aniway, is this code is good for that?
<?php
$loginError = array();
if(isset($_POST['login_submit']))
{
if(empty($_POST['email']) or !isset($_POST['email'])){$loginError[] = "Hiányzó email cím.";}
if(empty($_POST['pass']) or !isset($_POST['pass'])){$loginError[] = "Hiányzó jelszó.";}
if(strlen($_POST['email']) > 50 ){$loginError[] = "Hibás adat az email mezőben.";}
if(strlen($_POST['pass']) > 40 ){$loginError[] = "Hibás adat a jelszó mezőben.";}
if(count($loginError) == 0 )
{
$email = mysqli_real_escape_string($kapcs,$_POST['email']);
$pass = sha1($_POST['pass']);
$lekerdezes = mysqli_query($kapcs, "SELECT * FROM admin_user WHERE email = '$email'") or die(mysqli_error($kapcs));
if(mysqli_num_rows($lekerdezes) > 0 )
{
$adat = mysqli_fetch_assoc($lekerdezes);
if($adat['status'] == 1 )
{
if($adat['pass'] == $pass)
{
$_SESSION['adatok'] = $adat;
$_SESSION['email'] = $adat['email'];
$_SESSION['userid'] = $adat['id'];
header("Location:home.php");
}
else
{
$sql = "INSERT INTO loginattempts(log_address, log_datetime) VALUES ('".$_SERVER['REMOTE_ADDR']."', NOW())";
$insert_login_attempt = mysqli_query($kapcs, $sql) or die(mysqli_error($kapcs));
$loginError[] = "Hibás email cím vagy jelszó.";
}
}
else
{
$sql = "INSERT INTO loginattempts(log_address, log_datetime) VALUES ('".$_SERVER['REMOTE_ADDR']."', NOW())";
$insert_login_attempt = mysqli_query($kapcs, $sql) or die(mysqli_error($kapcs));
$loginError[] = "Még nincs aktiválva a fiók.";
}
}
else
{
$sql = "INSERT INTO loginattempts(log_address, log_datetime) VALUES ('".$_SERVER['REMOTE_ADDR']."', NOW())";
$insert_login_attempt = mysqli_query($kapcs, $sql) or die(mysqli_error($kapcs));
$loginError[] = "Hibás email cím vagy jelszó.";
}
}
}
?>
I would create a field in the database called status (blocked/ok) and assuming youve got a field timestamp for the last login...
Then Id connect to the database in case the login fails and save the status bloqued and the time stamp. the next attempt you would check the time.now vs last access...
I good suggestion would be create a function for the database connection so you can call it a couple of time without repeat the code, also dont forget use the try/except fot the db connection.
I have problem in little project,
how can I save table data in session?
<?php
session_start();
include 'connect.php';
if (isset($_POST["email"]))
{
$email = $_POST["email"];
$password = $_POST["password"];
$r=mysql_query("SELECT * FROM user_login WHERE `uemail` ='".$email."' AND `upass` = '".$password."'");
$s = $_POST["userid"];
$n=mysql_query("SELECT * FROM user_data WHERE `userid` ='".$s."'");
$q=mysql_fetch_assoc($n);
$_SESSION["name"]=$q["nfname"];
$k=mysql_num_rows($r);
if ($k>0)
{
header("location:user/index.php");
}
else
header("location:login.php");
}
?>
this code not working !! :(
please help !
You probably just missed the
session_start();
But here is the dildo (deal tho) xD
Your Login script is not secure, try this at the top of your index.php or whatever rootfile you have.
<?php
session_start();
function _login($email, $password) {
$sql = "SELECT * FROM user_login
WHERE MD5(uemail) ='".md5(mysql_real_escape_string($email))."'
AND MD5(upass) = '".md5(mysql_real_escape_string($password))."'";
$qry = mysql_query($sql);
if(mysql_num_rows($qry) > 0) {
// user with that login found!
$sql = "UPDATE user_login SET uip = '".$_SERVER['REMOTE_ADDR']."', usession = '".session_id()."'";
mysql_query($sql);
return true;
} else {
return false;
}
}
function _loginCheck() {
$sql = "SELECT * FROM user_login WHERE uip = '".$_SERVER['REMOTE_ADDR']."' AND MD5(usession) = '".md5(session_id())."'";
$qry = mysql_query($sql);
if(mysql_num_rows($qry) > 0) {
// user is logged in
$GLOBALS['user'] = mysql_fetch_object($qry);
$GLOBALS['user']->login = true;
} else {
// user is not logged in
$GLOBALS['user'] = (object) array('login' => false);
}
}
if(isset($_POST['login'])) {
if(_login($_POST["email"], $_POST["password"])) {
// login was successfull
} else {
// login failed
}
}
_loginCheck(); // checkes every Page, if the user is logged in or if not
if($GLOBALS['user']->login === true) {
// this user is logged in :D
}
?>
Ok, I'll bite. First 13ruce1337, and Marc B are right. There is a lot more wrong with this than not being able to get your data into your session.
Using PDO ( as 13ruce1337 links you too ) is a must. If you want to keep using the same style of mysql functions start reading up on how. Marc B points out that session_start(); before any html output is required for sessions to work.
As for your code, you got along ways to go before it is ready for use but here is an example to get you started
if (isset($_POST["email"])) {
//mysql_ functions are being deprecated you can instead use
//mysqli_ functions read up at http://se1.php.net/mysqli
/* Manage your post data. Clean it up, etc dont just use $_POST data */
foreach($_POST as $key =>$val) {
$$key = mysqli_real_escape_string($link,$val);
/* ... filter your data ... */
}
if ($_POST["select"] == "user"){
$r = mysqli_query($link,"SELECT * FROM user_login WHERE `uemail` ='$email' AND `upass` = '$password'");
/* you probably meant to do something with this query? so do it*/
$n = mysqli_query($link,"SELECT * FROM user_data WHERE userid ='$userid'");
//$r=mysql_fetch_assoc($n); <- this overrides your user_login query
$t = mysqli_fetch_array($n);
$_SESSION["name"] = $t['nfname'];
/* ... whatever else you have going on */