Just curious if my authentication is secure enough, really. Also wondering if I should ditch the secret
variable and just use password
everywhere instead.
Note: the method logic must remain seperated because the same User object will have CRUD methods added at a later date.
The table structure:
CREATE TABLE IF NOT EXISTS `users` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(255) NOT NULL,
`password` varchar(255) NOT NULL,
`secret` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=2 ;
The class:
class User {
public $db;
public $id;
public $username;
public $password;
public $secret;
public $signed_in = false;
function __construct ($db) {
$this->db = $db;
}
function redirect ($url) {
header('Location: '.$url);
die();
}
function exists () {
$sql = "select * from `users` where `username`=:username";
$stm = $this->db->prepare($sql);
$stm->bindParam(':username', $_POST['username']);
$stm->execute();
$res = $stm->fetch();
if (!empty($res['password'])) {
if (password_verify($_POST['password'], $res['password'])) {
$this->id = $res['id'];
$this->username = $res['username'];
return true;
}
}
return false;
}
function signIn () {
$this->secret = password_hash($_POST['password'], PASSWORD_DEFAULT);
if ($_POST['remember']) {
setcookie('username', $this->username, time() + 31556926, '/');
setcookie('secret', $this->secret, time() + 31556926, '/');
}
$_SESSION['username'] = $this->username;
$_SESSION['secret'] = $this->secret;
$sql = "update `users` set `secret`=:secret where `id`=:id";
$stm = $this->db->prepare($sql);
$stm->bindParam(':secret', $this->secret);
$stm->bindParam(':id', $this->id);
$stm->execute();
}
function signedIn () {
if (!empty($_COOKIE['username'])) {
$this->username = $_COOKIE['username'];
$this->secret = $_COOKIE['secret'];
}
else if (!empty($_SESSION['username'])) {
$this->username = $_SESSION['username'];
$this->secret = $_SESSION['secret'];
}
$sql = "select * from `users` where `username`=:username and `secret`=:secret";
$stm = $this->db->prepare($sql);
$stm->bindParam(':username', $this->username);
$stm->bindParam(':secret', $this->secret);
$stm->execute();
$res = $stm->fetch();
if ($res) {
array_walk_recursive($res, 'sanitize');
foreach ($res as $k => $v) {
if (property_exists($this, $k)) {
$this->$k = $v;
}
}
$this->signed_in = true;
}
}
}
How the class is used at the top of all pages in the app:
$user = new User($db); // create user object, $db is a PDO object
$user->signedIn(); // determine if they're signed in, populate $user->signed_in boolean
Signing the user in via a form:
if (isset($_POST['username'])) {
if ($user->exists()) {
$user->signIn();
$user->redirect('dashboard.php');
}
}
Any feedback is greatly appreciated. Like I said my primary concern here is security.