当前位置: 首页 > 知识库问答 >
问题:

JSF2.3中使用CDI的会话固定和会话范围bean

秦阳旭
2023-03-14

在用户登录时续订HTTP会话是常见的最佳做法。这将强制使用新的会话ID,从而避免会话固定漏洞。

当涉及@SessionScoped bean时,是否有使用CDI实现此功能的首选模式?困难在于,通过使当前HTTP会话无效,您将在下一个请求中获得不同的会话范围bean,但直到下一个请求。

例如,假设一个用于存储用户登录信息的会话bean:

@Named("sessionbean")
@SessionScoped
public class SessionBean implements Serializable {
    private int userId;
    private String username;
    private List<String> privileges;

    // Accessors omitted 
}

和另一个用于管理登录的bean:

@Named("loginbean")
@ViewScoped
public class LoginBean implements Serializable {

    private String username;
    private String password;
    @Inject private SessionBean session;
    @Inject private SessionManager sessionManager;
    @Inject private PrivilegeManager privilegeManager;      

    public String doLogin() {
        String destinationUrl;

        if (validate(username, password)) {
            FacesContext context = FacesContext.getCurrentInstance();

            // force renewal of HTTP session
            context.getExternalContext().invalidateSession();

            // retrieve new session bean  ** No longer works with CDI **
            Application app = context.getApplication();
            session = app.evaluateExpressionGet(context, "#{sessionbean}", SessionBean.class);

            session.setUsername(username);
            session.setSessionId(sessionManager.createNewSession(username));
            session.setPrivileges(privilegeManager.getPrivileges(username));

            destinationUrl = createLandingPageUrl();

        } else {
            destinationUrl = createFailureUrl("Unknown user or password");
        }

        return destinationUrl;
    }
}

对于托管Bean,这将检索一个新的SessionBean,但是对于CDI,上面的代码将只返回相同的SessionBean。有什么建议或聪明的想法吗?

共有2个答案

孟昊空
2023-03-14

因为我不是安全专家,所以我将把这个答案限制为仅与CDI有关。我也不知道所要求的东西是否是个好主意。不管怎样,我认为你会按照你的要求去做。

用纯粹的CDI术语表达,该问题可以重新表述为:

我知道有一个对象来自特定的上下文。我知道这个上下文产生的对象的生命周期。如何正确地告诉上下文使其正在管理的当前对象无效,并加载或创建一个新对象?

一般做法是:

  • @Injecta提供程序

因此,doLogin方法的相关部分可能如下所示(未经测试):

final AlterableContext context = (AlterableContext) this.beanManager.getContext(SessionScoped.class);
assert context != null;

final Bean<?> bean = beanManager.resolve(beanManager.getBeans(SessionBean.class));
assert bean != null;

context.destroy(bean);

final SessionBean newSessionBean = this.sessionBeanProvider.get();
assert newSessionBean != null;

我认为这应该行得通。

柳英豪
2023-03-14

困难在于,通过使当前HTTP会话无效,您将在下一个请求中获得不同的会话范围bean,但直到下一个请求。

然后,不要使会话无效,而是更改会话ID。换句话说,不要使用HttpSession#invalidate(),而是使用HttpServletRequest#changeSessionId()(自Servlet 3.1以来新增,考虑到您使用的是JSF 2.3,毫无疑问您已经在使用它了)。

在代码中,替换

// force renewal of HTTP session object
context.getExternalContext().invalidateSession();

通过

// force renewal of HTTP session ID
((HttpServletRequest) context.getExternalContext().getRequest()).changeSessionId();

这基本上改变了JSESSIONIDcookie,而不改变Httpsession。它非常适合预防会话固定。

显式使会话无效通常仅在注销期间有用。

 类似资料:
  • HttpSession 对象必须被限定在应用(或 servlet 上下文)级别。底层的机制,如使用 cookie 建立会话,不同的上下文可以是相同,但所引用的对象,包括该对象中的属性,决不能在容器上下文之间共享。 用一个例子来说明该要求: 如果 servlet 使用 RequestDispatcher 来调用另一个 Web 应用的 servlet,任何创建的会话和被调用 servlet 所见的必须

  • 4.3. 会话固定 关于会话,需要关注的主要问题是会话标识的保密性问题。如果它是保密的,就不会存在会话劫持的风险了。通过一个合法的会话标识,一个攻击者可以非常成功地冒充成为你的某一个用户。 一个攻击者可以通过三种方法来取得合法的会话标识: l猜测 l捕获 l固定 PHP生成的是随机性很强的会话标识,所以被猜测的风险是不存在的。常见的是通过捕获网络通信数据以得到会话标识。为了避免会话标识被捕获的风险

  • CDI新手问题。简单测试场景:JSF CDI会话范围bean。 我需要一种优雅的方式来实例化一组已知的会话范围的cdibean,而不必在页面上提及它们,也不必从其他bean调用它们的方法。作为一个测试用例——一个简单的日志bean,它只记录http会话的开始和结束时间。 当然,我可以创建一个空的JSF组件,将其放在站点范围的模板中,并使其触发所需会话bean的虚拟方法,但从我的观点来看,这有点难看

  • 我有一个@SessionScoped CDIBean,用于跟踪web应用程序中的用户会话信息。有没有办法从另一个@ApplicationScoped bean中找到这个bean的所有对象?

  • 在发布这个问题之前,我谷歌了很多这个问题,并且在网络Socket@ServerEndpoint中从HttpServletRequest访问Http会话是我能找到的最好的问题/答案之一,但它似乎不能解决我的问题。 我可以通过这种方式从websocket访问HttpSession,但是我无法访问与普通HTTP请求相同的CDI会话bean实例。 我也尝试在会话中存储Weld实例,并尝试在WebSocke

  • 我有一个会话范围的CDI托管bean: 在某个流之后,我需要从会话中删除这个bean,对于该流,我使用了以下代码,如下面的答案所示: 但是,它不起作用,仍然留在会话中。 我是否遗漏了什么?