【问题标题】:Play Framework 2.6 CSRF and SessionPlay Framework 2.6 CSRF和Session
【发布时间】:2018-03-09 10:40:43
【问题描述】:

我遇到了奇怪的问题。我正在我的网站上实现购物车功能,并使用会话来存储购物车位置。我有一个 POST 操作来向购物车添加新位置,并且我启用了 CSRF 过滤器来保护网站。我在产品页面上用 ajax 调用它,所以第一次调用没问题,但第二次调用未授权,日志中有 [CSRF] Check failed because no token found in headers for /cart。但它有。我称之为:

 $("form").submit(function(e){
        e.preventDefault();
        $.ajax({
            url: '/cart',
            method: 'POST',
            data: getCartPosition(),
            beforeSend: function(xhr){xhr.setRequestHeader('Csrf-Token', $('input[name=csrfToken]').val());},
            success: function (data, textStatus) {
                alert('Added!');
            },
            error: function (error) {
                alert('Error!');
            }
        });
    });

我将 CSRF 令牌放在模板中的某处:

@CSRF.formField

它正在请求中:

我已经在配置中启用了这个

play.filters.csrf.bypassCorsTrustedOrigins=true 
play.filters.hosts {
  # Allow requests to example.com, its subdomains, and localhost:9000
  allowed = ["localhost:9000", "localhost:4200"]
}

但奇怪的是它似乎将 csrfToken 置于会话中,因为在请求失败后我有这样的会话

Session(Map(cart -> {"positions":
[{"trackId":1},{"trackId":24},{"trackId":20}]}, 
username -> user, 
token -> 0639d0b0-e7c8-4e82-9aad-2a43044e72db, 
csrfToken -> e705413843ea96a6491a0e9e800ba36a712c4f70-1506542471068-0baeef7535eb9c889fb6fed2))

不知道为什么会出现,我的 add2cart 操作如下所示:

private def cartAction(addToCartForm: Form[CartPosition], action: (Cart, CartPosition) => Cart)(implicit request: UserRequest[Any]) = {
    addToCartForm.fold(
      _ => BadRequest("Error!"),
      position => {
        getCart match {
          case Some(cart) => Ok("Ok").withSession("cart" -> Json.toJson(action(cart, position)).toString(), "username" -> request.session.get("username").getOrElse(""), "token" -> request.session.get("token").getOrElse(""))
          case _ => Ok("Ok, no").withSession("cart" -> Json.toJson(action(Cart(Seq.empty), position)).toString())
        }
      }
    )
  }

def addToCart() = guestAction { implicit request =>
    cartAction(addToCartForm.bindFromRequest, addCartPos)
  }

而 addCartPos 只是将位置添加到 json

【问题讨论】:

  • 切换到 GET 并且它可以工作,但仍然没有弄清楚为什么它不能与 POST 一起工作
  • 实际上我在会话中得到 csrfToken 不仅是购物车,它只是把它放在任何地方
  • 我遇到了类似的问题,我使用addingToSession 而不是withSession 解决了这个问题。我猜后者会覆盖会话并清除渲染模板时放入其中的 csrf 令牌。不确定这是否与您遇到的问题相同,因为我不太了解 Play 中的 csrf 机制是如何工作的。

标签: scala playframework csrf


【解决方案1】:

我在 Play 2.7.3 上遇到了同样的问题。

在我的例子中,表单是由带有csrf token 的 Twirl 生成的,因为我使用 ajax 来提交表单,所以我从呈现的表单中复制了 csrf token 并将其传递给 ajax 标题正如 Play 的文档中所写。

表单可以提交多次,所以我需要更新令牌。因此,我正在通过控制器中从play.filters.csrf.CSRF.getToken.get.value 获取的 ajax 响应 new csrf token

但很遗憾,正如cutoffurmind所说,第二次提交失败了。

Knut Arne Vedaa 所描述的修复方法是为会话添加新令牌。我是通过withSession 方法做到的。

因此控制器响应如下所示:

 Ok(Json.obj(
    "status" -> (user != None),
    "notif" -> "Success login",
    "data" -> Map( 
       "adminUrl" -> "www.something ...", 
       "csrf" -> play.filters.csrf.CSRF.getToken.get.value
    )
 )).withSession(
    "uid" -> user.getOrElse(User()).id.toString,
    "csrfToken" -> play.filters.csrf.CSRF.getToken.get.value
 )

这看起来不是问题,因为 Play Framework 没有在服务器上保存会话数据,因此在 ajax 请求之后必须在客户端站点中更新令牌是合乎逻辑的。主要问题是文档中没有提到它(在 CSRF ajax 部分),因为人们根本没有按照预期的顺序从 A 到 Z 阅读文档,所以这可能很方便。

【讨论】:

    【解决方案2】:

    在我的情况下,解决方案是将 play.filters.csrf.cookie.name 配置选项设置为 null 以外的值:

    play.filters.csrf.cookie.name = csrf_token
    

    【讨论】:

    • 这对我也有用 - 我一直收到 [CSRF] Check failed 因为在我设置此属性之前在标头中找不到令牌 - 你知道为什么会这样吗?奇怪的是,这在没有设置属性的情况下在 2017 年起作用。文档很糟糕,我不清楚 csrf 保护如何与 cookie 交互。
    • 有几种类型的 CSRF 保护可用,其中一些使用 cookie en.wikipedia.org/wiki/Cross-site_request_forgery
    猜你喜欢
    • 2015-12-05
    • 1970-01-01
    • 2018-02-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-02-18
    • 1970-01-01
    相关资源
    最近更新 更多