yuzhigang5460 发表于 2015-4-26 20:33:58

使用CodeIgniter 3 Session类库

本帖最后由 yuzhigang5460 于 2015-4-26 20:37 编辑

相信无数人在使用CI2的Session类库时,遇到各种的坑,各种抱怨,各种不解。在CI中国论坛能搜到大量关于Session类库的提问,说明要想用好session类库还是得下一番功夫。本文将先从CI2入手,讲述CI下Session类库的设计理念和在CI3下的Session类库做了哪些重大改进。首先在这里简单说明一下Session和cookie的区别。

Session和cookie的区别

1.在某些语境中,cookie是session的一种实现方式,Ci的类库设计似乎就这么认为的。设想用户登录后使用Session保存数据的一种情景。用户输入密码和口令发送到服务器后,php就会在服务器端生成一个php sessionid和一个具体的数据包放到一个文件中,并存放到指定的文件夹下(如果session.save_handler=files),然后会把sessionId作为一个cookie放到响应中返回给客户端(前提是用户浏览器开启了cookie,否则会加入到url地址的查询字符串中)。用户再次访问服务器上的其他页面时,会把这个phpsessionid的cookie携带上作为请求的一部分发送给服务器,服务器就能根据session id到保存session的文件夹下找到特定的文件,读取其中的数据。

2.而cookie,在服务器端和客户端都是可以设置的。在客户端设置(通过js)时,主要作用是用户在自己的浏览器页面跳转时能传递数据。服务端不会太多理睬客户端设置的cookie,客户端发过来什么,它就原封不动地返回什么。同时,服务器也会发出让客户端设置cookie的指令,下次请求时客户端也会无脑地把cookie发送到服务器。

在CI2.0版本的Session类库中,默认使用了cookie来保存session,便带给我们各种不解。下面谈下CI2.0的session工作原理。

基本原理

使用Session库时,会有一个元数据。我们可以把这个元数据理解为在浏览器和服务器之间的一个令牌,有了这个令牌,服务器的城门才会对客户端的请求敞开。客户端好比是一个信使,要去服务器的王宫里报告信息,服务器的门卫就会检查这个令牌,这个令牌包含的基本信息是:ip地址、user agent、上次活动时间、Session id,其中Session id是重要的,Session id不对,直接拒绝。但是门卫也可能根据上级指示,需要根据他的外表判断是否是来自特定地区(IP地址的限制)、看下他的服饰是否符合某些特征(user agent限制),是否令牌是否已经过期(上次活动时间),如果都满足才能让特定的西域人进入王宫。

Array
(
    => 4a5a5dca22728fb0a84364eeb405b601
    => 127.0.0.1
    => Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_7;
    => 1303142623
)

门卫得到的上级指示来自于配置文件,它们放在config.php中:
$config['sess_match_ip']= FALSE;
$config['sess_match_useragent'] = TRUE;
$config['sess_time_to_update'] = 300;


门卫会拿令牌里的信息(http请求携带的cookie)和真实的信息(http请求头的信息)比对,判断你是否是合法的,不合法的话,就会认为你来路不明,拒绝你携带来的任何数据信息。如果你在使用CI自带的Session库时,总是出现读不到数据的情况,你就要看下你是否要求门卫检查的过于严格。特别是当设置sess_match_useragent设为TRUE时,会遇到各种的坑:
1. 你用flash组件上传文件,只有登录的用户才能上传文件,结果你每次判断用户是否登录都会出错,因为flash发送的http请求有可能更改了user agent;
2. 使用ie切换不同的模式,比如兼容模式,也会造成user agent不同;
3. user agent的长度最长是120个字符,手动设置User agent是需要截取字符到最大120个。
4. 另外,如果出现“Cannot modify header information - headers already sent by”错误,基本可以断定是你文件的编码格式有误,请去掉bom头。
5.一个会话并发冲突的问题,比较复杂。请在第三节中关注。

安全问题

CI使用cookie来传递数据本来就不够安全,而且如果数据量巨大时也会有性能问题。但是CI还是友好地加入以下几个安全验证机制:
1. 加密令牌
$config['encryption_key'] = 'mahuaz_';
$config['sess_encrypt_cookie'] =TRUE;
设置后,客户端检查cookie时就可以看到加密后的序列化数据。(即使sess_encrypt_cookie设为false也需要设置encryption_key不为空,这是个小bug)

