My user authorization process looks like that.
During signin process function fires following code.
$token = sha1(microtime(true) . mt_rand(10000, 90000));
setcookie('auth', $token, $timeout);
Then adds generated token into database table right after user id column. BTW, in both $_SESSION and $_COOKIE storing only $token value.
There is another function named protect that comes at the top of every page and checks if cookies exist:
- at first checks db table for
$token: if there is no user with this
token, signs out - Then checks
$_SERVER['HTTP_USER_AGENT'],$_SERVER['REMOTE_ADDR']with ones that stored in db tables.
Question
I feel that this is not secure way, because if attacker uses same PC and same browser, can get access simply by "copying-pasting" cookies. Also $_SERVER[‘REMOTE_ADDR’] doesn’t always work.
How can I make this login more secure?
Detailed
Here is function protect
public function protect() {
if (!isset($_SESSION)) {
session_start();
}
$data = array();
if (isset($_SESSION['auth'])) {
$stmt = $this->db->prepare("SELECT l.browser, l.ip, u.ban from log AS l, users AS u WHERE l.token =? AND u.id=l.user_id LIMIT 1") or die($this->db->error);
$stmt->bind_param("i", $_SESSION['auth']) or die($stmt->error);
$stmt->execute() or die($stmt->error);
$stmt->store_result();
if ($stmt->num_rows == 0) {
$this->signout();
}
$stmt->bind_result($data['browser'], $data['ip'], $data['ban']);
$stmt->fetch() or die($stmt->error);
$stmt->close() or die($stmt->error);
$this->validation->check("protection", $data);
} else {
if (!isset($_COOKIE['auth'])) {
header('Location:' . wsurl);
}
$stmt = $this->db->prepare("SELECT l.browser, l.timeout, l.ip, u.ban from log AS l, users AS u where l.token =? AND u.id=l.user_id LIMIT 1") or die($this->db->error);
$stmt->bind_param("s", $_COOKIE['auth']) or die($stmt->error);
$stmt->execute() or die($stmt->error);
$stmt->store_result();
if ($stmt->num_rows == 0) {
$this->signout();
}
$stmt->bind_result($data['browser'], $data['timeout'], $data['ip'], $data['ban']) or die($stmt->error);
$stmt->fetch() or die($stmt->error);
$this->validation->check("protection", $data);
session_regenerate_id();
$_SESSION['auth'] = $_COOKIE['auth'];
$stmt->close() or die($stmt->error);
}
}
And Validation checks this
if ($data['browser'] != md5($_SERVER['HTTP_USER_AGENT'])) {
$this->registration->signout();
}
if ($data['ban'] == 1) {
$this->registration->signout(false);
header('Location:' . wsurl . "?page=msg&id=34");
}
if ($data['ip'] != $this->common->getIP("long")) {
$this->registration->signout();
}
if (isset($data['timeout']) && !empty($data['timeout'])) {
if (($data['timeout'] - $this->common->getTime()) < 0) {
$this->registration->signout();
}
}
There’s not much you can do without affecting the usability of your app. For example you could use the user IP, user agent, or a mixture of user supplied environmental variables as a salt for your token, but if the users IP changed or any of the environmental variables, which aren’t necessarily static, it would not log them in again.
Honestly though, this is not a security issue YOU should worry about, this is the users responsibility. If someone can get on their computer and copy their cookies, they can probably take the extra step of extracting their passwords directly (easy if they use Firefox ..).