PHP永久登录、记住我功能实现方法和安全做法


Posted in PHP onApril 27, 2015

永久登录指的是在浏览器会话间进行持续验证的机制。换句话说,今天已登录的用户明天依然是处于登录状态,即使在多次访问之间的用户会话过期的情况下也是这样。永久登录的存在降低了你的验证机制的安全性,但它增加了可用性。不是在用户每次访问时麻烦用户进行身份验证,而是提供了记住登录的选择。

据我观察,最常见的有缺陷的永久登录方案是将用户名和密码保存在一个cookie中。这样做的诱惑是可以理解的——不需要提示用户输入用户名和密码,你只要简单地从cookie中读取它们即可。验证过程的其它部分与正常登录完全相同,因此该方案是一个简单的方案。

不过如果你确实是把用户名和密码存在cookie中的话,请立刻关闭该功能,同时阅读本节的余下内容以找到实现更安全的方案的一些思路。你将来还需要要求所有使用该cookie的用户修改密码,因为他们的验证信息已经暴露了。

永久登录需要一个永久登录cookie,通常叫做验证cookie,这是由于cookie是被用来在多个会话间提供稳定数据的唯一标准机制。如果该cookie提供永久访问,它就会造成对你的应用的安全的严重风险,所以你需要确定你保存在cookie中的数据只能在有限的时间段内用于身份验证。

第一步是设计一个方法来减轻被捕获的永久登录cookie造成的风险。尽管cookie被捕获是你需要避免的,但有一个深度防范流程是最好的,特别是因为这种机制即使是在一切运行正常的情况下,也会降低验证表单的安全性。这样,该cookie就不能基于任何提供永久登录的信息来产生,如用户密码。

为避免使用用户的密码,可以建立一个只供一次验证有效的标识:

<?php

$token = md5(uniqid(rand(), TRUE));

?>

你可以把它保存在用户的会话中以把它与特定的用户相关联,但这并不能帮助你在多个会话间保持登录,这是一个大前提。因此,你必须使用一个不同的方法把这个标识与特定的用户关联起来。

由于用户名与密码相比要不敏感一些,你可以把它存在cookie中,这可以帮助验证程序确认提供的是哪个用户的标识。可是,一个更好的方法是使用一个不易猜测与发现的第二身份标识。考虑在保存用户名和密码的数据表中加入三个字段:第二身份标识(identifier),永久登录标识(token),以及一个永久登录超时时间(timeout)。

mysql> DESCRIBE users;

+------------+------------------+------+-----+---------+-------+

| Field      | Type             | Null | Key | Default | Extra |

+------------+------------------+------+-----+---------+-------+

| username   | varchar(25)      |      | PRI |         |       |

| password   | varchar(32)      | YES  |     | NULL    |       |

| identifier | varchar(32)      | YES  | MUL | NULL    |       |

| token      | varchar(32)      | YES  |     | NULL    |       |

| timeout    | int(10) unsigned | YES  |     | NULL    |       |

+------------+------------------+------+-----+---------+-------+

通过产生并保存一个第二身份标识与永久登录标识,你就可以建立一个不包含任何用户验证信息的cookie。
<?php
$salt = 'SHIFLETT';
$identifier = md5($salt . md5($username . $salt));

$token = md5(uniqid(rand(), TRUE));

$timeout = time() + 60 * 60 * 24 * 7;
setcookie('auth', "$identifier:$token", $timeout);
?>

当一个用户使用了一个永久登录cookie的情况下,你可以通过是否符合几个标准来检查:
<?php
/* mysql_connect() */

/* mysql_select_db() */
$clean = array();

$mysql = array();
$now = time();

$salt = 'SHIFLETT';
list($identifier, $token) = explode(':', $_COOKIE['auth']);
if (ctype_alnum($identifier) && ctype_alnum($token))

{

  $clean['identifier'] = $identifier;

  $clean['token'] = $token;

}

else

{

  /* ... */

}
$mysql['identifier'] = mysql_real_escape_string($clean['identifier']);
$sql = "SELECT username, token, timeout

        FROM   users

        WHERE  identifier = '{$mysql['identifier']}'";
if ($result = mysql_query($sql))

{

  if (mysql_num_rows($result))

  {

    $record = mysql_fetch_assoc($result);
    if ($clean['token'] != $record['token'])

    {

      /* Failed Login (wrong token) */

    }

    elseif ($now > $record['timeout'])

    {

      /* Failed Login (timeout) */

    }

    elseif ($clean['identifier'] !=

            md5($salt . md5($record['username'] . $salt)))

    {

      /* Failed Login (invalid identifier) */

    }

    else

    {

      /* Successful Login */

    }
  }

  else

  {

    /* Failed Login (invalid identifier) */

  }

}

else

{

  /* Error */

}
?>

你应该坚持从三个方面来限制永久登录cookie的使用。

