你会做Web上的用户登录功能吗?
Web上的用户登录功能应该是最基本的功能了,可是在我看过一些站点的用户登录功能后,我觉得很有必要写一篇文章教大家怎么来做用户登录功能。下面的文章告诉大家这个功能可能并没有你所想像的那么简单,这是一个关系到用户安全的功能,希望大家能从下面的文章中能知道什么样的方法才是一个好的用户登录功能。以下内容,转载时请保持原文一致,并请注明作者和出处。
目录
用户名和口令
首先,我们先来说说用户名和口令的事。这并不是本站第一次谈论这个事了。如何管理自己的口令让你知道怎么管理自己的口令,破解你的口令让你知道在现代这样速度的计算速度下,用穷举法破解你的口令可能会是一件很轻松的事。在这里我想告诉从开发者的角度上来做设计这个用户名和口令的事。下面一几件规则:
- 限制用户输入一些非常容易被破解的口令。如什么qwert,123456, password之类,就像twitter限制用户的口令一样做一个口令的黑名单。另外,你可以限制用户口令的长度,是否有大小写,是否有数字,你可以用你的程序做一下校验。当然,这可能会让用户感到很不爽,所以,现在很多网站都提供了UX让用户知道他的口令强度是什么样的(比如这个有趣的UX),这样可以让用户有一个选择,目的就是告诉用户——要想安全,先把口令设得好一点。
- 千万不要明文保存用户的口令。正如如何管理自己的口令所说的一样,很多时候,用户都会用相同的ID相同的口令来登录很多网站。所以,如果你的网站明文保存的话,那么,如果你的数据被你的不良员工流传出去那对用户是灾难性的。所以,用户的口令一定要加密保存,最好是用不可逆的加密,如MD5或是SHA1之类的有hash算法的不可逆的加密算法。CSDN曾明文保存过用户的口令。(另,对于国内公司的品行以及有关部门的管理方式,我不敢保证国内网站以加密的方式保存你的口令。我觉得,做为一个有良知的人,我们应该加密保存用户的口令)
- 是否让浏览器保存口令。我们有N多的方法可以不让浏览器保存用户名和口令。但是这可能对用户来说很不爽。因为在真实世界里谁也记得不住那么多的口令。很多用户可能会使用一些密码管理工具来保存密码,浏览器只是其中一种。是否让浏览器保存这个需要你做决定,重点是看一下你的系统的安全级别是否要求比较高,如果是的话,则不要让浏览器保存密码,并在网站明显的位置告诉用户——保存口令最安全的地方只有你的大脑。
- 口令在网上的传输。因为HTTP是明文协议,所以,用户名和口令在网上也是明文发送的,这个很不安全。你可以看看这篇文章你就明白了。要做到加密传输就必需使用HTTPS协议。但是,在中国还是有很多网站的Web登录方式还在使用ActiveX控件,这可能成为IE6还大量存在的原因。我通常理解为这些ActiveX控件是为了反键盘记录程序的。 不过,我依然觉ActiveX控件不应该存在,因为在国外的众多安全很重要的站点上都看不到ActiveX的控件的身影。
用户登录状态
首先,我想告诉大家的是,因为HTTP是无状态的协议,也就是说,这个协议是无法记录用户访问状态的,其每次请求都是独立的无关联的,一笔是一笔。而我们的网站都是设计成多个页面的,所在页面跳转过程中我们需要知道用户的状态,尤其是用户登录的状态,这样我们在页面跳转后我们才知道是否可以让用户有权限来操作一些功能或是查看一些数据。
所以,我们每个页面都需要对用户的身份进行认证。当然,我们不可能让用户在每个页面上输入用户名和口令,这会让用户觉得我们的网站相当的SB。为了实现这一功能,用得最多的技术就是浏览器的cookie,我们会把用户登录的信息存放在客户端的cookie里,这样,我们每个页面都从这个cookie里获得用户是否登录的信息,从而达到记录状态,验证用户的目的。但是,你真的会用cookie吗?下面是使用cookie的一些原则。
- 千万不要在cookie中存放用户的密码。加密的密码都不行。因为这个密码可以被人获取并尝试离线穷举。所以,你一定不能把用户的密码保存在cookie中。我看到太多的站点这么干了。
- 正确设计“记住密码”。这个功能简直就是一个安全隐患,我觉得并不是所有的程序员都知道怎么设计这个事。一般的设计 是——一时用户勾选了这个功能,系统会生成一个cookie,cookie包括用户名和一个固定的散列值,这个固定的散列值一直使用。这样,你就可以在所有的设备和客户上都可以登录,而且可以有多个用户同时登录。这个并不是很安全。下面是一些更为安全的方法供你参考:
(——更新 2011/08/26,原文中有些小错误,并且说的不清楚,重新调整了一下——)
1)在cookie中,保存三个东西——用户名,登录序列,登录token。
用户名:明文存放。
登录序列:一个被MD5散列过的随机数,仅当强制用户输入口令时更新(如:用户修改了口令)。
登录token:一个被MD5散列过的随机数,仅一个登录session内有效,新的登录session会更新它。
2)上述三个东西会存在服务器上,服务器的验证用户需要验证客户端cookie里的这三个事。
3)这样的设计会有什么样的效果,会有下面的效果,
a)登录token是单实例登录。意思就是一个用户只能有一个登录实例。
b)登录序列是用来做盗用行为检测的。如果用户的cookie被盗后,盗用者使用这个cookie访问网站时,我们的系统是以为是合法用户,然后更新“登录token”,而真正的用户回来访问时,系统发现只有“用户名”和“登录序列”相同,但是“登录token” 不对,这样的话,系统就知道,这个用户可能出现了被盗用的情况,于是,系统可以清除并更改登录序列 和 登录token,这样就可以令所有的cookie失效,并要求用户输入口令。并给警告用户系统安全。
4)当然,上述这样的设计还是会有一些问题,比如:同一用户的不同设备登录,甚至在同一个设备上使用不同的浏览器保登录。一个设备会让另一个设备的登录token和登录序列失效,从而让其它设备和浏览器需要重新登录,并会造成cookie被盗用的假象。所以,你在服务器服还需要考虑- IP 地址,
a) 如果以口令方式登录,我们无需更新服务器的“登录序列”和 “登录token”(但需要更新cookie)。因为我们认为口令只有真正的用户知道。
b) 如果 IP相同 ,那么,我们无需更新服务器的“登录序列”和 “登录token”(但需要更新cookie)。因为我们认为是同一用户有同一IP(当然,同一个局域网里也有同一IP,但我们认为这个局域网是用户可以控制的。网吧内并不推荐使用这一功能)。
c) 如果 (IP不同 && 没有用口令登录),那么,“登录token” 就会在多个IP间发生变化(登录token在两个或多个ip间被来来回回的变换),当在一定时间内达到一定次数后,系统才会真正觉得被盗用的可能性很高,此时系统在后台清除“登录序列”和“登录token“,让Cookie失效,强制用户输入口令(或是要求用户更改口令),以保证多台设备上的cookie一致。
- 不要让cookie有权限访问所有的操作。否则就是XSS攻击,这个功能请参看新浪微博的XSS攻击。下面的这些功能一定要用户输入口令:
- 权衡Cookie的过期时间。如果是永不过期,会有很不错的用户体验,但是这也会让用户很快就忘了登录密码。如果设置上过期期限,比如2周,一个月,那么可能会好一点,但是2周和一个月后,用户依然会忘了密码。尤其是用户在一些公共电脑上,如果保存了永久cookie的话,等于泄露了帐号。所以,对于cookie的过期时间我们还需要权衡。
找回口令的功能
找回口令的功能一定要提供。但是很多朋友并不知道怎么来设计这个功能。我们有很多找回口令的设计,下面我逐个点评一下。
- 千万不要使用安全问答。事实证明,这个环节很烦人,而且用户并不能很好的设置安全问答。什么,我的生日啊,我母亲的生日,等等。因为今天的互联网和以前不一样了,因为SNS,今天的互联比以前更真实了,我可以上facebook,开心,人人网,LinkedIn查到你的很多的真实的信息。通过这些信息我可以使用安全问答来重设你的口令。 这里需要说一下 Facebook,Facebook的安全问答很强大,还要你通过照片认人,呵呵。
- 不要重置用户的密码。因为这有可能让用户的密码遭到恶意攻击。当然,你要发个邮件给用户让其确认,用户点击邮件中的一个链接,你再重置。我并不推荐这样的方法,因为用户一般都会用笔记下来这个很难记的口令,然后登录系统,因为登录系统时使用了“记住密码”的功能,所以导致用户不会去修改密码,从而要么导到被写下来的密码被人盗取,要么又忘记了密码。
- 好一点的做法——通过邮件自行重置。当用户申请找回口令功能的时候,系统生成一个MD5唯一的随机字串(可通过UID+IP+timestamp+随机数),放在数据库中,然后设置上时限(比如1小时内),给用户发一个邮件,这个连接中包含那个MD5的字串的链接,用户通过点击那个链接来自己重新设置新的口令。
- 更好一点的做法——多重认证。比如:通过手机+邮件的方式让用户输入验证码。手机+邮件可能还不把握,因为手机要能会丢了,而我的手机可以访问我的邮箱。所以,使用U盾,SecureID(一个会变化的6位数token),或是通过人工的方式核实用户身份。当然,这主要看你的系统的安全级别了。
口令探测防守
- 使用验证码。验证码是后台随机产生的一个短暂的验证码,这个验证码一般是一个计算机很难识别的图片。这样就可以防止以程序的方式来尝试用户的口令。事实证明,这是最简单也最有效的方式。当然,总是让用户输入那些肉眼都看不清的验证码的用户体验不好,所以,可以折中一下。比如Google,当他发现一个IP地址发出大量的搜索后,其会要求你输入验证码。当他发现同一个IP注册了3个以上的gmail邮箱后,他需要给你发短信方式或是电话方式的验证码。
- 用户口令失败次数。调置口令失败的上限,如果失败过多,则把帐号锁了,需要用户以找回口令的方式来重新激活帐号。但是,这个功能可能会被恶意人使用。最好的方法是,增加其尝试的时间成本(以前的这篇文章说过一个增加时间成本的解密算法)。如,两次口令尝试的间隔是5秒钟。三次以上错误,帐号被临时锁上30秒,5次以上帐号被锁1分钟,10次以上错误帐号被锁4小时……但是这会导致恶意用户用脚本来攻击,所以最好再加上验证码,验证码出错次数过多不禁止登录而是禁lP。
- 系统全局防守。上述的防守只针对某一个别用户。恶意者们深知这一点,所以,他们一般会动用“僵尸网络”轮着尝试一堆用户的口令,所以上述的那种方法可能还不够好。我们需要在系统全局域上监控所有的口令失败的次数。当然,这个需要我们平时没有受到攻击时的数据做为支持。比如你的系统,平均每天有5000次的口令错误的事件,那么你可以认为,当口令错误大幅超过这个数后,而且时间相对集中,就说明有黑客攻击。这个时候你怎么办?一般最常见使用的方法是让所有的用户输错口令后再次尝试的时间成本增加。
参考文章
- OWASP Guide To Authentication
- Dos and Don’ts of Client Authentication on the Web (PDF)
- Charles Miller’s Persistent Login Cookie Best Practice
- Wikipedia: HTTP cookie
- Personal knowledge questions for fallback authentication: Security questions in the era of Facebook
(转载本站文章请注明作者和出处 酷 壳 – CoolShell ,请勿用于任何商业用途)
《你会做Web上的用户登录功能吗?》的相关评论
我也经常被各种密码搞得头大。
很有用,受教了,一直没深入研究过这东西,现在有更完善的思路了。
这个很不错,原来一个看似简单的登录也有这么多学问。
哈哈,很好很强大,受教了~
恩,是得保护用户信息。。。必须的么~~
原文中的”如果用户的cookie被盗后”,应该是指cookie文件被盗吧?如果在用户登录期间,有人通过firecookie之类工具实时的查看cookie的话应该连sessionId也能看见了.
另外我对于”登录序列”和”登录token”的问题还是有点不明白,只要一个”登录token”就可以了吧?
对于登录用户,每次操作都会从浏览器带回”用户名”,”登录token”以及”sessionid”.
而对于复制用户的文件cookie的情况,只能带回”用户名”,”登录token”,而会新产生一个sessionid.事实上这个时候就应该登录失败了吧?因为sessionid和登录token不匹配了.
文章不错,很值得学习。
很好恨强大,受益匪浅,转载了
不潜水了,很好啊!
非常实用的文章,拜读了!谢谢
登录token,和登录序列有点不明白。
真正的用户在头一个session过期登录,那个登录token不也是一样不同?怎么区分是被盗用了呢?
@hi
如果没有server端的session呢?
要真正的安全,只能多通道验证。pc、互联网都是一个通道,手机短信是另一个,一次性的口令纸也是一个。。。。
安全与(系统与用户的)麻烦、成本是成正比(不一定线性)的。
如果有”找回密码”的功能, 就不可能用单向加密.
所谓找回,只是用随机密码重置密码,并把该随机密码发给你。应该算是找回控制权
“如MD5或是SHA1之类的有hash算法的不可逆的加密算法。”这一说法不正确,MD5、SHA1都不是加密算法。
用户名,token,和sessionid都是存在cookie里的,应可以一起搞走吧。
啊。。登陆还可以做的这么好!
如果不用明文保存用户口令,那找回密码怎么办?
@FreeCody
看来只有让用户重设密码了。。。
盗用者使用这个cookie访问网站时,我们的系统是以为是合法用户,然后更新“登录token”
————————————————————————————————
更新登录token,不是需要用户名、密码吗进行登录吗?只要盗用cookie就可以了吗?不是太明白这里面的逻辑
另外,如果一个用户在IE与Chrome都登录了,那两个浏览器记录的三个cookie是完全一样的吗?会不会造成同一时刻只能在一个机器的一个浏览器下浏览你的网站呢?
文中的那个做法是排他性的,也就是说,在一个时间里,只能有一个登录。
最近刚好在做很多网站的项目,受教了
很好,喜欢技术型的博客!
相当有意义的文章,谢谢了
坦白说,“正确设计密码”这小节有点看不懂:
“无需更新服务器的“登录序列”和 “登录token”(但需要更新cookie)”
cookie和服务器session值不是应该一致的吗?
写得很好,受益。
刚好我前两天也写了一篇类似的,写完才看到这篇。
贴出来跟大家分享一下,欢迎拍砖 http://spud.in/tech/how-to-design-a-login-module.html
我個人會採用一些密碼學的演算法,在登入時先將帳號密碼加上時效性丟入運算
Javascript 的部份只提供編碼,程式碼核心全部混淆過
如果是非常重要的系統,則會再加上實體系統驗證,例如手機驗證碼
其實設計再多,有一點還是要銘記在心
鎖是可以被破解的,我們延長小偷破解的時間,但仍無法保證他是非常安全的
很好,之前有玩过,但没有你这么强大,呵呵,受教了。 你的博客我已经看了好久了。哇咔咔。
发现博主脑中总被一些所谓“好”的概念框住了
关于“记住密码”哪个功能,如果采用容器管理的身份认证,不明文保存密码,或者不用可逆加密保存密码,恐怕会有一些困难。例如 Servlet 3.0 的 HttpServletRequest.login(String username, String password) 方法,你不传入明文密码是不能登录的。
有意思!^^
我跟@hi2 的问题一样,既然可以盗用cookie那么为什么不盗用3个(用户名,登录序列,登录token),这时怎么防盗呢? 麻烦博主解释一下 :)
另外http是明码提交,这时我们还可以对password 做个md5再提交, db里的值可以再加个密钥.
验证密码时: md5(requestPwdMD5+privateKey) = dbPwd
关于防盗,文中应该解释清了。对于你的第二个问题,我在文中说了,加密传输只有https一条路,没有之一。 你在client端做的md5同样可以被截获,截获后发给服务端,这个和明文传输口令本质上没有什么区别。
毫无新意
登录token,和登录序列有点不明白,请楼主详解,菜鸟
水平很差,每个建议都不是最好的解决方案。
看了文章的描述,觉得“登录序列”并没有起到什么作用,登录token可以视为一个sessionid吧?
根据文中3)b)所表述,当cookie被盗时,系统以为是合法用户,然后去更新“登录token“,这是不是说每次访问都要更新一次”登录token”。当cookie没有被盗时,自然系统认为当前用户也是合法用户!也要更新“登录token”?
@hwywhywl 是的,文中第3)点的设计就是这样的,在同一个时候只有一个合法用户,不支持用户的多个设备无口令登录,不支持用户的单个设备多个浏览器的无口令登录。
比如:你在A上登录了,过后你在B上又登录了,然后,A再登录,这样会导致所有设备浏览器上的cookie失效,所有的地方都需要重新输入口令。
4)对此做了一些优化。比如,先在A上用口令登录(在A上设置cookie),然后在B上用口令登录(在B上设置cookie),此时的A和B上的cookie是完全一样的。等下一次的时候,A和B的ip都变了,此时需要看时间和次数,如果时间够长,认为是合法用户,服务器商用更新token,客户端更新cookie。
看了文章,觉得把登陆这个经典功能分析的很详细,但是关于登陆序列和登陆token的部分还是又疑问:
先确认下,我的理解:
登陆序列:每个用户进行一次登陆操作(需要输入用户名和密码,也就是由未登录转变为登陆的操作)会生成
登陆token:每个已经登陆用户对服务器的每一次请求都会更新这个值
然后,为什么需要验证登陆序列这个值,我的理解是,这个值存在且匹配才说明用户已经登陆(至少username对应的用户登陆过)
关于盗用行为监测这块可以理解。那么同样是利用这三个值,我觉得是不是也可以直接来实现同一个用户多个设备登陆呢?
session可以是这样:
username: {
登陆序列1:登陆token,
登陆序列2:登陆token
}
每一次用户的需要输入命令的登陆都会产生一个登陆序列。
当一个用户的cookie被盗,比如登陆序列1,那么坏人登陆后,登陆token1会更新,然后真正的用户(登陆序列1)登陆,就发先token不匹配了,于是就发现又问题,这个时候具体怎么操作可以根据自己的需求,比如可以把username下的所有登陆序列都销毁掉
不知道这样的设计是否可行?
这篇文章很不错!难的回复,支持!
学习了这篇文章 支持一下
之前我们还是用 session做过!唉。。。
而真正的用户回来访问时,系统发现只有“用户名”和“登录序列”相同,但是“登录token” 不对,这样的话,系统就知道,这个用户可能出现了被盗用的情况
这句话我不大明白,用户回来时,这时他的session是仍然有效的,当其他用户(假设B)盗用cookie登陆之后,会在B的session里产生一个新的token,但是session并不是对每个用户共享的,而合法用户session里面的token并没有改变,他又是如何发现token不匹配的?
如果cookie被盗用,而本人不再使用呢?是不是应该在不同ip而相同cookie情况下清空cookie,让用户重输密码呢?
SQL注入楼主能详细介绍一下嘛?
能不能写一篇文章详细说明一下session和cookie?谢谢了。