关于cookie的安全性问题

最近在学习cookie和session的基础知识,在这过程中涉及到cookie的安全性问题时总是很疑惑,很多文章都会涉及到安全性的问题,但是总是一句话带过,并没有详细说到底是怎么回事,比如我看到别人的观点:

服务器鉴别session需要至少从客户端传来一个session_id,session_id通常存于cookie中,或是url(很少用url,主要涉及安全性和SEO的影响)

所以在工程上session离了cookie基本没法用,但是cookie可以单独使用,不过cookies是明文存储,安全性很低,只使用cookie的话盗取了cookie基本就获取了用户所有权限。

亦或者

某个用户竟然在凭证上伪造了内容,让服务器哥误认为他是某个用户(这个时候凭证是明文,该起来非常方便),然后被伪造的那个用户的信息泄漏了

当我看到这些的时候总是不太清楚到底是怎么回事,以上面的例子为例,假如有人可以伪造cookie的内容那就算使用session,也可以有人伪造session_id啊,所以很疑惑。。

另外,大家可以具体讲下用户填写账号密码并提交表单后,服务器验证的具体过程吗?(是根据session_id从内存或者数据库中取出账号密码进行对比?)

感激不尽~

阅读 21.2k
9 个回答

用于识别身份的cookie应该设置为HttpOnly,也就是禁止通过JS操作这个cookie,这样可以避免网站因为XSS漏洞而导致用户的cookie被XSS收集.

另一个方面,如果要防止cookie被中间人劫持,还得上HTTPS加密通信,这就得购买CA证书(受制于人)和配置Web服务器了,cookie里也有这个配置,就是secure,比如:

setcookie('sessid', 'uniqid', time()+3600, '/', '', true, true);
//上面这句PHP设置cookie的代码将生成类似下面的响应头
//Set-Cookie: sessid=uniqid; expires=Wed, 07-Sep-2016 12:08:22 GMT; path=/; secure; httponly

楼主还有一个疑问就是cookie怎么避免被伪造?
要搞清楚这个问题,就要了解cookie的认证过程:

//保护用户密码的盐(用户注册或修改密码时生成)
$salt = sha1(uniqid($user_id.'_'.getmypid().'_'.mt_rand().'_', true));

//数据库保存的用户密码($pwd_user是用户输入的密码明文)
$pwd_db = sha1($salt.sha1($pwd_user));

//cookie里的盐
//其中$global_salt是配置里config.php定义的全局盐,用来保护用户的盐,一旦修改,所有用户的cookie都将失效.
$cookie_salt = sha1($global_salt.sha1($salt));

//最终生成的cookie内容
$cookie = base64_encode($user_id.'|'.$cookie_salt);
//如果你需要高安全性,还可以使用AES对整个cookie的内容做一次加密.
//https://segmentfault.com/q/1010000006884052/a-1020000007074076
//$cookie = openssl_aes_encrypt($cookie, $key);
这里把过期时间设为604800秒(60*60*24*7,一周)
setcookie('sessid', $cookie, time()+604800, '/', '', false, true);

//解密cookie
//$cookie = openssl_aes_decrypt($_COOKIE['sessid'], $key);

//解码分割后拿到里面的$user_id和$cookie_salt
//根据$user_id查询$salt拼出$cookie_salt,然后跟cookie里的$cookie_salt做对比,一致则通过cookie认证.
$cookie = explode('|', base64_decode($_COOKIE['sessid']));
list($user_id, $cookie_salt) = $cookie;

可见因为有随机且唯一并经过哈希的盐(以及AES加密),所以cookie是很难伪造的.
根据cookie里的ID查询数据库用户表对应的盐,并与cookie中的盐比对,一致则通过用户身份认证.

大家回答的都差不多了,我来说说个人对cookie安全加密的一些看法。