1.Cookie需在一周内(或更少)过期
2.Cookie最好只能用于一次验证(在一次成功验证后即删除或重新生成)
3.在服务器端限定cookie在一周(或更少)时间内过期

如果你想要用户无限制的被记住,那只要是该用户的访问你的应用的频度比过期时间更大的话,简单地在每次验证后重新生成标识并设定一个新的cookie即可。

另一个有用的原则是在用户执行敏感操作前需要用户提供密码。你只能让永久登录用户访问你的应用中不是特别敏感的功能。在执行一些敏感操作前让用户手工进行验证是不可替代的步骤。

最后,你需要确认登出系统的用户是确实登出了,这包括删除永久登录cookie:

<?php

setcookie('auth', 'DELETED!', time());

?>

上例中,cookie被无用的值填充并设为立即过期。这样,即使是由于一个用户的时钟不准而导致cookie保持有效的话,也能保证他有效地退出。
PHP 相关文章推荐
15种PHP Encoder的比较
Mar 06 PHP
PHP+XML 制作简单的留言本 图文教程
Nov 02 PHP
PHP开发中四种查询返回结果分析
Jan 02 PHP
解析php下载远程图片函数 可伪造来路
Jun 25 PHP
Win7 64位系统下PHP连接Oracle数据库
Aug 20 PHP
PHP实现货币换算的方法
Nov 29 PHP
PHP速成大法
Jan 30 PHP
PHP批量查询WordPress留言者E-mail地址实现方法
Feb 15 PHP
PHP实现一维数组转二维数组的方法
Feb 25 PHP
php提取身份证号码中的生日日期以及验证是否为成年人的函数
Sep 29 PHP
PHP中快速生成随机密码的几种方式
Apr 17 PHP
ZendFramework2连接数据库操作实例
Apr 18 PHP
php curl 获取https请求的2种方法
Apr 27 #PHP
PHP curl伪造IP地址和header信息代码实例
Apr 27 #PHP
JavaScript实现滚动栏效果的方法
Apr 27 #PHP
php curl 上传文件代码实例
Apr 27 #PHP
php把大写命名转换成下划线分割命名
Apr 27 #PHP
PHP加密解密字符串汇总
Apr 26 #PHP
php开发中的页面跳转方法总结
Apr 26 #PHP
You might like
PHP使用Mysql事务实例解析
2014/09/08 PHP
PHP 读取和编写 XML
2014/11/19 PHP
php使用Jpgraph绘制简单X-Y坐标图的方法
2015/06/10 PHP
PHP模拟QQ登录的方法
2015/07/29 PHP
PHP检测用户是否关闭浏览器的方法
2016/02/14 PHP
ubutu 16.04环境下,PHP与mysql数据库,网页登录验证实例讲解
2017/07/20 PHP
php实现的支付宝网页支付功能示例【基于TP5框架】
2019/09/16 PHP
Javascript学习笔记7 原型链的原理
2010/01/11 Javascript
JavaScript Event学习第十一章 按键的检测
2010/02/10 Javascript
js word表格动态添加代码
2010/06/07 Javascript
jQuery+.net实现浏览更多内容(改编php版本)
2013/03/28 Javascript
jQuery学习之prop和attr的区别示例介绍
2013/11/15 Javascript
jquery.cookie.js用法实例详解
2015/12/25 Javascript
JS判断两个对象内容是否相等的方法示例
2017/04/10 Javascript
JS实现下拉菜单列表与登录注册弹窗效果
2017/08/10 Javascript
Vue 去除路径中的#号
2018/04/19 Javascript
微信小程序之自定义组件的实现代码(附源码)
2018/08/02 Javascript
JS实现倒序输出的几种常用方法示例
2019/04/13 Javascript
微信小程序实现录音功能
2019/11/22 Javascript
基于p5.js 2D图像接口的扩展(交互实现)
2020/11/30 Javascript
[43:57]LGD vs Mineski 2018国际邀请赛小组赛BO2 第二场 8.19
2018/08/21 DOTA
python对指定目录下文件进行批量重命名的方法
2015/04/18 Python
Python中index()和seek()的用法(详解)
2017/04/27 Python
python爬虫 Pyppeteer使用方法解析
2019/09/28 Python
python matplotlib如何给图中的点加标签
2019/11/14 Python
python pandas移动窗口函数rolling的用法
2020/02/29 Python
浅谈Python协程
2020/06/17 Python
CSS3制作彩色进度条样式的代码示例分享
2016/06/23 HTML / CSS
html5实现多图片预览上传及点击可拖拽控件
2018/03/15 HTML / CSS
应届生法律顾问求职信
2013/11/19 职场文书
建筑公司文秘岗位职责
2013/11/29 职场文书
给民警的表扬信
2014/01/08 职场文书
个人自我剖析材料
2014/02/07 职场文书
寻衅滋事罪辩护词
2015/05/21 职场文书
2016五四青年节活动总结范文
2016/04/06 职场文书
MongoDB日志切割的三种方式总结
2021/09/15 MongoDB