Web新手,最近想写一个完整的博客系统练练手,登录认证这一块有些地方思路不是很清晰,请教一下大家。
首先,单独的Session和Cookie我思路很清晰,但是两者结合再加上数据库后,思路就不怎么清晰了。出于安全考虑,各位在开发的时候,在本地Cookie会放一些什么内容?本地的Cookie内容是怎么和服务器上的Session打交道?希望各位不吝赐教。
然后,有没有必要在user表中增加一个字段比如说叫做AuthCode,再浏览器POST过来的username和password去数据库对比成功之后,将这个AuthCode写到Cookie去,下次浏览器发送请求的时候直接对比username和AuthCode就行了?
其次,我看了WordPress和Typecho的数据库表结构,里面没有类似于AuthCode的字段,但是登录之后却会有这么一个类似于AuthCode的Cookie值,比如说下图Typecho的:
网上那些教程都只简单的说了下到本地设置个Cookie就行了,这样真的安全?
处理登录的时候,各位是怎么在程序中实现的?能否说一下大致思路?
感谢各位的解答
1
kslr 2015-08-16 18:18:17 +08:00 1
没必要用Cookie,用Session就好了
|
2
oott123 2015-08-16 18:26:19 +08:00 1
设置了 cookie 之后签个名
|
3
lyragosa 2015-08-16 18:31:25 +08:00 1
不涉及多设备登录,交叉登录,登出一台设备自动登出所有设备等复杂的安全性措施的话。
可以完全不使用session,代价是每个页面都要重新请求一次数据库读出用户信息,然后比对客户端的cookie cookie字串肯定要加密,简单的用几个hash加密加点盐再加点注册时间啊随机字符串就行。 这样做的好处就是程序结构非常简单清晰易读,也不涉及session什么的。缺点就是开销稍微大了一丢丢,不过无所谓啦。 |
5
imink 2015-08-16 19:32:55 +08:00 1
可以看下php文档里面的start_session() 方法(没记错的话),每次用户登录的时候session后台开启,保存用户的authCode(可以根据uid还有password加密),以确保如果要浏览需要验证的页面(比如修改个人主页,编辑日志)的时候拥有权限。至于cookie,由于存在本地,可以定时删除,一般的应用情景是,记录用户上次登录的状态(“勾选以便下次直接登录”),或者用户个人网站偏好。
你图片上的cookie,有可能是为了“勾选以便下次登录”而设置的。 我平常用的最多的还是操作session。 |
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 而已。 |
7
incompatible 2015-08-16 19:50:09 +08:00 via iPhone 1
@lyragosa 每个请求都读数据库?
没有一个web开发的内行会这么干 |
8
rogeecn 2015-08-16 21:22:29 +08:00 1
都没给撸说说SESSIONID是通过cookie带过去的么?就像你登陆了再清空COOKIE肯定要被退出管理权限呀。
|
9
Keita1314 2015-08-16 21:46:36 +08:00 1
第一次登陆之后,服务器的session保存当前会话信息,然后在数据库的一张表保存sessionID与用户名,同时将sessionID写入浏览器的cookie。
再次登陆,服务端通过浏览器的cookie中取得sessionID,然后到数据库中根据sessioID判断出当前登陆用户。 大概流程就是这样子。 |
10
lyragosa 2015-08-16 21:52:55 +08:00 1
@incompatible 说了啊 肯定有坏处的,会有额外的开销。
但是如果是小型的产品的话,与其花时间折腾为了节省这点性能不如做与业务有关的事情。 换句话说就是,如果产品只有100个人用,就不要折腾优化十万并发请求的事情。 |
12
incompatible 2015-08-16 23:07:07 +08:00 1
@lyragosa 6楼已给出php中集成session的成熟方案
我比较熟悉的java servlet规范中也原生预置了类似的session方案 我没看出来这些开箱可用的方案哪里“折腾”了,反而你的方案还要写额外的代码,这才是真的折腾 |
13
ferock 2015-08-17 00:04:21 +08:00 via Android 1
都是一样的东西,不走ssl 都不安全
|
14
msg7086 2015-08-17 02:48:36 +08:00 1
#7 @incompatible 每个请求去读数据库有什么问题?又不会影响什么速度。
|
15
incompatible 2015-08-17 04:10:20 +08:00 1
@msg7086
当然会影响速度 读取数据库要耗时间在应用服务器与数据库服务器间的网络IO、要耗数据库的CPU #3的最大问题不在于每个请求读数据库会影响速度,而是在于业内没人这么干。 如我12楼所说,PHP和Java中都有成熟的方案,非常简单,根本不需要造一个毫无必要的轮子。 |
16
xuhaoyangx 2015-08-17 04:14:24 +08:00 1
@incompatible 赞同
|
17
kanezeng 2015-08-17 09:01:57 +08:00 1
@incompatible 单服务器这样肯定没问题,不过这样的话session信息其实是保存在服务器内存本身吧。如果服务器扩展到两台甚至,有可能存在一种情况是用户的不同请求被分配到不同的服务器上,那么如果请求被分配到内存中没有session信息的那些服务器上,用户就又变成未登录状态了吧。
所以如果脱离单机环境的话,这种问题还是值得讨论一下的吧? |
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 被拦截了,这样的鉴权方式就算改密码都没用,怎么办? 只能靠楼主自己去了解相关的解决方式了。 |
19
incompatible 2015-08-17 10:56:48 +08:00 2
@kanezeng
集群环境下通常采用这两种方案之一: - 在 load balancer 上做 sticky session ,把来自某个客户端的请求永远转发至相同的应用服务器 - 把 session 信息放在 redis/memcached 等支持高并发的缓存中 数据库虽然也可以做集中式存储,但是在高并发情况下性能会急剧下降,所以没人用它存 session |
20
kanezeng 2015-08-17 12:23:49 +08:00 1
@incompatible 赞同,前面回复的时候我脑子里想的也是 redis , redis 的特性应该是最适合这种情况的吧。
|
22
abcfyk 2015-08-17 16:17:08 +08:00
@haiyang416 我晕。。对 LZ 这种新手来说,这个明显比 session 难多了好吧。。
对新手来说, session 可以简单认为就是安全的, cookie 是不安全的。存取 session 只需要非常简单一行代码就搞定。这还不够简单还要取 uid ,加盐, hash 。。。 LZ 会晕的。 |
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 : |
24
haiyang416 2015-08-17 17:11:36 +08:00 1
@abcfyk session 保存登录状态,浏览器关闭就没了,每次打开浏览器都重新登录?设置 cookie 的保存时间会比设置 session 的生命周期更难? 什么都保存在 session 里,只靠 session id 裸奔,真的会比 cookie 安全?
|
25
jugelizi 2015-08-17 21:42:22 +08:00 1
@abcfyk 不要误导人
session 是依赖 cookie 的 cookie 做的不好 session 没有任何安全可言 网上加密方法很多, userid 和 IP 以及特定的 key 生成 cookie 服务端可以解开基本就认为安全的 |
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 加密再到服务器端解,这是非常不靠谱的。我若是破解了你的加密算法,岂不是我可以想登录哪个用户就登录哪个用户? |
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 。 |
28
haiyang416 2015-08-18 05:04:48 +08:00 via Android 1
对此帖的回复到此为止。
楼主可以鉴别和选择对自己有用的信息,能解惑最好。 |
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
内容包含登录相关所有基本操作,建议一读。 |
30
jugelizi 2015-08-18 08:44:43 +08:00 1
想了下还是回复下 在 php 的 session 中默认使用的文件管理,而且过期删除机制的随机性,我不认为在大中型项目中使用 session 来做用户凭证,要么 cookie 走加密,参见 discuz ,或者用 redis 等实现 session 存储,默认文件读写的导致的锁是会有问题的。
|
31
incompatible 2015-08-18 10:53:42 +08:00 1
@haiyang416 为什么要” 请只用 session 实现「记住我」一个月的功能“?就因为你贴那个英文文章里建议这么做(并且没有解释原因)?
关于算法破解,你赢了,我个人破解不了 uid 为 2 的 hash😂 但是破解这个难道不是时间问题? |
32
haiyang416 2015-08-18 11:08:12 +08:00 1
@incompatible 最后回复一次,我给你一个理由,请试想这样一个情形:
楼主写好了自己的博客系统,登录状态保存在 session 里,楼主听着歌儿,打开博客编辑器,愉快的写着文章。 一篇很好的文章花了楼主 45 分钟时间,完成后点击了发布按钮,然后,就没有然后了。 由于楼主写文章过于专注, 45 分钟内没有刷新或者访问自己的博客,等到点击了发布按钮后才发现自己被跳转到了登录界面,由于楼主没有事先保存文章,所以写好的文章就这么丢失了,楼主怒了,我明明登录了,为什么为什么为什么现在又要我登录? |
33
devion 2015-08-18 11:22:52 +08:00 1
原来是这样的!
|
34
incompatible 2015-08-18 13:11:14 +08:00 1
@haiyang416 难道“只用 session 实现「记住我」一个月的功能”就能杜绝这个问题了?😢
另外,为什么 45 分钟楼主没有刷新 /访问自己的博客 session 就没了? 以及 #24 “ session 保存登录状态,浏览器关闭就没了,每次打开浏览器都重新登录”这又是为什么? 你确定你足够懂 session 的运行机制? |
35
haiyang416 2015-08-18 13:20:03 +08:00 1
@incompatible 你真的会 PHP ?
|
36
elvba 2015-08-18 23:04:55 +08:00
@haiyang416 很明显他是会的,我也想知道为什么 45 分钟没有刷新 /访问自己的博客 session 就没了,这个 45 分钟时怎么来的?另外 “ session 保存登录状态,浏览器关闭就没了”,你自己登录 V2EX 关闭浏览器后就要重新登录了么?
|
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 中是又怎么做的 ?前面某位,他的作品有几万人玩,我也玩了一年多了,但是也被说成外行,没办法。 |
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 有生存期,并不是只有关闭浏览器才失效。 |
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 绝对是不正常的。 |