cookie一般情况下用于记录用户登录状态的,比如userid,千万不要记录密码,由于cookie是存储在客户端的,所以cookie很容易被人劫持修改。比如登录成功后在客户端写入cookie('userid') = 1,在服务器读取cookie userid的值,如果userid在数据库用户表中可以找到则证明当前用户userid为1且合法登录,显然这样是不可行的。如果用户自行修改userid为2或者其它用户的userid,那么服务器就认为当前合法登录的用户userid为2,这样用户就不需要知道userid为2的密码就合法登录了,修改cookie可以通过直接修改浏览器cookie文件或者通过JavaScript修改cookie值达到欺骗服务器的目的,所以需要对存储在客户端的cookie要进行加密。

提到加密可能大家首先想到MD5,通过MD5算法对userid进行加密然后存储在客户端cookie中,由于MD5是不逆的,所以服务器在收到cookie(‘userid’)的时候也不可以解密,那么服务器应该在用户数据表中添加一个字段专门存储MD5加密后的userid,然后通过SQL查询到userid。虽然MD5是不可逆的,但是MD5还是可以暴力破解的,尤其是对于userid这种纯数字的,比如userid为12345,MD5加密后的值为827CCB0EEA8A706C4C34A16891F84E7B,我们把加密后的值放到http://www.cmd5.com/中解密看看,不到2秒结果12345就出来了。如果用户知道了你的userid只是经过了一次MD5加密,那么用户随便对一个已经存在的userid进行MD5加密,然后伪造cookie(‘userid’),此时服务器会认为用户为合法用户了,获取了其它用户的权限。那么我的建议是对userid进行两次MD5加密或者加一个复杂的key,因为越复杂MD5暴力破解就越慢,如果暴力破解需要2万年那就没有任何意义了。

上面提到的使用已知加密算法(如MD5)还是有漏洞的,最好的是自己编写一套加密算法,这样用户就无法伪造其它用户了。当然了不是每个人都有编写加密算法的能力,我们还是可以用DES加密算的,DES加密需要密钥,而且还可以解密,当用户登录成功后使用预先设置好的密钥对userid进行DES加密,然后存储到客户端cookie中。服务器读取cookie(‘userid’)时在服务器端使用相同的密钥进行DES解密,然后取得userid,由于用户不知道密钥(密钥存储在服务器上),所以用户也无法伪造成其它用户了。我的项目用的是Thinkphp框架,采用的Thinkphp自带的加密算法。此外为了防止用户通过javascript修改伪造cookie,可以设置cookie的httponly属性为true,这样cookie就只能通过服务器读取不能通过javascript读取了,也防止了恶意用户通过XSS跨站脚本攻击获取其它用户的cookie信息,一定程度上提示了安全性。

别以为这样就万事大吉了,既然不能伪造cookie了,但还是可以通过抓包获取其它用户的cookie的,毕竟cookie是在网络中传输的,那怎么办呢?可以用https协议代替普通http协议,这样恶意用户抓到的报文已经是密文了,也就不知道cookie了。

点击登录后,浏览器会自动把你的cookie放在请求头(request headers)里,一起发送给服务器

clipboard.png
然后服务器就会验证你的cookie有没有过期,不合法之类的。。。

在强调cookie,session区别时。一般意味着cookie只包含原始用法,在头文件里设置键值对。方便服务器追踪。返回响应时同时返回cookie,前端页面也能‘回想起’用户在上一次加载页面时干了什么。相当于给无状态的http协议,打了个状态补丁。
比如说上次退出在用户设置界面,这次进入www.foo.com直接转到用户设置。或者是保留偏好设置,皮肤选项什么的。
使用cookie来登陆,指的是服务器验证后,直接在cookie中写入username:laowang;这样简单的记录方式。cookie能对上号就认定这个用户代理就是某个用户在使用。坏人只需要窃取这几条cookie,就可以在任何地方模拟是laowang的账号了。即便给这几条cookie加密,但是不会改变cookie和账号一一对应的关系,偷cookie,改头信息===获得访问权限。

