感谢IT牛人博客聚合上还能找到我之前博客的一些内容,因为之前主机没续费等各种原因,也懒得翻之前的备份文档了,就把那上面的文章直接拷过来整理一下好了,只搬迁一些还有意义的内容好了,其他的就让它随风而去吧。
话说月初的时候曾想玩玩移动app开发,目前app开发比较流行c/s架构,不,用时髦点的话说是云端架构。对于网络应用来说,第一个遇到的问题就是用户身份验证。之前基本上就没做过c/s架构的东西(和b/s对应的c/s),所以在这方面算是空白。后来面试过两个Android开发者,把这个问题当做一个系统设计的题目来问,也没有得到更多的想法。
当年做b/s时,用户身份验证这种事靠的是cookie&session,即浏览器发送请求时携带写入cookie的session_id,服务端通过session_id可以得到用户的一些信息。但session的局限性在于服务器必须维持这个状态。除此之外还有纯cookie,就是把需要维持key-value对写入浏览器的cookie里,为了安全还可以加个密。
在研究了新浪微博api的验证方式后,发现除了oauth外还有http basic auth这种方式。前者不多说,基本上用开放api开发应用都会用到。后者是通过将用户名和base64后的密码通过http headers发送给服务器进行验证的一种方式,缺点很明显,每次都得发送密码,尽管有加密,但还是有密码泄露的风险。
面试一个做过android上c/s app开发的同学时,他说他们app用的方法类似于以前open api的认证方式,就是每次请求中携带一个用作身份标识的token,服务器根据这个token来判断是谁发来的消息。这种认证方式的好处是不需要每次都发密码,但缺点同样明显,就是一旦token被截获,那么就可以随意向服务器发送请求了。
根据oauth的原理,以及脑海中仅存的上过几节的《信息安全》基础知识(这时候真恨大学时没怎么上过信息安全这门课),大概研究出了一种方案。实际上就是利用hmac这种加密算法,客户端用密钥对请求消息就行签名,然后将签名和请求消息一起发送给服务器,服务器继续利用密钥对请求消息签名,同时判断是否和客户端发送的签名结果一致。由于密钥并不随着网络交互进行发送,因此可以认为这种验证机制是安全的。
以微博客户端为例,用户在发微博时,服务器必须确认该客户端是这个用户,才能给这个用户发微博。不局限于http协议通信,假设客户端服务器通过普通tcp socket通信,消息为json。
当用户注册时,生成一对(user_token, user_secret)
,其中user_token用于标识用户,而user_secret为密钥之一,随着密码的改变而改变。当用户登陆时,生成一对(login_token, login_secret)
,这对的生命周期和登陆状态保存时间等同。比如客户端向服务器发送请求为{"user_token":"xxxx","login_token":"yyyy","action":"send","message":"tweet a twitter"}
,因为对象的属性可以认为有无序性,因此需要将key按字典序排列,得到一个字符串进行签名,即对字符串action=send&login_token=yyyy&message=tweet a twitter&user_token=xxxx
进行签名,oauth中还需要对其进行urlencode,使用的密钥为user_secret&login_secret
,假设签名为abcdefg,同时客户端需要将token发送给服务器,以便服务器确定用哪个密钥进行签名,即最终发送的json消息为{"user_token":"xxxx","login_token":"yyyy","action":"send","message":"tweet a twitter","signature":"abcdefg"}
。服务器收到请求消息后,即可以通过token拿到密钥,并对除signature以外的部分进行签名验证是否和signature相同,从而确认这个消息是否是由合法的用户发出的。
当然,这只是自己根据oauth衍生出来的想法,不知道真正的移动应用c/s验证是怎么搞的。以前在淘宝实习时,曾经听过一堂web安全方面的分享,也提到过所谓的cookie双token验证,不知道原理和这个是不是类似。还有几个问题没有想明白:
从安全的角度来说,虽然发送正常请求时,光有明文和密文没办法得到密钥,但登陆的时候总是要由服务器将密钥发送给客户端的,这个时候还是容易泄密。不过最终hmac签名的密钥可以再加上一段服务器和客户端约定好的字符串。但如果客户端被反编译,这段字符串仍然可以被找到。那么这段字符串就应该也是生成的,比如客户端的请求消息里加上timestamp,服务端用timestamp+user_token再签出一段字符串用作签名消息体的密钥的一部分。不过问题就是这个哈希算法应该也可以破解出来。不太明白oauth中的timestamp意义在哪?
第二个就是双token双secret到底有没有用,能否简化成只有login_token, login_secret, user_token。通过user_token和timestamp在客户端构造出一个临时的secret作为密钥的一部分,然后服务器利用相同的hash算法生成这个临时secret做签名,似乎也能保证安全。
还有就是虽然没有办法伪造消息,但如果截获这个消息,就可以无数次发这个包给服务器,这是一条有效消息。莫非timestamp的作用就是有个过期限制?