从wap网站的认证授权到csrf的协议类比本质
作者:云舒
摘要:这个文章是周五开始酝酿的,前半部分是总结,最后一小段只是为了说明一下安全的相通性,将csrf类比到tcp协议,从协议的角度阐述一下 csrf产生的本质原因。想不到写到一小半就腰酸背疼,大部分内容都只能草草带过,很多地方需要进一步解释,都没法深入的描述下去了。一直说要坚持健身, 却从来没去过,是在是罪过,先凑合着看看吧,有问题还请指出一下。
现在WAP站是越来越多了,尤其是SNS兴起之后。不过由于不同的手机不同的浏览器对cookie的支持不同,因此wap网站为了兼容性一般不能使 用cookie做用户认证之后的身份鉴别。cookie是session的基础,因此session同样不可用。那么如何安全的实现wap网站不同用户的 授权问题?这里简单总结一下可用的两种可靠的方案。根据这个总结,顺便引申一下普通网站的csrf问题。(虽然session的基础是cookie,但是 这里还是把它们作为两种不同的东西来描述)
第一种方案是伪session方案。先看看传统网站的session原理,客户端cookie中保存一个唯一的session id,这个id和服务器的一个文件对应,经过扩展之后这里可以是内存或者保存在数据库中内容。用户访问网站时,cookie里带上session id,服务器根据session id获取对应文件的内容,验证用户身份或执行其它操作。传统session的关键在于两点,第一点是服务器保存一个名字唯一的内容,可存储在文件,内存, 数据库或者其它地方。第二点是客户端保存这个内容的那个唯一的名字,并在每次访问网站的时候带上。
使用session方案时,在普通网站转变成wap网站之后主要存在二点改变,第一点是客户端要在没有cookie的情况下每次请求发起时将 session id发送给服务器。简单地说,GET请求的时候可以直接将session id附加在URL之后,POST请求的时候session id作为form的隐含字段发送到服务端,服务器从内容中获取这个session id进而获取保存在服务器上session内容,进行权限验证方面的事项。最终get请求生成的url类似 /getlist.do?sid=sessionid,post则为form中的隐含字段。
其次的变化在于要在服务端实现和原始session略有不同的session生成读取设置机制,以及将session id传送给客户端的方法。服务端在用户登录成功之后根据算法生成一个session id,session id的生成可以使用足够长度的随机数,也可以使用不可逆的hash算法,例如MD5算法,session id = MD5(loginname + logintime + salt)。同时,使用session id做为文件名生成一个文件(当然也可以是内存,或者以session id为主键的数据库条目)保存用户的关键信息,例如用户登录名登录状态等。生成完毕,在返回登陆后的页面给客户端时,将session id附加在get请求的url和post请求的form之后。关于伪session的读取和设置,主要改变在于session id的位置,从来源于cookie变为来源于get参数或者post内容,这个非常简单,获取到session id之后直接修改对应的文件(或者内存,数据库条目)即可。
通过上文的两点,即可在wap站点中实现一种session机制,作为登陆后的权限判断等操作的基础。随着新的web架构的发展,为了负载均衡的方便,更多的系统开始去中心化,客户端自己维持自己的状态,我在《Restful风格WEB架构需要注意的两点安全问题》也提到过。因此,有必要实现一种伪cookie机制来作为认证授权的基础。
伪cookie机制和伪session机制的区别,与cookie机制和session机制的区别一样,都在于是否在客户端保存关键数据,session 类机制客户端仅需要保存一个session id和服务端对应即可,服务端保存关键信息,而cookie类机制则全部保存在客户端。伪cookie机制和伪session机制的实现方法类似,服务端 对客户端的设置以及客户端到服务端的发送完全一样,区别在于它们的生成。这是因为session id仅仅在客户端和服务端之间起对应作用,而cookie本身就需要作为验证的数据,所以使用cookie机制时要保证服务端能解密读取cookie本身 的内容,这就决定伪cookie机制只能使用可逆的加密算法,而不能采用生成session id的MD5类HASH算法。
今天比较累,就懒得细写为什么了,大致描述一下就算了。cookie的加密算法使用对称加密如AES,或者非对称的RSA等均可。生成时方法大致如下 base64(RSA(“uniqid=random()&loginname=test&loginresult=success&logintime=20090222235143”, private_key)),签名可以md5(url+cookie+salt)实现。uniqid是用来在用户退出的时候使用的,logintime记 录时间戳是用来自己实现伪cookie过期机制使用的,其它的好理解。至于为什么伪session机制不需要签名而此处需要签名,那是因为他们的安全要求 不同。伪session的时候即使伪造了session id,但是由于session id对应的内容在服务端不存在,因此不存在安全问题。而伪cookie的授权机制,一旦加密算法破解出现了伪造的cookie,还可以通过加入salt的 md5签名将其丢弃。
继续快速推进。现在说说wap中的这种机制在普通网站中的作用——防御csrf攻击。这个很容易,简单的将伪cookie机制中的算法略作简化即可。使用签名自检验保证不伪造,使用时间戳保证不被重放,而且与传统的方案相比无需服务端对生成的随机token串做记忆对比。
比较有意思的是csrf产生的原因,这里我不从web的角度去说,而尝试从协议的角度来解说一下。我主要的目的是想说,安全都是相通的。csrf简单的说 就是用户登陆站点a之后,在访问站点b的时候,在站点b后台向站点a发起了一次请求,而且站点a执行了。从协议的角度来看,这是因为http协议在多次的 get,response之间是无状态的,是混乱的。用户与a站点的交互之中,被插入了一个数据包,而且被a站点执行了。那么为什么tcp层不会发生这种 包的插入执行最终导致混乱的结果?很简单,tcp包有seq和ack序列号。同样的,udp协议也有类似csrf的问题,协议中被插入伪造。但是基于 udp的dns协议却是比较可靠的,为什么?还是因为dns请求和回应的时候有一个dns id存在。基于这种协议的类比,显然现在所有的csrf方案都是为http协议实现一个类似的协议id。
作为协议的序列号来说,一般的都是由客户端生成的初始值,随后在客户端和服务端的交互中变化。而由于WEB客户端的原因,csrf防御方案实现的协议序列 号只能由服务端主动生成,随后交互,变化。在tcp协议中,一个tcp session中知道seq和ack的初始值即可推断出其它包的序列号,某种意义上说与tcp类似,在csrf的防御中以用户的一次登录活动为协议id的 一个生存周期,在安全上说是可以接受的。
大雨之夜,睡觉。现在确实是老了一些,精力不济啊,才11点半就累得不行了。