2

在安全性和身份验证以及大多数其他客户端-服务器通信方面,我还是个新手。我想做的很简单,如果可能的话,避免引入 3rd 方框架和类来完成这项工作。(我将 Google App Engine 与 Python 一起使用)

用户通过移动应用程序 (iOS) 登录一次服务。登录后,用户将发出许多请求,以获取消息、朋友、状态等信息。因此,每次应用程序与服务器通信时,我们不会发送该用户的电子邮件和密码进行身份验证,而是发送一个会话 ID。到目前为止,这只是我对系统的理解。

我想出了一个非常简单的方法,在我看来它会很好用,但是由于缺乏经验,我可能看不到很多东西。这样做会有什么问题:

  1. 在设备上,用户输入电子邮件/密码,凭据被发送到服务器并进行验证,一旦通过验证,就会生成一个随机数。这个随机数以 IntegerProperty 的形式存储在 User 模型上,名称为 session_number。

  2. session_number 被发送到用户的设备并被保存。现在,只要用户连接到服务器进行请求,session_number用户的整数 ID 号就会发送到服务器。我们得到该 userId 的 User 实体,现在我们比较 user.session_number == incoming_session_number 的值。如果他们匹配,我们很好,否则错误。

  3. 如果用户注销,我们会从数据存储中清除 session_number。

这里唯一的另一个问题是当用户从多个设备登录时如何处理?每个设备都应该存储自己的 session_number 吗?

4

2 回答 2

4

除了我对重新发明轮子的评论之外;我看到这个理论的主要问题是它完全受到重放攻击。

例如,给定一个 ID 为 99 且会话 ID/cookie 为“MONKEY-1”的用户——如果用户想要发送一个请求,他必须以某种方式使用“from, 99, MONKEY-1”对其进行签名。让我们假设它是这样的:

{ FROM: 99, TO: SERVER, COMMAND: GET-NEW-MAIL, SESSION: MONKEY-1 }

太好了,如果 MONKEY-1 是秘密的话。(无论你喜欢什么格式;它也可能是一个 4KiB 的二进制线路噪声块。)

然而,现在我们有一个窃听者,他看到了那个数据包经过……她现在发送了她自己的数据包。也许她很聪明,并且欺骗了真实用户 99 正在使用的源 IP 地址——即使窃听者听不到您的回复,她仍然可以向您发送数据包。也许那个看起来像这样:

 { FROM: 99, TO: SERVER, COMMAND: DELETE-ALL-MAIL, SESSION: MONKEY-1 }

有很多方法可以防止这种事情,它们的效果各不相同。在 SSL 中包装连接有助于使这变得极其困难(但绝不是不可能);这是很多网站都依赖的解决方案。一种更好的方法是使用双向通信以一种入侵者无法获取秘密数据的方式对事物进行签名,使用散列或公钥/私钥加密。例如,假设我们有这样的交换:

{ FROM: SERVER, TO: ??, COMMAND: PLEASE-LOGIN, NONCE: PIGEON }

{ FROM: BILL, TO: SERVER, COMMAND: LOGIN, AUTH: hash ( hash ( password ) . "PIGEON" ) }

......其中hash()表示,比如说,一个 SHA-256 总和,并且. "PIGEON"是指连接;

{ FROM: SERVER, TO: BILL, COMMAND: LOGIN-OK, SESSION: hash ( password ) ^ "MONKEY-1" }

… where^指的是一些操作,比如按位异或,也许。然后,随后,Bill 发送如下请求:

 { FROM: BILL, TO: SERVER, COMMAND: GET-NEW-MAIL, NONCE: "ARMADILLO", 
   AUTH: hash ( "GET-NEW-MAIL" . #\Newline . "ARMADILLO" . #\Newline . "MONKEY-1" ) }

现在,MONKEY-1 从未在任何时候畅通无阻;并且,在每个请求上给出的 AUTH 密钥与所使用的动词或命令以及一个随机数相关联,该随机数应该随每个请求而变化,服务器可以轻松验证其完整性,但窃听者无法再次重播相同的消息,或更改动词和做一些不同的事情。

解释密码问题:

我有一个数据库表,它包含

User: BILL, Password: hash(DOLPHIN)

如果,在网上,我收到

{ FROM: BILL, PASSWORD: hash(DOLPHIN), COMMAND: GET-ALL-MAIL }

......那么窃听者不太可能(但相当合理)知道密码是 DOLPHIN,但是,她不需要知道或关心:

 { FROM: BILL, PASSWORD: hash(DOLPHIN), COMMAND: DELETE-ALL-MAIL }

你提到加盐密码......你会怎么做?

 User: BILL, PASSWORD: hash( SALT . DOLPHIN )

除非您将 SALT 和 DOLPHIN 分开存放,否则您很难从 hash ( SALT . DOLPHIN )hash (DOLPHIN). 因此,要么用户必须向hash ( SALT . DOLPHIN )您提交(在客户端放置一个静态 SALT),要么您必须再次存储明文密码。

解决方法可能是做类似的事情

 Database: ( BILL => hash ( SALT . DOLPHIN ) )

 Server sends: ( NONCE )

 Client sends: ( BILL => hash ( NONCE . hash ( SALT . DOLPHIN ) ) )
于 2012-08-30T18:57:11.760 回答
4

这种安全模型的一个重要方面是您如何存储密码(或者更确切地说,您如何不存储密码)。这并不像看起来那么简单,因为如果操作不当会有很多危险。

本文涵盖了基础知识,并提供了一个很好的大纲来说明要研究的内容。它讨论了散列、彩虹表、加盐和慢散列函数。

由于您使用的是 App Engine,因此您需要研究 Python 中的加密库。hashlibhmac是标准库的一部分,但不幸的是 Python 的标准库没有任何慢散列函数的实现,如bcryptor pbkdf2

因此,我建议使用passlib密码散列。我自己将它用于一个项目(正在进行中)。具体来说,我决定使用sha512_crypt- 你可以看看这里的代码(欢迎任何批评)。

显然,还有一个由斯坦福构建的 PBKDF2的JavaScript 实现。

于 2012-08-30T19:02:47.593 回答