1

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:

  1. at first checks db table for $token: if there is no user with this token, signs out
  2. 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();
            }
        }
Community
  • 1
  • 1
Tural Ali
  • 22,202
  • 18
  • 80
  • 129
  • Your solution looks quiet good. I might save `$_SERVER['HTTP_USER_AGENT']` and `$_SERVER['REMOTE_ADDR']` to the database, too. Later on, you can check these values against the actual values instead the ones saved in the cookie. – SecStone Feb 08 '12 at 18:17
  • @SecStone so you think, I need to save only token in cookies, all other params in db, right? – Tural Ali Feb 08 '12 at 18:19
  • Exactly, because these values could be easily manipulated. However, if you'd like to have more security, the only solution is to use SSL to avoid capturing the traffic (e.g. if you surf in a public WiFi network and someone copies the cookies which are sent along with every request.) – SecStone Feb 08 '12 at 18:51

2 Answers2

4

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 ..).

Naatan
  • 3,424
  • 4
  • 32
  • 51
  • So, what do you think about my method? Is that secure enough? – Tural Ali Feb 08 '12 at 18:11
  • 1
    In my opinion you can remove the user agent and ip address, you are making authentication more troublesome for the end-user, just to resolve a use-case scenario that has nothing to do with your app. Unless your system needs extremely tight security, in which case you shouldn't be saving their authenticated session in a cookie to begin with. – Naatan Feb 08 '12 at 18:14
  • But if I don't save some params in cookie how will Rememmber me work? By setting timeout in db table? – Tural Ali Feb 08 '12 at 18:21
  • I don't get your question, how will you maintain their active session? You can use PHP sessions or you can use cookies but make them session cookies (ie. don't set a TTL). – Naatan Feb 08 '12 at 18:33
  • Look, I think I can do the same thing with only session: if rememmber me ticked, by setting timeout date in db table and checking every time for this value I can use only session (without cookies): if current time - timeout < 0 then signout else, regenerate session. How about this? – Tural Ali Feb 08 '12 at 18:40
  • 1
    Sorry I can't really follow what you're saying, can you rephrase? If you're saying you will use php session's to connect the active session to session data in a DB and you will expire this DB data after a certain amount of inactivity then I'd say that's a good approach. – Naatan Feb 08 '12 at 18:46
  • No-no-no. Let's say we save $token in session: $_SESSION['auth']. We don't use any cookie. During login, we're checking if user ticked "Rememmber me". If yes: we add record into "timeout" column. For ex, time() + 60 * 60 * 24 * 7 (one week). Then every time protect() function checks for this timeout value. If timeout expired, script directly signs out user(deletes all session variables .. etc), else regenerates session id and $_SESSION['auth'] – Tural Ali Feb 08 '12 at 18:52
  • PHP Sessions's only remember session based data, hence the name :p So when a user leaves and comes back a new session would be created. If you want to remember the users authentication across sessions you use cookies. I was simply suggesting the use of sessions over cookies as you were concerned about security, For the record, anything you save in a $_SESSION variable is only visible to the server, the client does not have any visibility of this data. So you wouldn't have to use tokens in that case. – Naatan Feb 08 '12 at 20:07
1

I may have misunderstood - but an attacker who can steal a cookie could steal the session by creating a cookie and adding the HTTP_USER_AGENT, REMOTE_ADDRelements to the stolen token.

I would set the token, and have a database table with

TOKEN    REMOTE_ADDR  HTTP_USER_AGENT
------------------------------------

columns. Your protect() method should check the token and then compare the current user's remote address and user agent against the database.

That way, an attacker who can steal your cookie would also have to be able to mimic your browser (trivial), and IP address (a lot harder, but still do-able).

Also, check this question.

Community
  • 1
  • 1
Neville Kuyt
  • 29,247
  • 1
  • 37
  • 52
  • Have taken a look at the question: "Store authentication details on the server. That is, don't send details such as username in the cookie." But if I don't save some params in cookie how will Rememmber me work? By setting `timeout` column some value in db table? Also "You can also check the IP address. But this causes problems for users that have changing IP address due to load balancing on multiple internet connections etc (which is the case in our environment here)." I'm bit confused, to store ip or not to store? – Tural Ali Feb 08 '12 at 18:29
  • By keeping a token in the cookie, and keeping that same token in a database or something on your server. Use the token to authenticate them. IP address is not reliable - someone who flips from one address to the other could lose their session. On the other hand, if you don't use IP address in this scheme, anyone who can steal the cookie can steal the session. Also worth reading up on the basic PHP session framework - that may well do everything you need out of the box... – Neville Kuyt Feb 08 '12 at 18:45
  • Let's say we save $token in session: $_SESSION['auth']. We don't use any cookie. During login, we're checking if user ticked "Rememmber me". If yes: we add record into "timeout" column. For ex, time() + 60 * 60 * 24 * 7 (one week). Then every time protect() function checks for this timeout value. If timeout expired, script directly signs out user(deletes all session variables .. etc), else regenerates session id and $_SESSION['auth'] . What do you think about this solution? – Tural Ali Feb 08 '12 at 18:54