并用Go完成了Demo。本人对安全外行,不过自我感觉应当是个比较有安全感的身份认证方案。
不想写大段文字来说明,看得懂的,依着代码来瞧瞧吧.
首先,协议包应当包含最少下面几个字段:
type Proto struct {
//...
Timestamp int64 `json:"timestamp"` //当前时间戳
Nonce uint32 `json:"nonce"` //随机数
Signature string `json:"signature"` //Hash_hmac("sha1",sortAsc(Token,Timestamp,Nonce))
BodyCrc string `json:"bodycrc"` //Hash_hmac("md5",body)
UserID string `json:"userid"`
Body string `json:"body"`
//...
}
然后,用户登录时,是能得到其对应的Token的,并且这个Token在服务端是有有效期限管理的.
App端将用户Token保存在加密后的sqlite库中.
当App端要与服务端通讯时,在App端首先用相关参数时进行哈希.
Signature = Hash_hmac("sha1",sortAsc(Token,Timestamp,Nonce))
BodyCrc = Hash_hmac("md5",body)
然后再传给服务端,注意协议包中是无须再传用户的Token的。
用户的Token在这仅作为hash字符串中的一个参数而已。
服务端收到后,就可以依参数做一系列的验证了。
关于身份部份,可以做如下检验:
///
// 身份合法性验证
///
u, err := UserDB.Get(msg.UserID)
if err != nil {
s.Stop()
log.Println("[IOLoop] [UserDB.Get] 用户库中没有找到此用户,断开连接.")
return
}
if UserDB.IsBlacklistsUser(msg.UserID) {
s.Stop()
log.Println("[IOLoop] [IsBlacklistsUser] 属列入黑名单的用户,断开连接.")
return
}
// Signature = Hash_hmac("sha1",sortAsc(Token,Timestamp,Nonce)))
// 检查token是否正确
// 防止选择明文攻击
if token, ret := u.CheckSignature(msg.Timestamp, msg.Nonce); ret {
if !u.IsTokenExpire(token) {
s.Stop()
log.Println("[IOLoop] [IsTokenExpire] 为过期的用户Token,断开连接.")
return
}
} else {
s.Stop()
log.Println("[IOLoop] [CheckSignature] 签名不符,属非法用户,断开连接.")
return
}
其中关于Signature的部份,服务端是同样依Timestamp+Nonce再加从相关库找出来的用户Token依相同算法来Hash. 对比
其值是否相同.
这部份主要用于验证消息包的合法性,或者可以用随用户token一起生成用户的aes密钥,然后用aes将其加密后在服务端将消息内容再解密。
///
// 消息包合法性验证
///
//如BodyCrc值为空,则不进行检查
if s.CheckBody(msg.BodyCrc, msg.Body) == false {
s.Stop()
log.Println("[IOLoop] [CheckBody] 数字签名不符,消息内容有被改过,断开连接.")
return
}
// 是否为重放攻击
if s.IsReplayAttack(msg.Timestamp, msg.BodyCrc) {
s.Stop()
log.Println("[IOLoop] [IsReplayAttack] 碰到重放攻击,断开连接.")
return
}
///
还有一些就不列了.
相关的一些结构体:
type MemStore struct {
users map[string]User
mtlock sync.RWMutex
//...
}
type Session struct {
//...
UserID string
MsgLastTimestamp int64 //最末消息包Timestamp
MsgLastBodyCrc string //最末消息包BodyCrc
//...
}
type User struct {
ID string
Token string
CreatedAt time.Time //Token生成时间
ExpiresAt time.Time //Token超期时间
//...
}
这套方案我认为还算简单可行。不过相关人士表示不太感冒,认为不够安全(似乎分分钟被破的节奏),要用更安全更复杂的方案。
好吧,这方面不太懂,不过好歹也花了我点功夫。记一下,不然过段时间就不记得了。 反正用的也都是烂大街的东西。
也没被采用,应当无甚关系。
BLOG: http://blog.csdn.net/xcl168