将身份验证凭据作为GET参数发送时存在安全问题


Security issue when sending auth credentials as GET parameters

我通过向用户发送带有登录名/密码的直接链接来对用户进行身份验证,这样他就可以按下链接并通过URL中的get参数登录。

示例:http://example.com/index.php?r=site/login&p=password&email=igor.savinkin%40gexample.com

我的问题是关于安全问题。如果有人将网络嗅探器与互联网线路并行,从而复制凭据或访问缓存数据,该怎么办?我使用Yii框架,所以它对安全维护非常好;登录最初是通过POST参数完成的。我听说的POST与GET请求:

  • 在处理敏感数据时使用POST
  • 对安全操作使用GET,对不安全操作使用POST

不要在url中使用密码!

这真的很糟糕,任何嗅探(或监控)流量的人都可以获得密码。此外,任何有权访问电子邮件收件箱的人都会知道密码。

另一种选择是使用只有效一次的令牌。在发送邮件之前,您会生成一个唯一的令牌,并将其与用户的电子邮件地址链接。

你会有一个类似的url

http://hostname.com/index.php?r=site/login&token=544a0a62e280e3.83969641&email=igor.savinkin%40gmail.com

当用户使用你的url时

  • 检查提供的令牌是否与数据库中的givn地址链接
  • 丢弃令牌以确保没有人会使用相同的url登录

之后最好的办法是不提供任何使用给定url登录的方式。因为我认为你会通过电子邮件发送链接,而SMTP协议是不安全的。

此类URL可能被广泛缓存、保存或以其他方式可见:

  • 如果通过电子邮件发送,密码将以明文形式通过网络发送,因为电子邮件实际上总是处于明文状态
  • 邮件提供商的硬盘上会有一份副本
  • URL将存储在用户硬盘上的某个位置,很可能是明文的
  • 浏览器将记住历史记录中的URL
  • 如果运气不好,URL甚至可能泄露到谷歌的索引中

URL有很多机会获得大量曝光,这是它的主要问题。POST请求从定义上来说是短暂的,并不是由兼容的客户端永久存储的(你甚至不能通过电子邮件等方式向某人"发送"POST请求)。

像这样传递用户凭据是非常不安全的。如果你想要这个,那么我至少会加密用户名和密码,稍后再解密

我前段时间写了一堂小课,可以帮你做这件事。你可以在github上找到它。https://github.com/peterurk/cipher

将密码作为GET参数发送会增加恶意个人可能了解该密码的位置数:

  • 你提到的网络嗅探
  • 如果攻击者进入该用户的电子邮件,他们还会找到您服务的登录详细信息
  • 如果用户在代理后面,该代理的管理员可以在其日志中看到密码
  • 类似地,任何可以在您的网站上查看访问日志的人也可能获取密码

另一种选择是发送一个有时间限制的登录令牌。您不发送用户名和密码,而是向用户发送一个带有令牌的链接,该链接在短时间后过期。

最简单的形式是,该令牌是用户凭据、所需到期时间和仅为服务器所知的秘密令牌的哈希组合(最后一位防止攻击者重新生成寿命更长的令牌)。由于您可能使用单向哈希函数,因此当用户单击链接时无法对其进行解码,因此您还可以发送id和到期时间,以帮助服务器在用户单击链接后确定有效性。

因此,在您当前为用户生成电子邮件的地方,您可以调用下面这样的函数。

// When should this token expire? $expiryTimestamp = strtotime("+3 hours"); $mySecret = "some secret phrase here"; $token = md5($userId . $expiryTimestamp . $mySecret); // Put them together to build the link $params = array( 'token' => $token, 'userId' => $userId, 'expiry' => $expiryTimestamp ); $link = '/site/login?' . http_build_query($params);

然后,当用户单击链接时,您可以在服务器上检查它,如下所示。 if ($_REQUEST['token] == md5($_REQUEST['userId'] . $_REQUEST['expiry'] . $mySecret)) { // User is good to go and within the timeframe, let them in } else { // Something not matching here, don't log them in }

您应该使用有时间限制的令牌,而不是在URL中发送密码。例如

www.example.com?token=1234567890

其中CCD_ 6是加密安全的伪随机数或序列。

点击链接后,用户将被要求设置自己的密码,然后才能使用系统的其余部分。一旦用户设置了自己的密码,令牌就应该过期,因此不能再次使用。

由于令牌有时间限制,敏感数据存储在URL写入的任何位置(例如,如果电子邮件在用户邮箱中未打开)的风险较小。GET请求具有以下相关风险,因为参数在URL中传输,而POST请求在请求体中具有这些风险:

  • 默认情况下,URL会记录在用户浏览器和系统之间的服务器、设备和代理上
  • URL在浏览器历史记录中可用
  • URL可以通过referer HTTP头泄漏

使用HTTPS可以降低风险。即使GET参数仍然可见,但无法解密SSL连接的任何东西都看不到它们(例如,ISP、MITM和非公司代理服务器看不到GET参数)。

以这种方式发送帐户设置链接并没有太大的风险,越来越多的电子邮件提供商在相互传输时对其电子邮件进行加密,许多提供商允许通过SSL检索电子邮件。正是这段旅程,从用户的邮箱到他们的设备或浏览器,带来了最大的MITM攻击风险(想想连接wifi的咖啡店里的黑客)。然而,对于高安全性系统(如银行),这种类型的账户激活电子邮件是不可接受的。