在其他会话中重用ViewState值(CSRF)

时间:2015-05-21 11:57:47

标签: jsf-2 viewstate jsf-2.2 myfaces

我正在使用* myfaces-api-2.2.3, javax.faces.STATE_SAVING_METHOD 设置为客户端

我有以下情况,

1)用户X登录系统并添加用户XXX(使用jsf f:ajax操作),同时检查chrome dev工具,您可以看到与 ViewState 值。

2)复制 ViewState 值(来自chrome dev工具 - >网络标签) - >将它放入带有表单的html文件中(模仿我的原始添加用户X

3)从用户X会话退出(会话在此过程中失效)

4)以用户Y登录 - >在浏览器中打开 html文件并点击表单的提交按钮 - >您会注意到添加了用户XXX(尽管表单中使用的 ViewState 值属于其他用户(用户X)。

我认为 ViewState 值不能以这种方式使用,我认为它应该阻止这种动作,为什么可以使用一个 ViewState 在一个拥有自己的 ViewState 值的全新会话中的值,以及如何确保用户无法重用 ViewState

请参阅我的其他问题并BalusC回答:Prevent CSRF in JSF2 with client side state saving

2 个答案:

答案 0 :(得分:6)

使用客户端状态保存时,这是指定/预期的行为。 JSF视图状态不保存在会话中,而是保存在客户端HTML表单中的隐藏输入字段中。仅当使用服务器端状态保存时,JSF视图状态将在用户的HTTP会话中不存在时失效。

我没有在MyFaces中看到any way以使客户端保存的状态无效,以防它与"错误"重新关联。回发期间的会话。 org.apache.myfaces.CLIENT_VIEW_STATE_TIMEOUT上下文参数接近。您可以将其设置为与会话超时相同的时间。

CSRF攻击场景有点夸大了IMO。首先,攻击者需要能够获得客户端状态。最简单的方法是XSS漏洞。只有,JSF在任何地方都有XSS攻击防范(如果你小心escape="false")。最难的方法是掌握整个HTML输出。这可以通过中间人攻击来完成。只是,这也为攻击者提供了整个会话cookie,因此在这种情况下整个CSRF攻击场景是不必要的过于复杂的#34;对于攻击者来说,攻击者可以简单地劫持会话。最好的保护措施就是使用HTTPS而不是HTTP。

即使这样,如果攻击者以某种方式掌握了客户端视图状态,攻击者也无法使用它做很多危险事情,而无需解密,反序列化和操纵它。 MyFaces默认加密它很长时间(早在2.0.x之前)。攻击者无法在没有正确密钥的情况下对其进行解密,因此也无法对其进行操作。这种客户端视图状态加密已成为JSF 2.2规范的必需部分(因此Mojarra也这样做)。默认情况下,当您重新启动应用程序服务器时,此键会被重置(因此所有客户端状态也将失效)。如有必要,您可以通过web.xml中的以下环境条目指定固定密钥:

<env-entry>
    <env-entry-name>jsf.ClientSideSecretKey</env-entry-name>
    <env-entry-type>java.lang.String</env-entry-type>
    <env-entry-value>[AES key in Base64 format]</env-entry-value>
</env-entry>

您可以使用this page生成Base64格式的随机AES密钥。

即便如此,如果攻击者以某种方式成功解密视图状态并让另一个用户实际提交它(例如通过XSS漏洞或网络钓鱼站点),那么最好的办法是在其中包含基于HTTP会话的CSRF令牌。 JSF页面。但是,如果webapp在某处仍然存在XSS攻击漏洞,或者通过HTTP而不是HTTPS提供服务,那么这也是不安全的。

另见:

答案 1 :(得分:-1)

Spring Security 默认不检查 GET 请求。在 CSRF 保护中,防止危险的 POST 请求操作很重要。因此,您可以考虑使用以下代码集忽略 ajax partialSubmit="true" 请求。

@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {


    @Override
    protected void configure(HttpSecurity http) throws Exception {

        RequestMatcher csrfRequestMatcher = new RequestMatcher() {
            private final Pattern allowedMethods = Pattern.compile("^GET$");
            @Override
            public boolean matches(HttpServletRequest request) {

                if(request == null)
                    return false;

                if (allowedMethods.matcher(request.getMethod()).matches()) {
                    return false;
                }

                else return request.getParameter("javax.faces.partial.ajax") == null || !request.getParameter("javax.faces.partial.ajax").equals("true");
            }

        };

        http
            .anyRequest().authenticated()
            .....
            .csrf().requireCsrfProtectionMatcher(csrfRequestMatcher);
    }
}