avatar

Derek Zeng

A programmer

About Authentication

by coderek

这篇文章记录一下我对验证浅薄的认识。

身份认证是网络应用真非常重要的一个部分。通常我们做认证的目的有两个

  1. 确认请求发送者身份
  2. 授权用户资源给第三方应用

这里主要说的是第一种。

最简单的情况是用户名和密码验证。客户端让用户输入用户名和密码,然后发给服务端进行验证。服务端在数据库里通过用户名找到被加密的密码。然后用相同方式加密收到的密码并跟数据库密码比较,如果相同就验证成功。验证成功后,服务端会生成一个会话对象并保存在服务端。这个会话的id会作为验证的结果返回给客户端。返回方式是把sessionid加在在http response header里的"set-cookie:"项。 客户端一般将这个id储存在cookie里面(浏览器会自动处理),并在接下来跟服务器的交流中都会将这个cookie附带在header中发给服务端。服务端以此来得到会话对象以及相应的状态。

这里有几点要说说。

首先是加密哈希的区别。 加密是使用一个密钥和一定的算法算法将明文转换为一个字符串的过程。这个字符串是可以转换回去的。现在有的算法比如说对称的AES,非对称的RSA。 哈希码也是将明文转换成字符串的方法,但它是单向的,目前有的算法比如说,md5和sha1

对于密码,一般能不存就不存,如果存就一定要哈希。md5, sha1都不是很安全, 但一般大家还是会用sha1。

在ruby 里面生成sha1很简单

require 'digest/sha1'
Digest::SHA1.hexdigest 'foo'

另外,对于会话这个东西,有些应用会在用户第一次请求收到是创建。然后如果用户后来登入成功,服务端还会创建一个新的验证过的会话对象。这样做的好处是,用户在没有登入的情况下也可以保存自己的状态,当然这个未登入的状态也可以在客户端以localstorage的方式保存。

会话在服务端的保存方式有很多,可以是数据库,文件保存。现在nosql比较流行,也可以用像mongodb这样的document store来保存在内存里面。有条件的话,后者当然是最好的了。一个是快,另外会话这种东西放内存里比较好清除。

客户端的在发送请求的时候一般会把cookie放在http header里面

GET / HTTP/1.1
Host: disqus.com
Connection: keep-alive
Cache-Control: max-age=0
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_1) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.97 Safari/537.11
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Referer: http://disqus.com/profile/login/?next=http%3A%2F%2Fdisqus.com%2F
Accept-Encoding: gzip,deflate,sdch
Accept-Language: en-US,en;q=0.8
Accept-Charset: UTF-8,*;q=0.5
Cookie: __utma=40641725.1068985267.1355748103.1355748103.1355748103.1;sessionid=61ad04d74dcb9e14a4ceb6226f989858

cookie只是客户端persist state的一种方式。在现在API 流行的时代,token based使用起来也很方便。客户端在发放请求是只用将sessionid放在query string或form data来证明自己身份即可。

会话一般都会有一个期限,过了这个期限,任何发自同一个客户端的请求都将被看作一个新的请求。当然服务端也可以主动吊销这个会话。过期的会话通常会被清除。

最后说一下安全性。因为会话对象是用会话id来对号,所以如果这个id被别人知道了,那么他就可以冒充这个对话的主人来跟服务器对话。这是非常危险的。试想如果这个对话里保存着用户的信用卡信息,后果是很严重的。客户端有义务确保这个id的安全,方式可以是加密保存,并且不让应用随便读取不属于它的信息等等(cross site request forgery)。另外,客户端与服务端在验证后一定要用https加密通道,以防有人监听网络。

PS: 有一种需求是要将密码存在会话里面。这个密码会在接下来的请求中使用到。通常是对第三方有关联的应用做认证。术语叫Single Sign On (真正的SSO是不需要密码的,但很多时候两个系统并没有很好的合作关系的时候就需要直接用密码跳过登陆界面了)。有两种方案可以存储这种密码,但都是要加密并且在会话结束之后清除密码。 首先是存在服务器端内存中,这个方案我个人觉得不是很安全。程序员如果知道算法跟密钥就可破解。 其次是存在客户端,有些客户端有存储敏感信息的机制,并且东西在用户自己手中。发生什么事情也不会怪到程序员身上。

(End of article)