nao*_*oru 13 security playframework-2.0
对于我的应用程序,我正在实现与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());
}
}
Run Code Online (Sandbox Code Playgroud)
当用户通过isuser身份验证时session().put("email", email);
我有两个问题.第一:当用户在不使用注销的情况下离开应用程序时,如何使会话无效?第二个更严重的是我使用firefox插件检查了cookie cookies manager+并且我可以复制一个cookie然后粘贴它因此我可以访问方法而无需首先登录,基本上我可以窃取会话
Jam*_*per 39
Play Framework使用无状态会话.服务器端没有存储状态,而是所有状态都存储在会话cookie中.为了验证会话,Play使用密钥对会话进行签名,并在有会话cookie的请求到达时验证签名.如果用户要篡改会话数据,例如,如果他们将会话中的电子邮件地址更改为其他人的电子邮件地址,则签名将不匹配,因此Play将拒绝会话cookie.
是的,您可以复制cookie并在以后使用它.但是你不能改变cookie.这意味着你可以"窃取"的唯一cookie就是你自己的,但偷你自己并不是真正的偷窃.所以不,你不能窃取会话,除非通过利用其他漏洞,如XSS,但会话令牌也容易受到攻击.
默认情况下,Play配置为创建"会话"cookie,即在您关闭浏览器时过期的cookie.因此,如果用户退出浏览器,浏览器将删除所有会话cookie,用户将不再登录.会话令牌也是如此.
有一个需要注意的问题,那就是会话令牌也会在服务器上到期,因为服务器保持状态.无状态签名会话(例如Play中使用的会话)不会.但是,您可以自己实现过期机制,方法是在创建会话时在会话中存储时间戳,并验证该时间戳不早于getUsername()方法中已配置的有效期.由于时间戳存储在已签名的会话中,因此在不更改签名的情况下不能篡改时间戳,因此这种简单的机制非常安全.更高级的解决方案可能是实现每次请求进入时更新该时间戳的过滤器,以便过期可以基于上次访问,而不是用户登录时.
您的假设绝对正确,您不能在Zentask示例之后使服务器上的会话无效.虽然会话cookie使用配置文件中的私钥进行签名,但相同的未签名cookie值会生成签名的相同cookie.正如您已经想到的那样,如果有人从用户那里窃取cookie,用户和您(服务器)都不能阻止小偷"登录"到用户的帐户.
现在基本上有两种选择:
将用户 ID 简单地放在 cookie 中根本不安全。正如您所指出的,任何人都可以发明 cookie 值。
会话:相反,您需要在 cookie 中放置一个任意(例如随机)值,然后在服务器上在映射表中查找用户的身份。该任意值必须经常更改,因此您的登录会话通常持续 30 分钟。每次登录都会提供一个新的任意值,该值称为会话 ID。
无效:在一段时间没有任何请求(例如 30 分钟)后,通过从查找表(在服务器端)中删除该条目来使会话无效。任何具有不在表中的会话 ID 的请求都被视为未经身份验证的请求,您将再次提示登录。用户是否忘记注销也没有关系。
Hacking:由于该值是任意的,因此黑客无法提前知道未来的 session id 是什么。您仍然容易受到会话窃取的影响,但难度要大得多:黑客只需在使用时找到会话 ID,然后只能在特定时间内使用它。您可以采取一些措施来防止这种情况发生,例如只允许来自特定 IP 地址的特定会话请求。您还可以快速循环会话 ID,即使是每个请求,但也有负面影响。一般而言,为每次登录提供唯一的会话 ID,尤其是在通过 HTTPS 完成时,足以满足大多数身份验证需求。
持久性:如果在任何给定的会话期间(例如 30 分钟)并发用户的数量很少,那么您不一定需要将其放入数据库中。在内存中维护它的开销很低,但缺点是如果您循环服务器,所有用户都需要再次登录。如果确实将会话 ID 放入数据库中,则需要确保服务器启动时可以清除所有旧会话。
用户信息:将用户的电子邮件地址放在 cookie 中仍然有价值,但仅用作登录的“默认”用户 ID。这应该仅被视为对用户的一种方便,而不应视为用户已通过身份验证的指示。
我建议使用一个模块来为您生成会话 ID。在此模块中,您可以使用诸如 createSessionId() 之类的方法。生成会话Id的逻辑保留在这个方法中。
我将创建会话 ID 作为(userId + providerId(Facebook/Google - 在 OAuth/UsernamePassword/Any Provider 的情况下)+ 当前时间戳 + UUID)的组合,并在创建此会话 ID 后,我将使用某种算法对其进行加密。这会给我会话 ID
这样做的优点是: