My favorites | Sign in
Project Home Downloads Wiki Issues Source
Search
for
SecurityDesign  

Featured, Phase-Design
Updated Sep 21, 2011 by i...@nleven.com

#Security Design of SecureGAppProxy

目的

本文的目的是文档化SecureGAppProxy 0.3 beta的安全设计,以便评估其安全性。

设置密码阶段

  • 密码验证:为了保证一定的密码熵。
    • 密码组合规则
      • 7位及以上
      • 包括字母、数字
      • 去除重复的长度大于等于3的子串后,密码长度仍然大于4。(如:123123ab将被处理成ab)
    • 密码字典检查:将密码和密码字典中的密码比较相似度
      • 编辑距离 > 3
      • 密码长度 - 最长公共子串长度 > 4
  • 哈希计算
    • 密码会被转换成哈希值,然后再参与后面的密钥协商。
    • 计算过程使用PKCS#5的PBKDF1,参数如下:
      • 哈希函数:SHA256
      • 迭代次数:2^19
      • salt: your-appspot-id.appspot.com
  • 密码部署
    • uploader负责将哈希值部署到服务端上。
    • 具体方法是直接建立一个password.py的程序文件,然后把哈希写进去,然后和其他程序一块上传部署。

登录阶段

  • 登录阶段基于IEEE Std. 1363.2 BPKAS-PAK方案。它源于论文Provably Secure Password-Authenticated Key Exchange Using Diffie-Hellman
    • 256位的椭圆曲线群secp256r1,参看SEC 2: Recommended Elliptic Curve Domain Parameters
    • REDP函数用的是ECREDP1
    • 需要用哈希函数的地方全部用的是sha256
    • BPKAS-PAK中最后一轮客户端向服务器的密钥确认是可选的,但是SecureGAppProxy没有省略。
    • 协议使用的是之前计算的哈希值,哈希值将直接存储在Google App Engine上。请注意,如果攻击者获取了这个哈希值,则不需要知道密码,就能登录SecureGAppProxy。因为密码本身和登录没太大关系。计算哈希的目的只是不想让攻击者能看到原始密码,因为密码经常隐含用户的一些隐私信息。
  • 使用BPKAS-PAK可达到以下目的
    • 双方都能确认对方持有密码。
    • 为后续通信准备会话密钥。一个是加密密钥、一个是鉴别密钥。
    • 提供前向安全性。(如果密码泄露了,虽然之后的通讯不再安全,但是已经发生的通讯数据仍然是安全的)
    • 抵抗离线字典攻击。(即便攻击者截获了整个通讯过程,无法仅通过拦截下的数据就用密码字典攻击。)
  • 密钥派生
    • 完成密钥协商后,就可以派生出加密和鉴别密钥,为之后的通讯作准备。
    • 加密密钥 = KDF1("Encrypt")
    • 鉴别密钥 = KDF1("Authenticate")
  • 抵御在线词典攻击
    • 服务器维护一个计数,记录当前在进行的或失败的登录尝试。计数每24小时清零。
    • 如果计数超过5,则登录时会给出一个链接,要求验证用户是否是人类。
      • 该链接由reCAPTCHA MailHide生成。原本是用来保护电子邮件地址被spammer的爬虫抓到,这里借用来保护一串数字。然后客户端向服务端提交这串数字表明屏幕前的是个人类……
      • MailHide服务需要API Key,目前SecureGAppProxy会自己去申请一个Key,不用用户干预。
      • MailHide链接生成方法,请参考这里
  • 记住密码
    • 0.3beta后允许在本机上保存密码,自动登录。
    • 密码的哈希值会保存在proxy.conf中。
    • 一旦包含哈希值的proxy.conf泄露,应该停止使用旧密码登录SecureGAppProxy,因为此时安全性已经无法保证。并应该立即设置新的密码。

加密通信

  • 加密为CBC模式的AES,密钥长度256位。
  • 消息鉴别码用的是SHA256的HMAC,鉴别密钥的长度是256位。
  • 消息格式:IV | Encrypt( Compress( MSG_ID | MESSAGE ) ) | HMAC
    • IV,128位的初始向量。
    • MSG_ID,128位的消息编号。
      • MSG_ID由两部分组成,前64位是一个时间戳,后64位是随机数。
      • MSG_ID有效时间为15分钟。只要还没有过期,服务器和客户端会保存所有收到的MSG_ID。如果收到的MSG_ID重复,或者过期,则认为发现了重放攻击。
    • 使用Encrypt then Authenticate
  • 时钟同步
    • 由于用了时间戳,要求客户端和服务端之间时钟同步(虽然精度要求很低)。因此,本地客户端启动后会同步时钟。
    • 具体方法
      • 向一组知名Web网站发起GET请求。
      • 取得HTTP应答中的Date字段。
      • 收集所有结果,取中位数。
    • 同步后,就得到了一个相对本地时钟的偏移。

一些问题

Q:使用SecureGAppProxy浏览https安全吗?

A: 安全,整个过程能看到明文内容的只有客户端和GAE。假定GAE是安全的话,那就是安全的。

Q: 使用SecureGAppProxy浏览http安全吗?

A: 线路1上的嗅探者什么也不会得到,但线路2上的通信仍然是明文的。不过在这点上,VPN也是这样的情况。

Q: 浏览时匿名吗?

A: 如果单纯依靠IP来判断浏览的来源,是匿名的。但是,GAE用urlfetch请求时,会告知服务器your-appspot-id.appspot.com。Web服务器和线路2上的攻击者能知道这个信息。如果攻击者还能控制线路1,从appspot-id追查使用者ip恐怕是不难的。

Comment by Lyricco...@gmail.com, Jun 23, 2011

访问 HTTPS 不一定安全的. GAE 的 urlfetch 在HTTPS中使用的 CA 列表和客户端的 CA 可能不一致 也就是说GAE认可的 HTTPS 在客户端未必被认可 这点是无解的(至少在GAE提供目标主机的证书之前是无法解决的)

Comment by project member i...@nleven.com, Jul 4, 2011

@Lyricco...@gmail.com,

Hi,以前是没办法确认服务器证书的,但是GAE某次更新后有这个功能了,把validate_certificate设成True就可以了。

http://code.google.com/appengine/docs/python/urlfetch/fetchfunction.html

validate_certificate A value of True instructs the application to send a request to the server only if the certificate is valid and signed by a trusted CA, and also includes a hostname that matches the certificate. A value of False instructs the application to perform no certificate validation. A value of None defaults to the underlying implementation of URL Fetch. The underlying implementation currently defaults to False, but will default to True in the near future.

有的网站使用的证书不是正式CA签发的,这些网站SecureGAppProxy会拒绝访问的。

Comment by Kaishek1...@gmail.com, Aug 18, 2011

就是說線路一上絕對安全?

Comment by project member i...@nleven.com, Aug 21, 2011

@Kaishek1...@gmail.com

我是这么认为的~而且设计目标是这个。

Comment by Cickumqt, Feb 10, 2012

我觉得很不错了.想咨询一下开发者我正在学习密码学,求指导书籍

Comment by moonlightedge1, Feb 23, 2012

Diffie-Hellman密码体制是用并非基于密钥的利用质数的幂的同余约定密钥的.那么一开始的密码是为了什么呢?为了验证?传递了MD5?或是将约定密钥所需的传递信息用AES加密以防止中间人攻击?

Comment by project member i...@nleven.com, Feb 27, 2012

@moonlightedge1

主要是为了身份验证,防止中间人攻击。

详细的可以看那篇论文里的The PAK Protocol部分。 实现起来还蛮简单的,先把用户密码映射成有限群的元素w,然后密钥协商时,发送gx·w。所以如果不知道w是无法知道公钥g^x 的。

@Cickumqt

我也只是会用一些,太理论的我也不懂。指导书籍的话,Bruce Schneier的Applied Cryptography还是不错的。

Comment by moonlightedge1, Mar 1, 2012

先把用户密码映射成有限群的元素w>>这里的有限群是指椭圆曲线群还是指其他的什么?抱歉啊,我只是个机电专业的工科生,数论方面,近世代数方面,都比较模糊.只是看过一些数论的欧拉定理什么的,对椭圆曲线群不是很懂.

另有一个不解之处,这个项目以及一个相近项目(http://code.google.com/p/encrypted-gappproxy/ ) 在AES时明明为了16字节倍数填充补足了字节,为何通过这个应用下载的文件还是字节数一致呢?各种HASH函数本来就是填充的,如果填充方式一致,本来就该相等,倒是可以理解,就是字节数不理解

Comment by 7896...@gmail.com, Mar 1, 2012

最近感觉gfw对g.cn的ip干扰很严重,能否增加dns mapping功能

Comment by moonlightedge1, Mar 2, 2012

感觉似乎那种padding方案有很小的概率会出现错误,也许以下的方案是不错的选择 def encrypted (string):

pad = (16-(len(string)+1)%16)%16
crypt_obj=AES.new(encrypt_key, AES.MODE_CBC) encrypt_string = crypt_obj.encrypt(string+'4'pad+chr(pad))
return encrypt_string

def decrypted (string):

crypt_obj=AES.new(encrypt_key, AES.MODE_CBC) decrypt_string = crypt_obj.decrypt(string) pad = ord(decrypt_string[-1])

return decrypt_string[0:-1-pad]

Sign in to add a comment
Powered by Google Project Hosting