如何防止 JSF 2.2 接受来自不同会话的 ViewState?

How can I prevent JSF 2.2 from accepting a ViewState from a different session?

我正在使用 JSF 2.2 (Glassfish 4.1)。我们的 webapp 依赖 Primefaces 6.0 和 Omnifaces 2.6.9。 JSF 状态存储在服务器上。 例如,我有这种形式,其中 userModel 是一个 javax.faces.view.ViewScoped bean。

<h:form id="user">
    <p:inputText id="name" value="#{userModel.name}"/>
    <p:inputText id="pass" value="#{userModel.pass}"/>
    <p:commandButton id="create" value="#{msgs.lbl_add}" action="#{userModel.addUser()}"/>
</h:form>

一家公司扫描了我们的网络应用程序是否存在安全问题,并声称它存在 CSRF 漏洞。攻击者可以像这样向我们的应用程序用户之一提供虚构的表单以执行不需要的操作。

<!DOCTYPE html>
<html>
    <body>
        <form action="http://appserver:8080/myapp/users.jsf" method="POST">
            <input type="hidden" name="javax.faces.source" value="user:create"/>
            <input type="hidden" name="user:create" value="user:create"/>
            <input type="hidden" name="user" value="user"/>
            <div>
                <input type="text" name="user:name" value="FAKEUSER"/>
                <input type="text" name="user:pass" value="FAKEPASSWORD"/>
                <input type="text" name="javax.faces.ViewState" value="1185295409278172717:-3206872038807094332"/>
            </div>
            <input type="submit" name="submit" value="Create User"/>
        </form>
    </body>
</html>

我读到 ViewState 是防止 CSRF 的 JSF 方式。但是在我们的 Web 应用程序中可能会出现以下情况(如果重要,则使用 HTTP 协议)。

  1. 攻击者访问我们的应用程序登录页面以在其页面源中找到有效的 ViewState。
  2. 攻击者使用他的 ViewState 准备 HTML 上述文件并发送给受害者。
  3. 受害者在浏览器中打开 HTML 文件并提交(来自本地文件系统,例如 file:///C:/... 或由本地网络服务器托管)
  4. 已创建用户。

结论是我们这个场景下的webapp/JSF没有检查接收到的ViewState是否属于JSESSIONID标识的session。

这不是漏洞吗?我怎样才能避免这种情况?

在我们的案例中,这是我们应用程序中的一个错误。我们还使用了 Deltaspike 库并在我们的应用程序中注册了 'Handle-All-Exceptions' class。

import org.apache.deltaspike.core.api.exception.control.ExceptionHandler;
import org.apache.deltaspike.core.api.exception.control.Handles;
import org.apache.deltaspike.core.api.exception.control.event.ExceptionEvent;

@ExceptionHandler
public class ExceptionDispatcher {

    public void processException(@Handles ExceptionEvent<Throwable> evt) {
        // Handle exception by just logging
    }
}

如果 JSF 发现无效的 ViewState,这也会“处理”javax.faces.application.ViewExpiredException。结果请求正常处理。

更好的实现将重定向到错误页面并使会话无效。