11

我想知道 cookie 登录最安全的方式是什么?如果您只是将通行证(用盐加密)和用户名存储在 cookie 中并针对用户表进行验证,则潜在的攻击者可以窃取 cookie 并登录。人们通常不会在那里查看“最后一次在线”。

那么“记住我的饼干”有更好的方法吗?IP不是一个好的选择,是吗?(有些机器一直在改变IP)。

4

3 回答 3

21

我想我找到了一个聪明的解决方案!

这个(复杂的?)脚本的优点:

  • 当用户在选中记住我的情况下成功登录时,除了标准会话管理 cookie 之外,还会发出一个登录 cookie。[2]
  • 登录 cookie 包含用户的用户名、系列标识符和令牌。系列和令牌是来自适当大空间的不可猜测的随机数。这三者一起存储在数据库表中。
  • 当未登录用户访问该站点并显示登录 cookie 时,会在数据库中查找用户名、系列和令牌。
  • 如果存在三元组,则认为用户已通过身份验证。使用的令牌将从数据库中删除。生成一个新令牌,并使用用户名和相同的系列标识符存储在数据库中,并向用户发出一个包含所有三个的新登录 cookie。
  • 如果存在用户名和系列但令牌不匹配,则假定为盗窃。用户会收到措辞强硬的警告,并且用户记住的所有会话都将被删除。
  • 如果用户名和系列不存在,则忽略登录 cookie。

我在数据库中创建了一个包含以下信息的表:

    session | token | username | expire

记住我的 cookie 将具有以下设置:

    $value = "$session|$token|$userhash"; //Total length = 106
  • Session将是一个 40 (sha1) 个字符的字符串。
  • Token将是一个 32 (md5) 个字符的字符串。
  • Userhash在 cookie 中将是 32 个(用户名的 md5)字符的字符串。
  • Username在数据库中将是正常的用户名。
  • Expire现在将是 + 60 天。

剧本:

