V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
supersheep
V2EX  ›  PHP

如何避免cookie伪造?

  •  
  •   supersheep · 2011-06-04 13:55:12 +08:00 · 14086 次点击
    这是一个创建于 4681 天前的主题,其中的信息可能已经有所发展或是发生改变。
    我用的php,初学者的做法一般就是
    这样设置

    setcookie('username','xxx',time()+3600*24);
    setcookie('password','xxx',time()+3600*24);

    再这样读取

    $logged = false;
    $username = @$_COOKIE['username'];
    $password = @$_COOKIE['password'];
    if(isset($username)&&isset($password)){
    $logged=true;
    $user = getUser($username,$password);
    }

    这种做法的话用户名和密码就都是明文保存在本地的,不安全对吧。
    那我可以加密后再保存到cookie里,登陆的时候再在后台程序里解密,加解密的方法只有我自己知道。
    这样是可以避免用户名密码泄露,但是要通过cookie伪造登陆应该还是很方便。
    我想知道后者一般是如何避免的?谢谢指教。
    26 条回复    1970-01-01 08:00:00 +08:00
    manhere
        1
    manhere  
       2011-06-04 14:05:25 +08:00
    用session啊
    Hyperion
        2
    Hyperion  
       2011-06-04 14:08:09 +08:00
    session+1 cookie是绝对不可信的
    icyflash
        3
    icyflash  
       2011-06-04 14:10:09 +08:00
    cookies+session
    icyflash
        4
    icyflash  
       2011-06-04 14:11:59 +08:00
    不过伪造登陆的话,除非你把会话状态时间设置很短或关闭浏览器就清除,不然抓包后都可以伪造的
    reus
        5
    reus  
       2011-06-04 17:00:46 +08:00
    $key = rand(0, 65535);
    //然后把$key存起来,每个用户都不同的
    setcookie('username', $username, xxx);
    setcookie('sig', sha1($username . $key . $_SERVER['REMOTE_ADDR'] . $_SERVER['HTTP_USER_AGENT']), xxx);

    验证的话:
    $logged = false;
    $key = get_key($_COOKIE['username']);
    if (sha1($_COOKIE['username'] . $key . $_SERVER['REMOTE_ADDR'] . $_SERVER['HTTP_USER_AGENT']) == $_COOKIE['sig']) {
    $logged = true;
    }
    supersheep
        6
    supersheep  
    OP
       2011-06-04 17:10:40 +08:00
    @icyflash @Hyperion @manhere 下午一直在看cookie+session验证的内容。
    大致思想似乎是把sessionid存在cookie里,然后每次只通过session来确认状态,这个我能明白。
    但是关闭浏览器就失效了,这个时候cookie里的session就只是一个值而已了。是不是要把相关信息存在数据库里?具体要怎么做,我有点迷糊。

    @reus 的方法看明白了,这个$key也是存在数据库里吗?感觉随机数也有一定的重复概率啊,不过好像这里重复也没有关系。
    reus
        7
    reus  
       2011-06-04 17:22:43 +08:00
    @supersheep 就是动态密钥,其实你全部用同一个密钥也是ok的,风险会大些
    要伪造一个cookie那必然需要知道加密密钥,而通过sha1或者md5过的字符串是不能反推出密钥的,所以没法伪造
    把ip和user-agent加入加密过程里可以对抗cookie泄漏,不是那个ip那个浏览器,泄漏了也不能用
    挺安全的,除非你机器整个被拿下……
    reus
        8
    reus  
       2011-06-04 17:26:35 +08:00
    哦,还要把密码也加进去,这样密码修改以后,验证也会失效,用来防止密码泄漏
    sha1($username . $password . $key . $ip . $user_agent) 这样
    supersheep
        9
    supersheep  
    OP
       2011-06-04 17:28:52 +08:00
    @reus 嗯,你的方法我看明白了,就是要多开一个地方存这个key。另外还是想继续听听前几楼的见解。
    reus
        10
    reus  
       2011-06-04 17:30:09 +08:00
    @supersheep 觉得麻烦可以用同一个key啊,phpmyadmin就是只用一个key的,在配置文件里面可以看到
    supersheep
        11
    supersheep  
    OP
       2011-06-04 17:32:05 +08:00
    @reus 嗯,先用这种方式好啦,谢谢哈!
    Livid
        12
    Livid  
    MOD
       2011-06-04 17:34:31 +08:00
    嗯,赞成 8 楼的做法。

    在 cookie 里存 session id。

    在服务器上存 session data。session data 有 user id 及第一次验证成功时的 IP 和 user-agent。

    session data 在每次读取出来之后,进行验证,如果发现 IP 或 user-agent 有变,那么就撤销此 session。
    supersheep
        13
    supersheep  
    OP
       2011-06-04 17:41:17 +08:00
    @Livid 如何根据cookie里存的session去获得服务器上对应的session data呢?
    @reus 想到,验证需要password的话,password是存在哪里呢?
    reus
        14
    reus  
       2011-06-04 17:42:43 +08:00
    @Livid 如果不验证密码,那如果密码泄漏了,即使自己修改了密码,窃取方也还是可以成功验证的,所以需要保证密码修改之后所有session都失效
    Livid
        15
    Livid  
    MOD
       2011-06-04 17:43:15 +08:00
    @supersheep 服务器的 MySQL 数据库有一张 session 表,PK 是 session id,也就是在客户端 cookie 里存的那个 session id。

    另外,我觉得都 2011 年了,可以考虑不用学 PHP 了,建议你看看这个:

    http://www.v2ex.com/tornado/index.html
    manhere
        16
    manhere  
       2011-06-04 17:44:15 +08:00
    @supersheep
    验证需要password的话,password是存在哪里呢?

    password肯定在服务器端,验证并不是要返回密码给客户端,只是给它一个合法的身份。
    reus
        17
    reus  
       2011-06-04 17:44:46 +08:00
    @supersheep password就在数据表里面啊,就是再次生成一次加密串,看和cookie里面的是否一样,一样就是合法的否则非法
    supersheep
        18
    supersheep  
    OP
       2011-06-04 17:47:05 +08:00
    @Livid php也不算深入,只不过是自己目前实现想法最顺手的工具,还有就是部署方便。python有机会再研究吧,gae被墙多少会有点儿别扭。感谢推荐。
    reus
        19
    reus  
       2011-06-04 17:58:48 +08:00
    部署方便也是我还在用php的原因,因为SAE只支持php,做面向国内大众的站,这是成本最低的方式了,免费空间和流量
    虚拟主机也不难找,选择多
    python或者rails的话,没有虚拟主机,都必须用vps或者独立主机,这个成本太高了,国内的话。国外的vps网速是不够的
    php语言本身是很不好的,啰嗦,函数命名不规范,写起来不美观。解决方法是用这个 http://code.google.com/p/php-snow/ 或者 这个 http://fructoselang.org/
    或者干脆自己做个DSL,我选择了这个……
    Livid
        20
    Livid  
    MOD
       2011-06-04 17:59:58 +08:00
    V2EX,豆瓣,知乎,42区,下厨房……国内其实已经有不少网站是完全用 Python 做的了。
    reus
        21
    reus  
       2011-06-04 18:09:26 +08:00
    我也是用python来做php的代码生成器..
    supersheep
        22
    supersheep  
    OP
       2011-06-04 18:13:10 +08:00
    @reus 平时js写得比较多,一到php里发现不能把函数当参数传,函数返回数组不能直接用[]引用什么的就觉得很不爽……
    lijia18
        23
    lijia18  
       2011-06-04 18:27:46 +08:00
    伪造是挡不住的,唯一靠谱的方法是随机一个字符串。
    reus
        24
    reus  
       2011-06-04 18:48:14 +08:00
    @supersheep 是啊,语言那是相当的不现代…
    walleve
        25
    walleve  
       2011-06-04 18:53:56 +08:00
    不用就是最安全的。

    建议直接数据库存储cookie/session
    supersheep
        26
    supersheep  
    OP
       2011-06-04 19:32:08 +08:00
    @Livid 写着写着又有不明白了,ua和ip的信息应该已经隐含在cookie里了,为什么还要在数据库里存一份呢?
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   954 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 28ms · UTC 21:22 · PVG 05:22 · LAX 14:22 · JFK 17:22
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.