PHP 登录中 Session 和 Cookie 问题... - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
zeacev
V2EX    PHP

PHP 登录中 Session 和 Cookie 问题...

  •  2
     
  •   zeacev 2015-08-16 18:14:37 +08:00 5658 次点击
    这是一个创建于 3788 天前的主题,其中的信息可能已经有所发展或是发生改变。

    Web新手,最近想写一个完整的博客系统练练手,登录认证这一块有些地方思路不是很清晰,请教一下大家。

    首先,单独的Session和Cookie我思路很清晰,但是两者结合再加上数据库后,思路就不怎么清晰了。出于安全考虑,各位在开发的时候,在本地Cookie会放一些什么内容?本地的Cookie内容是怎么和服务器上的Session打交道?希望各位不吝赐教。

    然后,有没有必要在user表中增一个字段比如说叫做AuthCode,再浏览器POST过来的username和password去数据库对比成功之后,将这个AuthCode写到Cookie去,下次浏览器发送请求的时候直接对比username和AuthCode就行了?

    其次,我看了WordPress和Typecho的数据库表结构,里面没有类似于AuthCode的字段,但是登录之后却会有这么一个类似于AuthCode的Cookie值,比如说下图Typecho的:
    Typecho Cookie AythCode

    网上那些教程都只简单的说了下到本地设置个Cookie就行了,这样真的安全?

    处理登录的时候,各位是怎么在程序中实现的?能否说一下大致思路?

    感谢各位的解答

    39 条回复    2015-10-21 03:49:40 +08:00
    kslr
        1
    kslr  
       2015-08-16 18:18:17 +08:00   1
    没必要用Cookie,用Session就好了
    oott123
        2
    oott123  
       2015-08-16 18:26:19 +08:00   1
    设置了 cookie 之后签个名
    lyragosa
        3
    lyragosa  
       2015-08-16 18:31:25 +08:00   1
    不涉及多设备登录,交叉登录,登出一台设备自动登出所有设备等复杂的安全性措施的话。

    可以完全不使用session,代价是每个页面都要重新请求一次数据库读出用户信息,然后比对客户端的cookie

    cookie字串肯定要加密,简单的用几个hash加密加点盐再加点注册时间啊随机字符串就行。

    这样做的好处就是程序结构非常简单清晰易读,也不涉及session什么的。缺点就是开销稍微大了一丢丢,不过无所谓啦。
    zeacev
        4
    zeacev  
    OP
       2015-08-16 19:11:28 +08:00
    @lyragosa

    @kslr 你们两位结论正好相反,能否详细说说你的思路?
    imink
        5
    imink  
       2015-08-16 19:32:55 +08:00   1
    可以看下php文档里面的start_session() 方法(没记错的话),每次用户登录的时候session后台开启,保存用户的authCode(可以根据uid还有password加密),以确保如果要浏览需要验证的页面(比如修改个人主页,编辑日志)的时候拥有权限。至于cookie,由于存在本地,可以定时删除,一般的应用情景是,记录用户上次登录的状态(“勾选以便下次直接登录”),或者用户个人网站偏好。

    你图片上的cookie,有可能是为了“勾选以便下次登录”而设置的。
    我平常用的最多的还是操作session。
    raincious
        6
    raincious  
       2015-08-16 19:46:09 +08:00   1
    用 start_session() 启动会话之后,PHP 会自动帮你处理会话数据,包括向Client发Cookie(而不用你自己去发),然后将存在服务器上的用户数据填充给 $_SESSION。整个操作你只需要调用 start_session、剩下的 PHP 会帮你处理好,你只需要读写 $_SESSION,就是这么简单。

    而 PHPSESSION 那个 Cookie 只是 PHP 自己的 Session 机制设定的一个 Session ID 而已。
    incompatible
        7
    incompatible  
       2015-08-16 19:50:09 +08:00 via iPhone   1
    @lyragosa 每个请求都读数据库?
    没有一个web开发的内行会这么干
    rogeecn
        8
    rogeecn  
       2015-08-16 21:22:29 +08:00   1
    都没给撸说说SESSIONID是通过cookie带过去的么?就像你登陆了再清空COOKIE肯定要被退出管理权限呀。
    Keita1314
        9
    Keita1314  
       2015-08-16 21:46:36 +08:00   1
    第一次登陆之后,服务器的session保存当前会话信息,然后在数据库的一张表保存sessionID与用户名,同时将sessionID写入浏览器的cookie。
    再次登陆,服务端通过浏览器的cookie中取得sessionID,然后到数据库中根据sessioID判断出当前登陆用户。
    大概流程就是这样子。
    lyragosa
        10
    lyragosa  
       2015-08-16 21:52:55 +08:00   1
    @incompatible 说了啊 肯定有坏处的,会有额外的开销。
    但是如果是小型的产品的话,与其花时间折腾为了节省这点性能不如做与业务有关的事情。

    换句话说就是,如果产品只有100个人用,就不要折腾优化十万并发请求的事情。
    zeacev
        11
    zeacev  
    OP
       2015-08-16 22:00:03 +08:00
    @rogeecn 这个我知道
    incompatible
        12
    incompatible  
       2015-08-16 23:07:07 +08:00   1
    @lyragosa 6楼已给出php中集成session的成熟方案
    我比较熟悉的java servlet规范中也原生预置了类似的session方案
    我没看出来这些开箱可用的方案哪里“折腾”了,反而你的方案还要写额外的代码,这才是真的折腾
    ferock
        13
    ferock  
    PRO
       2015-08-17 00:04:21 +08:00 via Android   1
    都是一样的东西,不走ssl 都不安全
    msg7086
        14
    msg7086  
       2015-08-17 02:48:36 +08:00   1
    #7 @incompatible 每个请求去读数据库有什么问题?又不会影响什么速度。
    incompatible
        15
    incompatible  
       2015-08-17 04:10:20 +08:00   1
    @msg7086
    当然会影响速度 读取数据库要耗时间在应用服务器与数据库服务器间的网络IO、要耗数据库的CPU

    #3的最大问题不在于每个请求读数据库会影响速度,而是在于业内没人这么干。
    如我12楼所说,PHP和Java中都有成熟的方案,非常简单,根本不需要造一个毫无必要的轮子。
    xuhaoyangx
        16
    xuhaoyangx  
       2015-08-17 04:14:24 +08:00   1
    @incompatible 赞同
    kanezeng
        17
    kanezeng  
       2015-08-17 09:01:57 +08:00   1
    @incompatible 单服务器这样肯定没问题,不过这样的话session信息其实是保存在服务器内存本身吧。如果服务器扩展到两台甚至,有可能存在一种情况是用户的不同请求被分配到不同的服务器上,那么如果请求被分配到内存中没有session信息的那些服务器上,用户就又变成未登录状态了吧。
    所以如果脱离单机环境的话,这种问题还是值得讨论一下的吧?
    haiyang416
        18
    haiyang416  
       2015-08-17 10:25:57 +08:00   1
    博客系统的登录状态一般来说需要保存特定时间长度,不推荐直接使用 session 来储存,因为你对其运行机制不熟悉,会遇到很多意外情况。
    作为练手项目,我们可以直接使用 cookie 来保存登录状态。
    比如你可以将用户 id 存入 cookie ,在每次打开页面都检查 $_COOKIE["uid"] 是否设置来判断用户是否登录。
    然而只依靠一个 uid 来鉴权是非常危险的事情,因为 cookie 是可以伪造的,所以我们需要在 uid 以外另添加一个 cookie 来杜绝伪造。
    这个 cookie 通常是一个 hash 值,比如就将它取名为 AuthCode ,它的值是可以是 hash ("sha256", uid + salt ) 或者其他类似的字符串。
    那么在每次打开页面的时候就需要判断两种情况:是否设置了 $_COOKIE["uid"]; hash ("sha256", $_COOKIE["uid"] . "IAmSalt!") 是否和 $_COOKIE["authCode"] 相同;
    那么只要 hash 算法或者 salt 不泄漏,就可以杜绝大部分伪造,一下就感觉安全了很多是不是?
    当然,这只能应付“最最最”基本的情况,比如我的 cookie 被拦截了,这样的鉴权方式就算改密码都没用,怎么办?
    只能靠楼主自己去了解相关的解决方式了。
    incompatible
        19
    incompatible  
       2015-08-17 10:56:48 +08:00   2
    @kanezeng
    集群环境下通常采用这两种方案之一:
    - 在 load balancer 上做 sticky session ,把来自某个客户端的请求永远转发至相同的应用服务器
    - 把 session 信息放在 redis/memcached 等支持高并发的缓存中
    数据库虽然也可以做集中式存储,但是在高并发情况下性能会急剧下降,所以没人用它存 session
    kanezeng
        20
    kanezeng  
       2015-08-17 12:23:49 +08:00   1
    @incompatible 赞同,前面回复的时候我脑子里想的也是 redis , redis 的特性应该是最适合这种情况的吧。
    zeacev
        21
    zeacev  
    OP
       2015-08-17 13:40:31 +08:00
    @ferock 这里说的安全不是传输安全,
    abcfyk
        22
    abcfyk  
       2015-08-17 16:17:08 +08:00
    @haiyang416 我晕。。对 LZ 这种新手来说,这个明显比 session 难多了好吧。。

    对新手来说, session 可以简单认为就是安全的, cookie 是不安全的。存取 session 只需要非常简单一行代码就搞定。这还不够简单还要取 uid ,加盐, hash 。。。 LZ 会晕的。
    abcfyk
        23
    abcfyk  
       2015-08-17 16:29:35 +08:00   1
    我的一些见解。希望能帮到你。

    Q :出于安全考虑,各位在开发的时候,在本地 Cookie 会放一些什么内容?
    A : cookie 通常用于保存非敏感信息。比如 userId ,记住密码的开关,比如 authCode 也是这种

    Q :本地的 Cookie 内容是怎么和服务器上的 Session 打交道?
    A :注意看 phpsessid 那个东西,这个可以认为是服务器上随机生成的一个随机数,对用户 /浏览器 /浏览进程唯一,每次浏览器请求服务器的时候会说:“哎,你给我 xx 页面的东西。我的 sessionId 是 xx ”。然后服务器上跑的 PHP 可以通过 nginx/apache 拿到这个 sessionId ,也就知道你是谁了。你的身份就对应上了。

    Q :然后,有没有必要在 user 表中增加一个字段比如说叫做 AuthCode ,再浏览器 POST 过来的 username 和 password 去数据库对比成功之后,将这个 AuthCode 写到 Cookie 去,下次浏览器发送请求的时候直接对比 username 和 AuthCode 就行了?
    A :没有必要

    Q :其次,我看了 WordPress 和 Typecho 的数据库表结构,里面没有类似于 AuthCode 的字段,但是登录之后却会有这么一个类似于 AuthCode 的 Cookie 值。
    A :我一般会用 AuthCode 做记住密码的开关或者权限等级等一些信息。其他 PHPer 我不清楚。。

    Q :网上那些教程都只简单的说了下到本地设置个 Cookie 就行了,这样真的安全?
    A :不是很明白这个说法。本地设置 cookie 是设置什么?名称是什么?值是什么? Web 开发一个原则就是永远不要相信用户的输入,所有保存在客户端的信息都是非常不安全的。

    Q :处理登录的时候,各位是怎么在程序中实现的?能否说一下大致思路?
    A : 额。。太长了。有空再写。


    A :
    haiyang416
        24
    haiyang416  
       2015-08-17 17:11:36 +08:00   1
    @abcfyk session 保存登录状态,浏览器关闭就没了,每次打开浏览器都重新登录?设置 cookie 的保存时间会比设置 session 的生命周期更难? 什么都保存在 session 里,只靠 session id 裸奔,真的会比 cookie 安全?
    jugelizi
        25
    jugelizi  
       2015-08-17 21:42:22 +08:00   1
    @abcfyk 不要误导人
    session 是依赖 cookie 的 cookie 做的不好 session 没有任何安全可言
    网上加密方法很多, userid 和 IP 以及特定的 key 生成 cookie 服务端可以解开基本就认为安全的
    incompatible
        26
    incompatible  
       2015-08-18 01:27:41 +08:00   1
    @haiyang416
    @jugelizi

    cookie 与 session 跟 安全 是八竿子打不着的关系
    简单来说 cookie 是保存在浏览器中的客户信息 session 是保存在服务器端的客户信息

    cookie 与 session 唯一的联系就是 一些 Web 应用容器是通过在 cookie 中设置 sessionId 的方式来实现客户与 session 的绑定的(在浏览器不支持 cookie 的情况,比如 Java 的 servlet 容器会通过重写 url 、在所有 url 后加上 jsessionid 的方式来实现客户与 session 的绑定)

    至于‘登录后记住我’这种功能,其实就是一个鉴权的过程。首次登录时浏览器中的网页发送用户名和密码给服务器端,服务器完成鉴权后,发给客户一个凭证,此后的客户的访问,带着凭证来证明合法身份即可。
    这个凭证理论上来说你可以选择放在 cookie 中,也可以选择放在 session 中,两种方式的安全性是一致的。但实际上如果放在 cookie 中,服务器端依然要护凭证和 cookie 的对应关系及过期时间,还不如干脆放在 session 中来得省事儿。


    以上过程中有两个安全弱点。 一是鉴权过程中用户名和密码会被截获(使用 https 可破)。 二是完成鉴权后的访问中, cookie 被截获(使用 https 可破)或者被冒用(请不要把钥匙交给别人)。 显然这都不是 cookie 或 session 可以解决的问题。所以说‘ cookie 跟 session ’与‘安全‘没有关系。


    至于把 userId+IP+特定 key 加密再到服务器端解,这是非常不靠谱的。我若是破解了你的加密算法,岂不是我可以想登录哪个用户就登录哪个用户?
    haiyang416
        27
    haiyang416  
       2015-08-18 04:33:41 +08:00 via Android   1
    @incompatible 请只用 session 实现「记住我」一个月的功能,并且比用 cookie 更适合新人博客练手。
    另外,算法不是你想破解就破解,比如 uid 为 1 的 hash 为 dc0afe3bee4310343511b13f2cd8ac19c8ddfaf9572c13e02c44bddf8722b722 ,算法为 sha256 (uid + salt ),请“破解” uid 为 2 用户的 hash 。
    haiyang416
        28
    haiyang416  
       2015-08-18 05:04:48 +08:00 via Android   1
    对此帖的回复到此为止。
    楼主可以鉴别和选择对自己有用的信息,能解惑最好。
    haiyang416
        29
    haiyang416  
       2015-08-18 05:54:29 +08:00 via Android   1
    忘记贴一个网址了: https://paragonie.com/blog/2015/04/secure-authentication-php-with-long-term-persistence#title.2
    内容包含登录相关所有基本操作,建议一读。
    jugelizi
        30
    jugelizi  
       2015-08-18 08:44:43 +08:00   1
    想了下还是回复下 在 php 的 session 中默认使用的文件管理,而且过期删除机制的随机性,我不认为在大中型项目中使用 session 来做用户凭证,要么 cookie 走加密,参见 discuz ,或者用 redis 等实现 session 存储,默认文件读写的导致的锁是会有问题的。
    incompatible
        31
    incompatible  
       2015-08-18 10:53:42 +08:00   1
    @haiyang416 为什么要” 请只用 session 实现「记住我」一个月的功能“?就因为你贴那个英文文章里建议这么做(并且没有解释原因)?

    关于算法破解,你赢了,我个人破解不了 uid 为 2 的 hash
    但是破解这个难道不是时间问题?
    haiyang416
        32
    haiyang416  
       2015-08-18 11:08:12 +08:00   1
    @incompatible 最后回复一次,我给你一个理由,请试想这样一个情形:
    楼主写好了自己的博客系统,登录状态保存在 session 里,楼主听着歌儿,打开博客编辑器,愉快的写着文章。
    一篇很好的文章花了楼主 45 分钟时间,完成后点击了发布按钮,然后,就没有然后了。
    由于楼主写文章过于专注, 45 分钟内没有刷新或者访问自己的博客,等到点击了发布按钮后才发现自己被跳转到了登录界面,由于楼主没有事先保存文章,所以写好的文章就这么丢失了,楼主怒了,我明明登录了,为什么为什么为什么现在又要我登录?
    devion
        33
    devion  
       2015-08-18 11:22:52 +08:00   1
    原来是这样的!
    incompatible
        34
    incompatible  
       2015-08-18 13:11:14 +08:00   1
    @haiyang416 难道“只用 session 实现「记住我」一个月的功能”就能杜绝这个问题了?

    另外,为什么 45 分钟楼主没有刷新 /访问自己的博客 session 就没了? 以及 #24 “ session 保存登录状态,浏览器关闭就没了,每次打开浏览器都重新登录”这又是为什么? 你确定你足够懂 session 的运行机制?
    haiyang416
        35
    haiyang416  
       2015-08-18 13:20:03 +08:00   1
    @incompatible 你真的会 PHP ?
    elvba
        36
    elvba  
       2015-08-18 23:04:55 +08:00
    @haiyang416 很明显他是会的,我也想知道为什么 45 分钟没有刷新 /访问自己的博客 session 就没了,这个 45 分钟时怎么来的?另外 “ session 保存登录状态,浏览器关闭就没了”,你自己登录 V2EX 关闭浏览器后就要重新登录了么?
    haiyang416
        37
    haiyang416  
       2015-08-19 08:29:36 +08:00 via Android
    @elvba 这个帖子里讨论的是新手练习写博客系统,所以我在第一次回复中就说了, session 的运行机制要比 cookie 复杂,不建议新手深究。至于 45 分钟,是随口说的,在 PHP 中原生 session 生命周期只有大约 24 分钟,并不是只设置携带 session id 的 cookie 的过期时间可以解决的,在服务器中同样可能被 GC 。所以就避免不了要设置服务器 session 的生命周期,就会牵扯到 php. ini 中 session 相关的设置,比如生命周期,最大生命周期,回收概率计算,保存位置等等。并且这个设置是全局性的,所以会影响到整个博客系统甚至同服务器其他 PHP 程序。当然这些设置可以在 PHP 脚本中设置,但是需要保证每次启用 session 时都必须设置,不然仍然可能被 GC 。当然,有些人会先将楼主设定到 PHP 程序员的水平,认为这些设置都是非常简单的,或者可以放弃原生 session 使用其他储存方式,并且部署 SSL 来保证 session id 不被截取。
    反倒是前面回复中提到的每次打开网页都查询数据库和使用 cookie 的方式被人不耻,其实楼主提到的 typecho 就是这么做的, WordPress 中是又怎么做的 ?前面某位,他的作品有几万人玩,我也玩了一年多了,但是也被说成外行,没办法。
    gdtv
        38
    gdtv  
       2015-10-20 20:24:25 +08:00
    @haiyang416
    @incompatible

    抱歉我挖坟了,因为我最近在做用户登录模块。
    在 PHP 里,只用 session 应该是实现不了“记住密码”功能的,@haiyang416 说得没错,如果要实现这个功能只能借助 cookie ,那又涉及 cookie 的加密了,那就回到了 @haiyang416 的观点“要同时处理 session 和 cookie ”,那我何不一开始就只处理 cookie 呢?
    我比较赞同 @haiyang416

    还是希望 @incompatible 能说一下只用 session 实现记住密码的功能,如果能实现,那比用 cookie 简单方便多了。

    PS: phpmyadmin 登录后 24 分钟不操作就会被自动注销登录,就是因为 session 有生存期,并不是只有关闭浏览器才失效。
    incompatible
        39
    incompatible  
       2015-10-21 03:49:40 +08:00 via iPhone
    @gdtv 抱歉此中我的回帖在 36 楼就终结了 因为我真的不会 php 所有关于 cookie 和 session 的机制我都是从 http 权威指南以及 java servlet 规范中习得的

    如果 php 真的有 24 分钟后 session 就被 gc 这样的诡异机制,那它绝对妄称 V2EX 上“最好的编程语言”

    但是请相信我,没有比使用 session 实现“登录后记住我”更正统的方式。你需要的就是在服务器端维持这个 session 足够长的有效性,以及在浏览器端维护一个与该 session 对应的 cookie 。

    在数十万并发的 java web 系统的中,我通过把 session 放在 redis 中的方式来实现应用服务器的 scale out 以及 session 的有效性。在 php 中,请按照 37 楼的提示对 php 进行配置。总之一个只有 24 分钟生命的 session 绝对是不正常的。
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     4553 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 43ms UTC 05:37 PVG 13:37 LAX 21:37 JFK 00:37
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86