if(isset($_SESSION['check']) || $_SESSION['check']){
    //User is logged in
}else if(isset($_COOKIE['remember']) && strlen($_COOKIE['remember'])==106){
    //THERE is a cookie, which is the right length 40session+32token+32user+2'|'
    //Now lets go check it...
    conncectdb(); //Sets connection
    //How do I protect this script form harmful user input?
    $plode = explode('|',$_COOKIE['remember']);
    $session = mysql_real_escape_string($plode[0]);
    $token = mysql_real_escape_string($plode[1]);
    $userhash = mysql_real_escape_string($plode[2]);
    $result = mysql_query(" SELECT user 
                FROM tokens 
                WHERE session = '$session' 
                AND token = '$token'
                AND md5(user) = '$userhash';")
    if(mysql_num_rows($result)==1){
        //COOKIE is completely valid!
        //Make a new cookie with the same session and another token.
        $newusername = mysql_result($result,0,0);
        $newsession = $session;
        $newtoken = md5(uniqid(rand(), true));
        $newuserhash = md5($username);
        $value = "$newsession|$newtoken|$newuserhash";
        $expire = time()+4184000;
        setcookie('remember', $value, $expire, '/', 'www.example.com', isset($_SERVER["HTTPS"]), true);
        mysql_query("   UPDATE tokens 
                SET token='$newtoken', expire='$expire'
                WHERE session = '$session' 
                AND token = '$token'
                AND md5(user)='$userhash';");
        //Set-up the whole session (with user details from database) etc...
    } else if(mysql_num_rows(mysql_query("SELECT user FROM tokens WHERE session = '$session' AND md5(user) = '$userhash';"))==1)){
        //TOKEN is different, session is valid
        //This user is probably under attack
        //Put up a warning, and let the user re-validate (login)
        //Remove the whole session (also the other sessions from this user?)
    } else {
        //Cookie expired in database? Unlikely...
        //Invalid in what way?
    }
} else {
    //No cookie, rest of the script
}

脚本的优点:

  • 多次登录。您可以为您所在的每台计算机创建新会话。
  • Cookie 和数据库将保持清洁。活跃用户每次登录都会更新 cookie。
  • 一开始的会话检查确保数据库不会收到无用的请求。
  • 如果攻击者窃取了 cookie,它会获得一个新的令牌,但不会获得一个新的会话。因此,当真正的用户使用旧的(无效的)令牌但使用有效的用户会话组合访问网站时,用户会收到潜在盗窃的警告。通过登录重新验证后,会创建一个新会话,并且攻击者持有的会话无效。重新验证确保受害者确实是受害者,而不是攻击者。

参考:http: //jaspan.com/improved_persistent_login_cookie_best_practice

于 2011-01-23T13:05:01.160 回答
6

这种“记住我”功能始终是一个额外的安全风险。

因为就像在会话中一样,您只有一个标识符,不仅足以识别用户(它是谁?),而且还可以验证该用户(真的是他/她吗?)而无需进行实际验证。

但与具有(或应该具有)只有很短的生命时间(通常少于一个小时)的会话相反,并且标识符是(或应该)定期更改(基于时间并根据真实性/权威状态更改的必要性) ,“记住我”标识符的有效期为数天,甚至数月或数年!而这种长有效期带来了额外的安全风险。

因此,在询问如何实现这样的“记住我”功能之前,您应该问自己是否真的想要额外的安全风险。这主要取决于您的应用程序拥有的资产和身份验证的目的,以及您是否想冒“记住我”功能所带来的假冒/身份盗窃的风险。

如果是这样,请确保使用 HTTPS 提供基本安全性,并在 cookie 中设置HTTPOnly标志和安全标志。然后您可以执行以下操作来构建这样的“记住我”功能:

  • 身份验证请求
    如果用户通过 HTTPS 进行身份验证并设置了“记住我”选项,则生成一个随机的记住我令牌,将其存储在服务器端的“记住我”数据库中,并设置带有安全标志的记住我cookie价值。然后开始一个新会话并设置一个记住我的标志。

  • 任何其他要求

    1. 如果当前没有会话,通过 HTTPS 重定向到记住我页面,检查是否有记住我的cookie。如果有一个记住我的令牌并且它是有效的,则使其无效,生成一个新令牌,将其存储在“记住我”数据库中,使用该新令牌设置一个 cookie,并使用设置记住我标志创建一个新会话。否则重定向到登录页面。
    2. 如果当前会话无效(确保使用严格的会话失效),如果设置了记住我标志,则通过 HTTPS重定向到记住我页面;否则重定向到登录页面。

有了这个,身份验证通过 HTTPS 得到保护,包括初始身份验证和“记住我”身份验证。并且用户仅在当前会话期间是真实的;如果过期,用户必须通过使用记住我令牌或提供他/她的登录凭据来重新进行身份验证。并且由于记住我令牌存储在数据库中,用户可以使任何现有的记住我令牌无效。

于 2011-01-23T13:35:12.380 回答
5

最受欢迎的方式:

  • 许多脚本使用某种会话跟踪。当用户第一次访问网站时,它会为用户生成一个唯一的随机 ID,并将会话信息存储在服务器中,并将 ID 存储在 cookie 中。然后,服务器使用唯一 ID(称为会话 ID)来识别用户。与会话 ID 关联的信息只有服务器才能看到。PHP默认使用这个,

  • 有些将用户数据存储在 cookie 本身中,但带有使用秘密字符串作为密钥的 HMAC 签名。如果签名不匹配,脚本会丢弃 cookie。这样,服务器就不必将会话数据保存在服务器上。用户通过查看 cookie 来查看会话中的内容,因此您不应在其中存储敏感数据。只需用户 ID(可能还有登录时间和 cookie 到期时间)就足够了。虽然用户可以看到会话信息中的内容,但 cookie 中的签名确保用户无法自己修改会话数据。

这些方式提供了一些安全性,即用户不能篡改会话数据,但不能保护用户免受窃听。他们总是可以使用数据包嗅探器并从任何开放的 WiFi 网络中窃取会话。一些应用程序确实依赖于用户 IP,但攻击者是否在同一个网络中并不重要。一些应用依赖User-Agent,但是当用户更新浏览器或从另一个浏览器导入数据时会出现问题。

如果您真的关心安全性,请使用 HTTPS

另请阅读这篇文章,尤其是名为“网站运营商如何解决问题? ”的部分。

于 2011-01-23T12:06:25.520 回答