2

I am in the process of creating a login system which uses both sessions (for those who disallow the use of cookies (to agree with the cookie law.. I am using the site http://www.cookielaw.org/the-cookie-law.aspx as a reference)

Now, I have this system for my cookie authentication

function GenerateString(){
        $length = mt_rand(0,25);
        $characters = '0123456789abcdefghijklmnopqrstuvwxyz';
        $string = '';

        for ($p = 0; $p < $length; $p++) {
            $string .= $characters[mt_rand(5, strlen($characters) -1)];
        }
        return $string;
}
$RandomString = GenerateString();

$CookieAuth = $DB->prepare("INSERT INTO cookieauth (Username,RandomString) VALUES (?,?)");
$CookieAuth->bind_param('ss',$_POST['Username'],$RandomString); 
$CookieAuth->execute(); // Insert the Authentication Methods into the database 
$CookieAuth->close(); // Allow another query/statement

$GetInsertID = $DB->prepare("SELECT ID FROM CookieAuth WHERE RandomString=?");
$GetInsertID->bind_param('s',$Randomstring);
$GetInsertID->execute();
$GetInsertID->bind_result($RowID);
$GetInsertID->fetch();
$GetInsertID->close(); 

setcookie("Auth[ID]",$RowID);
setcookie("Auth[UName],$_POST['Username']);
setcookie("Auth[RandomString]",$RandomString);

Then to process the cookie:

if(isset($_COOKIE['Auth'])){
   $Authenticate = $DB->prepare("SELECT Username,RandomString FROM cookieauth WHERE ID=?");
   $Authenticate->bind_param('i',$_COOKIE['Auth']['ID']);
   $Authenticate->execute();
   $Authenticate->bind_result($RowUsername,$RowString);
   $Authenticate->fetch();
   $Authenticate->close();

if ($_Cookie['Auth']['UName'] == $RowUsername){
    if ($_COOKIE['Auth']['RandomString'] == $RowString){
        header("Location: LoggedIn.php");
    }else{
        die("Possible Cookie Manipulation, Autologin Cannot Continue");
    }
}else{
    die("Possible Cookie Manupulation, Autologin Cannot Continue!");
}

My overall objective is to provide an auto login feature by using cookies. As people should know they are essentially stored on the hard drive as plain text.. So If i include a randomly generated string which will be changed on further processing each time (then updating the cookie to match the database) is this a reasonably secure way to achieve the task? I mean, I understand that this is not 100% secure due to some users might attempt to manipulate the random string, so I could resort to a salt, random key then use hash_hmac to sha512 the salt+key and save that as the cookie...

My overall question, is the chunks I have provided a semi-secure method to process automatic logins via cookies and can minimize the possibility of some bad guys manipulating the keys to achieve the required data?

Daryl Gill
  • 5,464
  • 9
  • 36
  • 69
  • If someone can GUESS a valid PHP session ID (assuming standard session ID length/complexity settings), then you can't keep them out. there's no point in generating your own codes, when PHP is already generating random ones. – Marc B Apr 28 '13 at 00:44
  • The random generation or the key will change the cookie on each entry to the website, update my database & cookie to match, each users row will be different, each key for each user will be different.. So I guess implimenting some sort of algorithm might assist in security? – Daryl Gill Apr 28 '13 at 00:46
  • sessions can do that too... session_regenerate_id(). you're mostly trying to reinvent the wheel. – Marc B Apr 28 '13 at 00:48
  • You might be finding the following link helpful: http://web.archive.org/web/20130214051957/http://jaspan.com/improved_persistent_login_cookie_best_practice – M8R-1jmw5r Apr 30 '13 at 01:14
  • Exact duplicate: http://stackoverflow.com/q/5459682/338665 or http://stackoverflow.com/q/2336678/338665 (I'd mark it against the second)... – ircmaxell Apr 30 '13 at 20:03

1 Answers1

6

Introduction

Why do you want to authenticate cookie when that is exactly what sessions are going ? If you want to change the ID you can easily achieve that with session_regenerate_id as @MarcB has pointed.

My Assumptions

I want to assume i did not understand the question clearly and probably this is what you want to achieve

  • Store Values to Cookie
  • Know if such values have been modified

You solved it already

I could resort to a salt, random key then use hash_hmac to sha512 the salt+key and save that as the cookie...

That is exactly the the solution but you need to note that

  • Session Can Sill be hijacked
  • PHP has better ways of generating random strings
  • Imagine the overhead having to updated your mysql table every time for something sessions can easily do for you
  • using hash_hmac 512 would generate 126 in hex format you need to understand that there is Browser Cookie Limits so i suggest you reduce it to 256

Your Solution Modified

If we are going to use your solution we need some little modification

session_start();

// Strong private key stored Securly stored
// Used SESSION For demo
$privateKey = isset($_SESSION['key']) ? $_SESSION['key'] : mcrypt_create_iv(128, MCRYPT_DEV_URANDOM);

$my = new SignedCookie($privateKey);
$my->setCookie("test", "hello world", time() + 3600);
echo $my->getCookie("test");

Output

  hello world 

But the Data was stored like this :

enter image description here

This just uses hash_hmac to sign and verify your values and also uses a random variable to make sure the bad guys are not able to build table of possible values because really they don't have to break the hash .. the can just study it can also use a valid one previously used eg.

10 Cookies = AAAA
1 Cookie = BBBB

He can login with valid session and changed cookies from BBBB to AAAA so even if you re not storing to database always include a random argument

You can also still remove the cookies like this :

 $my->setCookie("test", null, time() - 3600);

Simple Class Used

class SignedCookie {
    private $prifix = '$x$';
    private $privateKey;

    function __construct($privateKey) {
        $this->privateKey = $privateKey;
    }

    function setCookie($name, $value, $expire, $path = null, $domain = null, $secure = null, $httponly = null) {
        $value = $value === null ? $value : $this->hash($value, mcrypt_create_iv(2, MCRYPT_DEV_URANDOM));
        return setcookie($name, $value, $expire, $path, $domain, $secure, $httponly);
    }

    function getCookie($name, $ignore = false) {
        if (! isset($_COOKIE[$name]) || empty($_COOKIE[$name]))
            return null; // does not exist

        if ($ignore === false) {
            if (substr($_COOKIE[$name], 0, 3) !==  $this->prifix)
                return - 1; // modified

            $data = pack("H*", substr($_COOKIE[$name], 3)); // Unpack hex

            $value = substr($data, 32, - 2); // Get Value
            $rand = substr($data, - 2, 2); // Get Random prifix

            if ($this->hash($value, $rand) !== $_COOKIE[$name])
                return - 1; // modified

            return $value;
        }
        return $_COOKIE[$name];
    }

    function hash($value, $suffix) {
        // Added random suffix to help the hash keep changing
        return $this->prifix . bin2hex(hash_hmac('sha256', $value . $suffix, $this->privateKey, true) . $value . $suffix);
    }
}

Conclusion

You are not a security expert Just Use so just use SSL (SSL also has its issues but far better) or Look for an existing secure authentication service. @ircmaxell reminded me of Schneier's Law recently :

@Baba: "surprise" is the enemy of security. The ONLY thing that should be secret is the private key. Remember Schneier's Law: Anyone can invent an encryption scheme that they themselves can't break. My answer is based on tried and true cryptographic principles.

well i think you should take to that advice too.

Baba
  • 94,024
  • 28
  • 166
  • 217
  • I really like this answer, thank you for your time into posting something like this. Another off-topic question would be what application are you using to view the cookie data? – Daryl Gill Apr 30 '13 at 23:46
  • `F12 in chrome` or `Firebug in Firefox` .. there so many tools around – Baba May 01 '13 at 09:41