2. 自动更新机制
CI默认每五分钟更新一次令牌,更新发生在客户端的一次请求中。客户端每发送一次请求,会把cookie的信息发送到服务器,服务器根据发来的cookie判断是否到了应该更换令牌的时间了,如果是,就会重新换一个新的令牌返回给客户端。这就相当于门卫给你换了令牌,下次要使用新令牌进门。此时即使有坏人伪造了一个令牌也不起作用了,因为旧令牌已经作废。这样就相当于加了一条安全机制。可以使用:
$config['sess_time_to_update'] = 300;
来设置多长时间来换一次令牌。这个时间不要设置的太短,更新频繁也会影响性能。这里不要和sess_expiration混淆:
$config['sess_expiration']= 7200;
这个配置是用来指示整个cookie的过期时间的,相当于令牌完全失效,再怎么更换都不起作用。
在CI中文论坛上,每每有遇到各种session问题,总有人推荐使用hex封装的Session库 ,或者使用stblog的作者Saturn的Session库https://github.com/cnsaturn/codeigniter-my-session前者使用PHP原生Session,不需要额外配置;后者需要安装 memcached 服务以及 memcache/memcached 扩展。hex封装的这个Session库特性有限,它有如下特点:
1. 简化了CI自带Session的安全机制,没有做每五分钟更新session_id的设置;
2.没有加密Session(似乎也没必要);
3.永远不会过期,虽然它会读取$config['sess_expiration'] = 7200;判断Session是否过期,但是如果发现过期的话,并不是直接返回空数据,而是重新产生一个新的Session id,把旧数据转到新数据中。除非你超过了最大gc回收时间:7200s。
4.config.php中的以下配置节都没有用到:
$config['sess_cookie_name']= 'ci_session';
//$config['sess_expiration']= 7200; 该配置节有用
$config['sess_expire_on_close'] = FALSE;
$config['sess_encrypt_cookie'] = FALSE;
$config['sess_use_database'] = FALSE;
$config['sess_table_name']= 'ci_sessions';
$config['sess_match_ip']= FALSE;
$config['sess_match_useragent'] = TRUE;
$config['sess_time_to_update'] = 300;


所以,使用这个库也只能解决大部分问题,真正的解决方案也只能等到CI3来处理,事实上CI3也如愿地解决了上面提到的很多问题。

————————————————————
复制代码太麻烦了,先讲了个开头,还没到正题。还有几篇相关文章代码太多了。欢迎高手批评指正,提出问题,共同探讨,共同进步。
优雅地使用CodeIgniter 3之Session类库(1)
优雅地使用CodeIgniter 3之Session类库(2)
优雅地使用CodeIgniter 3之Session类库(3)
使用CodeIgniter的Session类库问题集锦(坑)



yuzhigang5460 发表于 2015-4-27 22:07:19

v阿杰 发表于 2015-4-27 21:31
看过你的文章,怎么感觉官方只是在存储的方式上更新了,而在数据设置上却换成原生的了? ...

CI3官方建议多使用原生的方法,这是它的一个指导意见。
事实上,CI3 的changlog上有大量使用原生方法的建议,可能认为封装的过度。

CI3的Session类兼容了CI2的方法,所以CI2的方法对CI3依然有效。CI3的Session类最大的好处是加入了几种不同的数据类型:flashdata, tempdata, userdata,方便你的调用。其他看来,Session类本身也没有太多需要封装的地方,save_hanlder原生php都支持,现在CI3让你切换的更方便了而已。

yuzhigang5460 发表于 2016-10-26 15:31:26

本帖最后由 yuzhigang5460 于 2016-10-26 16:54 编辑

gogogo1027 发表于 2016-10-25 18:29
CI的Session类的安全问题我觉得作者多虑了,首先是可以选择混淆加密cookie,其次可以换一种方式,CI是提供了可 ...
这篇文章就是在讲怎么加密cookie的。作为一个框架,总得有多种Session的存储方案。

除此之外,
你听过CSRF吗?

我不知道偷窃来的cookie到底是什么,但是我知道能通过它得到什么。

gogogo1027 发表于 2015-8-18 20:42:36

CI2的时候Session会因为ajax而改变信息,瞄了下CI3取消了ajax请求时刷新session的操作。我这边的后台登录信息是存放在CI的session,使用session数据库存储,安全还是挺安全的问题在于2.0的时候经常因为ajax请求数据时刷新掉了session_id导致频繁重新登录,我单单把那个session类升级到3.0完美解决这个问题。

Hex 发表于 2015-4-27 00:24:34

好文章,顶~~

yuzhigang5460 发表于 2015-4-27 09:21:20

Hex 发表于 2015-4-27 00:24
好文章,顶~~

感谢帮主支持~{:soso_e157:}

v阿杰 发表于 2015-4-27 21:31:14

看过你的文章,怎么感觉官方只是在存储的方式上更新了,而在数据设置上却换成原生的了?

适可而止 发表于 2015-6-16 22:54:11

赞个。。。

bigboy2050 发表于 2015-7-20 13:58:27

太好了!!!

wblyqq 发表于 2015-7-27 10:13:21

good 非常受益

zairoo 发表于 2015-7-28 10:12:25

那现在ci 3.0要用回以前的2.2的那种cookie存储方式要怎么设置呢
页: [1] 2
查看完整版本: 使用CodeIgniter 3 Session类库