jsf-2 - 通过客户端状态保存防止 JSF2 中的 CSRF

标签 jsf-2 csrf viewstate jsf-2.2 myfaces

我正在使用 MyFaces 2.2.3 和客户端状态保存 + PrimeFaces

询问后how to prevent the re-use of a ViewState in different sessions我是told by BalusC ,我可以通过覆盖 from 渲染器来注入(inject)我自己的 CSRF token ,让值成为 CSRF token ,

我正在寻找一种不会强制我修改我的 xhtml 页面的解决方案 :)

BalusC 通过扩展 ViewHandlerWrapper 提出了一种更好的方法来防止 CSRF 攻击。 ,而且效果很好,我只需要稍微修改restoreView通过以下方式

public UIViewRoot restoreView(FacesContext context, String viewId) {
    UIViewRoot view = super.restoreView(context, viewId);
    if (getCsrfToken(context).equals(view.getAttributes().get(CSRF_TOKEN_KEY))) {
        return view;
    } else {
        HttpSession session = (HttpSession) context.getExternalContext().getSession(false);
        if (session != null) {
            session.invalidate(); //invalidate session so (my custom and unrelated) PhaseListener will notice that its a bad session now
        }
        try {
            FacesContext.getCurrentInstance().getExternalContext().redirect("CSRF detected and blocked"); //better looking user feedback
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }
}

旧解决方案

到目前为止我尝试过没有成功,

添加到 faces-config.xml
<render-kit>
    <renderer>
        <component-family>javax.faces.Form</component-family>
        <renderer-type>javax.faces.Form</renderer-type>
        <renderer-class>com.communitake.mdportal.renderers.CTFormRenderer</renderer-class>
    </renderer>
</render-kit>   

然后在 CTFormRenderer.java
@Override
public void encodeEnd(FacesContext context, UIComponent arg1) throws IOException {
    //how to set form value be a CSRF token?
}

@Override
public void decode(FacesContext context, UIComponent component) {
    HttpSession session = (HttpSession) context.getExternalContext().getSession(false);
    String token = (String) session.getAttribute(CSRFTOKEN_NAME);
    String tokenFromForm = //how to get the value stored in form value attribute because (String) component.getAttributes().get("value"); return null
    //check token against tokenFromForm...
}

我不想为每个 h:form 添加自定义组件,而是我想扩展 form渲染器,所以我的所有表单都将具有 csrf token .

最佳答案

这个<h:form>渲染器覆盖方法对 PrimeFaces 不安全 partialSubmit="true" .此外,重用其标识提交表单的隐藏字段将是特定于 JSF 实现的,因为这不是 JSF API 的一部分。

再想一想,将 CSRF token 直接存储在 JSF View 状态本身中要简单得多。您可以通过自定义 ViewHandler 来实现这一点如下在 UIViewRoot 中设置一个属性(自动保存在 JSF View 状态):

public class CsrfViewHandler extends ViewHandlerWrapper {

    private static final String CSRF_TOKEN_KEY = CsrfViewHandler.class.getName();

    private ViewHandler wrapped;

    public CsrfViewHandler(ViewHandler wrapped) {
        this.wrapped = wrapped;
    }

    @Override
    public UIViewRoot restoreView(FacesContext context, String viewId) {
        UIViewRoot view = super.restoreView(context, viewId);
        return getCsrfToken(context).equals(view.getAttributes().get(CSRF_TOKEN_KEY)) ? view : null;
    }

    @Override
    public void renderView(FacesContext context, UIViewRoot view) throws IOException, FacesException {
        view.getAttributes().put(CSRF_TOKEN_KEY, getCsrfToken(context));
        super.renderView(context, view);
    }

    private String getCsrfToken(FacesContext context) {
        String csrfToken = (String) context.getExternalContext().getSessionMap().get(CSRF_TOKEN_KEY);

        if (csrfToken == null) {
            csrfToken = UUID.randomUUID().toString();
            context.getExternalContext().getSessionMap().put(CSRF_TOKEN_KEY, csrfToken);
        }

        return csrfToken;
    }

    @Override
    public ViewHandler getWrapped() {
        return wrapped;
    }

}

请注意,当 restoreView()返回 null , JSF 会抛出 ViewExpiredException “照常”。

要让它运行,请在 faces-config.xml 中注册如下:
<application>
    <view-handler>com.example.CsrfViewHandler</view-handler>    
</application>

因为它对服务器端状态保存没有附加值(value),如果当前 JSF 应用程序配置了客户端状态保存,如果需要,您可以在 View 处理程序的构造函数中检测如下:
FacesContext context = FacesContext.getCurrentInstance();

if (!context.getApplication().getStateManager().isSavingStateInClient(context)) {
    throw new IllegalStateException("This view handler is only applicable when JSF is configured with "
        + StateManager.STATE_SAVING_METHOD_PARAM_NAME + "=" + StateManager.STATE_SAVING_METHOD_CLIENT);
}

关于jsf-2 - 通过客户端状态保存防止 JSF2 中的 CSRF,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30507389/

相关文章:

java - 通过覆盖 ResourceBundle 重用属性文件中的键值

django:升级到 1.2 CSRF 后,虽然我没有启用 CSRF 保护,但会引发 403

asp.net - 为什么 DropDownList.SelectedValue 依赖于 viewstate?

c# - ViewState 仅在 Safari 中无效

java - 在 Java 应用程序中找到了类,但在 JSF 中没有找到?

java - JSF 中使用漂亮的欢迎页面

ajax - Django 1.9 AJAX 表单 CSRF token 403 错误 - "CSRF cookie not set"

python - Django csrf_token 未实现隐藏字段

javascript - 从回发响应更新 View 状态

java - 为什么 Glassfish 拒绝我在 Netbeans 7.3.1 下的所有身份验证工作?