【问题标题】:Reusing ViewState value in other session (CSRF)在其他会话中重用 ViewState 值 (CSRF)
【发布时间】:2015-08-03 01:59:22
【问题描述】:

我正在使用 *myfaces-api-2.2.3 并将 javax.faces.STATE_SAVING_METHOD 设置为 client

我得到了以下场景,

1) 用户 X 登录系统并添加用户 XXX(使用 jsf f:ajax 操作),在检查 chrome 开发工具时,您可以看到正在提交的表单以及 ViewState 值。

2) 复制该 ViewState 值(来自 chrome 开发工具 --> 网络选项卡)--> 将其放入带有表单的 html 文件中(模仿我原来的 添加用户 X)

3) 从用户 X 会话中注销(会话在该过程中失效)

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

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


查看我的其他问题和BalusC 答案:Prevent CSRF in JSF2 with client side state saving

【问题讨论】:

  • 仅供参考:这是客户端状态保存的指定/预期行为。只是不确定如何为 MyFaces 回答“我如何确保用户不能重用 ViewState”,而不是切换回服务器端状态保存。此外,CSRF 的可能性有点夸张,因为视图状态基本上包含组件状态,而不是登录用户和其他会话数据。这一切都取决于形式,例如如果它是一些用于用户管理的管理表单,攻击者可以准备一些状态(例如,为自己分配管理员角色)并让管理员用户提交它,但您仍然需要 XSS 漏洞和/或会话劫持。
  • 视图状态 ID 不应该随着会话 @BalusC 而消失吗?
  • 仅用于服务器端状态保存。另见 a.o. stackoverflow.com/a/3642969
  • @BalusC,我知道它,对于当前的实现,只有在服务器端状态保存的情况下“死”,但是如果视图状态 id(只是 id)也放在会话范围内的“有效视图状态”中id'列表,这个问题就解决了。我不认为这是一个“错误”或真正的“安全问题”,而是一个可以“改进”的“遗漏”。

标签: jsf-2 viewstate jsf-2.2 myfaces


【解决方案1】:

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

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

CSRF 攻击场景在 IMO 有点夸张。首先,攻击者需要能够掌握客户端状态。最简单的方法是 XSS 漏洞。只是,JSF 到处都有 XSS 攻击防护(如果你小心escape="false")。最难的方法是掌握整个 HTML 输出。这可以通过中间人攻击来完成。只是,这也为攻击者提供了整个会话 cookie,因此在这种情况下,整个 CSRF 攻击场景对于攻击者来说“不必要地过于复杂”,因为攻击者可以简单地劫持会话。最好的保护措施就是使用 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 提供服务,这也是不安全的。

另见:

【讨论】:

  • 感谢您的回答,所以您是说如果我继续使用客户端状态保存,我能够防止我在问题中描述的场景的唯一方法是生成我自己的 csrf 每个会话的令牌并检查每个客户端 -> 服务器请求是否使用的令牌等于该会话的当前令牌?
  • 如果你想保持客户端视图状态保存,那确实是你最好的选择。并且,确保您没有 XSS 漏洞,并且 web 应用程序通过 HTTPS 提供服务。
  • 好的,谢谢,有没有办法将令牌注入到我从客户端到服务器的所有 POST 提交中,我不想修改我所有的h:form,相反,我更喜欢单点条目,在其中我将向form 注入令牌,然后在提交时将针对服务器上的令牌进行测试
  • 覆盖 h:form 渲染器。在编码中写入令牌并在解码中验证它。如果您使用 PrimeFaces,请注意 partialSubmit="true",如果您没有明确指定它,它将跳过该令牌。
  • 谢谢,我正在使用 PF,我应该如何为 primefaces 明确指定它? (如果你愿意,我可以开一个新的 Q'?
【解决方案2】:

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2010-12-26
    • 2012-09-01
    • 1970-01-01
    • 2013-03-08
    • 2014-01-25
    • 1970-01-01
    • 1970-01-01
    • 2013-11-19
    相关资源
    最近更新 更多