http协议对各种代理设备,中转路由又完全是明文的。非常容易中途被复制一份cookie。

现在常用session技术可以看作对cookie的一个封装,或者说应用。具体实现可能不同。大概就是客户端post账号密码后,验证通过,服务器写一条数据在服务器本地标记xxx正在线上。两边互相确认身份本质上还是通过cookie。好处是服务器可以在一段时间无请求时,清理掉记录,防止其他人伪造cookie登陆。中途也可以设置策略临时改变用以验证身份的cookie内容。
具体能有多安全,看服务器session的实现。
segment fault里面关于session原理的问题还是挺多的,可以搜索试试。https://segmentfault.com/a/11...

可以这样区分,假设你在一个化装舞会,大家按主办方要求全部打扮得一样。你为了确认你的同伴,约定了一个暗号就是cookie。任何人听到了都可以来冒充是你的同伴。session就是每次对暗号后都换个新暗号,你朋友走了还会告诉你一声不要继续和别人对暗号了。
有点hack的感觉,其实也不是绝对安全。
只能说大多数时候够用,真正机密信息都是靠证书,特定二进制文件或者其他协议。
要安全可以考虑上https,直接简单写cookie也没关系。都是明文的锅。

session可以看作是一条生命周期非常有限的cookie,作用在服务端。cookie技术则主要是指用http头信息记录用户在浏览器中的行为,多数时候用来给前端页面增强功能,也可以提供广告定制一类的服务。

术语不严谨,旨在理解精神。?
我开始的时候还蛮容易把服务器session的概念和HTML5的sessionStorage搞混的。注意用途不一样。

也可以有人伪造session_id啊

没错,session_id可以伪造,但是,session的有效期一般比较短,(有一些网站,你访问之后没有退出,静静的放着,过个1,2个小时可能就会提示你重新登录,就是因为session过期了.)一般鉴于session_id的有效时间比较短,所以没什么大碍.

登录过程

  • 首次提交用户密码以及记住密码选项到服务器,从数据库验证,成功返回两个cookie,一个是session_id的cookie,一个是用于下次登录免输入密码的cookie

  • 登录之后浏览网页,每次都会携带session_id到服务器,验证身份.如果服务器设置了session过期时间为2个小时,那么两个小时之后,你的账号就会强制被退出了,因为你的session过期了.

  • 下一次登录的时候,浏览器会携带上次账户密码登录获取到的cookie,服务器对这个cookie进行验证,成功的话就直接登录,失败就进行账户密码的登录.一般说的cookie泄露是指这个cookie泄露.

谢邀。
上面的回答基本已经说差不多了。
cookie的保存在本地时间比较久,有可能别人获取到了,还可以继续使用
session是在服务器存,一般就是登陆信息,cookie里面存个session标识,session过期时间是服务器设置的。一般为了服务器的性能,过期时间都在三十分钟以下。
你总不能说你的电脑实时被监控吧,实时被监控,也就不用什么cookie截获了,直接电脑什么都是透明的了。

顶一波楼上!哈哈,一看见这种问题,就习惯在答案中搜索有没有httponly字样。

至于题主说的伪造sessionId,这个伪造二字,是不是可以理解为暴力破解?如果是暴力破解,那基本上可以放心,因为sessionId本身就是有秘钥等特殊字符串组合生成的。

再说说窃取,如果加上httponly属性,那么基于浏览器端的XSS是不可能获取到的,剩下的泄密途径大概也就剩下被抓包了,如果上网环境不能保证,请求有可能被人随意抓包,那理论上神仙也救不了。

PS:之前在https://www.conoha.jp/zh/买VPS的时候,网络经常慢,所以有时候会开个VPN什么的,但当页面重启请求时,会让我各种重新登录,由此可见,sessionIdIP进行关联,也不失为一种办法。

关于伪造cookie的身份认证可以参考下csrf攻击

喔 大家回答的都好详细...赞一个

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题
宣传栏