【问题标题】:Play framework security issue regarding cookies and sessions播放有关 cookie 和会话的框架安全问题
【发布时间】:2019-05-16 17:19:27
【问题描述】:

对于我的应用,我正在实现与 zentask 中显示的相同的安全性。

public class Secured extends Authenticator {

@Override
public String getUsername(Context ctx) {
    return ctx.session().get("email");

}

@Override
public Result onUnauthorized(Context ctx) {
    ctx.flash().put("error", "please login to proceed");
    return redirect(routes.Application.index());
}

}

当用户通过身份验证时isuser session().put("email", email);

我有两个问题。第一:当用户离开应用程序而不使用注销时,如何使会话无效?第二个更严重的是我使用firefox插件cookies manager+检查了cookie,我可以复制一个cookie然后粘贴它,因此我可以访问方法而无需先登录,基本上我可以窃取会话

【问题讨论】:

    标签: security playframework-2.0


    【解决方案1】:

    Play 框架使用无状态会话。服务器端没有存储状态,而是所有状态都存储在会话 cookie 中。为了验证会话,Play 使用密钥对会话进行签名,并在带有会话 cookie 的请求到达时验证签名。如果用户要篡改会话数据,例如,如果他们将会话中的电子邮件地址更改为其他人的电子邮件地址,则签名将不匹配,因此 Play 将拒绝会话 cookie。

    是的,您可以复制 cookie 并稍后使用。但是您不能更改 cookie。这意味着您可以“偷”的唯一 cookie 是您自己的,但从您自己那里窃取并不是真正的窃取。所以不,你不能窃取会话,除非通过利用 XSS 等其他漏洞,但会话令牌也容易受到攻击。

    默认情况下,Play 配置为创建“会话”cookie,即在您关闭浏览器时过期的 cookie。因此,如果用户退出浏览器,浏览器将删除所有会话 cookie,用户将不再登录。会话令牌也是如此。

    有一个注意事项需要注意,那就是会话令牌也会在服务器上过期,因为服务器持有状态。无状态签名会话,例如 Play 中使用的会话,则不会。但是,您可以自己实现过期机制,方法是在创建会话时将时间戳存储在会话中,并验证该时间戳不早于 getUsername() 方法中配置的过期时间。由于时间戳存储在签名的会话中,不改变签名就无法篡改时间戳,所以这种简单的机制是相当安全的。更高级的解决方案可能是实现一个过滤器,在每次请求进入时更新该时间戳,以便过期可以基于上次访问,而不是用户登录时间。

    【讨论】:

    • “但会话令牌也容易受到此攻击。”在普通服务器上,当我注销时,任何窃取了我的 cookie 的人都无法再使用它。是的,cookie 不应该被盗,但需要纵深防御。
    • 这个。应该在 Play 中实现。
    【解决方案2】:

    您的假设是绝对正确的,您不能按照 Zentask 示例使服务器上的会话无效。尽管会话 cookie 是使用配置文件中的私钥签名的,但相同的值会产生相同的签名 cookie。正如您已经知道的那样,如果有人从用户那里窃取 cookie,用户和您(服务器)都无法阻止小偷“登录”用户的帐户。

    现在基本上有两种选择:

    1. 将您已有的关于用户的易失性信息存储在只有您和用户知道并且不时更改的 cookie 中。一个例子是密码哈希的一部分。一旦用户更改密码,该信息将不再有效,所有旧会话 cookie 均无效。这种方法的一个缺点:如果用户不更改存储的信息,cookie 将在很长一段时间内有效,甚至可能永远有效。
    2. 创建服务器端会话管理。为此,您必须有一个数据库、一个键值缓存或类似的东西。在那里,您存储了一个随机生成的(加密安全的)会话密钥、用户名/ID 以及会话将自动失效的日期。您还可以存储 IP 地址以提高防止 cookie 窃取的安全性。然后必须将会话密钥写入 cookie。当用户单击注销按钮时,您会使当前会话(或者该用户的所有会话)无效。

    【讨论】:

    • 第二种选择被认为是有效的,它基本上意味着如果使用@Secured注释装饰一个类,每个reqauest都必须针对db进行验证???
    • 基本上,是的。但它也允许跟踪未登录的用户。如果客人注册后可以做您想要永久保存的事情,这很有用。
    【解决方案3】:

    将用户 ID 简单地放在 cookie 中根本不安全。正如您所指出的,任何人都可以发明 cookie 值。

    会话: 相反,您需要在 cookie 中放入任意(例如随机)值,然后在服务器上在映射表中查找用户的身份。该任意值必须经常更改,因此您的登录会话通常会持续 30 分钟。每次登录都会提供一个新的任意值,该值称为会话 ID。

    失效:在一段时间(例如 30 分钟)没有任何请求的情况下,通过从查找表(在服务器端)中删除该条目来使会话失效。任何具有不在表中的会话 id 的请求都将被视为未经身份验证的请求,并提示您再次登录。用户忘记注销也没关系。

    黑客攻击:因为该值是任意的,所以黑客无法提前知道未来的会话 id 是什么。您仍然容易受到会话窃取的影响,但难度要大得多:黑客必须仅在使用会话 ID 时找到它,然后才能使用它一段时间。您可以采取一些措施来防止这种情况发生,例如只允许对特定会话的请求来自特定 IP 地址。您还可以快速循环会话 ID,甚至是每个请求,但也有不利的一面。通常,为每次登录提供唯一的会话 ID,尤其是通过 HTTPS 完成时,足以满足大多数身份验证需求。

    持久性:如果在任何给定会话期间(例如 30 分钟)内并发用户的数量很少,那么您不一定需要将其放入数据库中。在内存中维护它的开销很低,但缺点是如果循环服务器,所有用户都需要重新登录。如果确实将会话 ID 放入数据库中,则需要确保在服务器启动时它可以清除所有旧会话。

    用户信息: 将用户的电子邮件地址放入 cookie 中仍然有价值,但仅用作“默认”用户 ID 以登录身份。这仅应被视为对用户的一种方便,而不是表明用户已通过身份验证。

    【讨论】:

    • 我在 cookie 上放置了以下信息 session().put("email", email); session().put("时间戳", new Date().toString()); session().put("ip", request().remoteAddress());而不是比较它。够好吗?
    • 取决于您是否希望黑客进入。任何人都可以生成电子邮件、时间戳和 IP 地址,如果您只需要验证这些,那么您就被黑客入侵了。密钥是一个随机生成的数字,它在服务器上映射到经过身份验证的用户(和 IP 地址)。没有实际身份验证,黑客无法在您的(服务器端)表中获取条目。
    • 所以生成和 UUID ,将其分配给 cookie 并在 30 分钟后使 UUID 无效会带来相对良好的保护?
    • 播放会话 cookie 已签名。因此,它不仅仅是 cookie 中的用户 id,它是一个带有签名的用户 id,用于验证将用户 id 放入 cookie 中的是 Play。
    • 我不知道 Play 是如何签署 cookie 的。但是一种相对安全的方法是生成cookie = "Sign|Data" = "Encrypt(Data, Salt)|Data",其中Encrypt是单向函数。也就是说,如果您知道 DataSalt,则始终可以生成 Sign。但是你不能使用Sign 得到DataSalt。通常md5, sha1, sha256 是很好的加密函数(查找 HMAC),Salt 保存在服务器上(没有客户端知道它)。所以服务器发送“Sign|Data”,客户端发回“Sign|Data”。在服务器上验证 Encrypt(Data, Salt) == Sign。如果DataSign 被篡改,则验证失败
    【解决方案4】:
    • 我建议使用一个模块来为您生成会话 ID。 在这个模块中,您可以使用一些方法,如 createSessionId() 或其他东西。 生成会话ID的逻辑你保存在这个方法中。

    • 我会将会话 ID 创建为 (userId + providerId(Facebook/Google-in case of OAuth/UsernamePassword/Any Provider) + 当前时间戳 + UUID) 的组合,并且在创建此会话 ID 后,我将加密它与一些算法。这会给我会话 ID

    • 这样做的好处是:

      • 虽然生成会话 ID 需要时间,但没有人能理解它。
      • 另一个优势是,您可以更改您的加密逻辑/策略 随时在 createSessionId() 方法中创建会话 ID。

    • Playframework 中 session 的另一个问题是 session 没有过期:
      • 为了处理这个问题,一旦用户登录,我们可以在会话中存储时间戳,即除了 cookie 之外什么都没有(通过加密可能是?)
      • 现在为每个请求检查会话中的时间戳。如果时间戳大于 30 分钟,则使会话无效。如果时间戳不大于 30 分钟,则将会话中的时间戳更新为当前时间戳

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-01-15
      • 1970-01-01
      • 2011-05-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多