Session Fixation and Session Scoped Beans in JSF 2.3 with CDI

…衆ロ難τιáo~ 提交于 2020-01-23 07:34:11

问题


It's a common best practice to renew the HTTP session when logging in a user. This will force a new session ID, avoiding session fixation vulnerabilities.

Is there a preferred pattern for implementing this with CDI when @SessionScoped beans are involved? The difficulty is that by invalidating the current HTTP session, you'll then get a different session-scoped bean with the next request, but not until the next request.

For example, assume a session bean for storing user login information:

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

    // Accessors omitted 
}

And another bean for managing the login:

@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;
    }
}

With Managed Beans this would retrieve a new SessionBean, but with CDI, the code above would just return the same SessionBean. Any recommendations or clever ideas?


回答1:


The difficulty is that by invalidating the current HTTP session, you'll then get a different session-scoped bean with the next request, but not until the next request.

Then don't invalidate the session, but change the session ID. In other words, don't use HttpSession#invalidate(), but use HttpServletRequest#changeSessionId() (new since Servlet 3.1, which you should undoubtedly already be using given that you're using JSF 2.3).

In code, replace

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

by

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

This basically changes the JSESSIONID cookie without changing the HttpSession. It's perfect for session fixation prevention.

Explicitly invalidating the session is usually only useful during logout.




回答2:


I'm going to restrict this answer to be solely about CDI since I am not a security expert. I also don't know whether the general thing being asked for is a good idea or not. Regardless, here is how I think you would do what you're asking for.

Expressed in purely CDI terms, the question can be rephrased like:

I have an object that I know came from a particular Context. I know the lifecycle of objects produced by this Context. How can I properly tell the Context to invalidate the current object that it is managing, and load or create a new one?

The general approach is going to be:

  • @Inject a Provider<SessionBean> instead of SessionBean directly (this will let you ask CDI for the "new" object properly)
  • @Inject a BeanManager (so you can get the right Context that manages SessionScoped objects)
  • ask the BeanManager to give you the AlterableContext corresponding to the SessionScoped annotation
  • tell the AlterableContext to destroy the current bean's contextual instance
  • call Provider.get() to cause a new one to be created

So the relevant parts of your doLogin method might look like this (untested):

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;

I think that should work.



来源:https://stackoverflow.com/questions/59667297/session-fixation-and-session-scoped-beans-in-jsf-2-3-with-cdi

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!