4

我在测试使用 Play 的 CSRF 保护的控制器时遇到了一些问题。为了证明这一点,我创建了一个非常简单的 Play 应用程序,该应用程序几乎不会出现问题。

https://github.com/adamnfish/csrftest

完整的详细信息在该存储库的 README 中,但在这里总结一下:

考虑一个设计用于处理表单提交的控制器。它有一个使用 CSRFAddToken 的 GET 方法和一个使用 CSRFCheck 的 POST 方法。前者将 CSRF Token 添加到请求中,以便可以将包含有效令牌的表单字段放入呈现的视图中。当提交该表单时,如果 CSRF 检查通过并且提交有效,则会发生其他事情(通常是重定向)。如果表单提交无效,则会重新显示表单提交以及任何错误,以便用户可以更正表单并再次提交。

这很好用!

但是,在测试中,我们现在遇到了一些问题。要测试控制器,您可以在测试中向它传递一个虚假请求。可以通过将 nocheck 标头添加到假请求来跳过 CSRF 检查本身,但由于没有可用于生成表单字段的令牌,因此无法呈现视图。测试失败,出现 RuntimeException,“Missing CSRF Token (csrf.scala:51)”。

鉴于它在实际运行但在测试中不起作用时,似乎这肯定是 FakeRequests 在 Play 测试中运行的方式存在问题,但我可能做错了什么。我已经实现了 http://www.playframework.com/documentation/2.2.1/ScalaCsrf 中描述的 CSRF 保护和http://www.playframework.com/documentation/2.2.1/ScalaFunctionalTest描述的测试. 如果有人设法测试受 CSRF 保护的表单,我将不胜感激。

4

6 回答 6

4

一种解决方案是使用浏览器进行测试,例如 Fluentlenium,因为这将管理 cookie 等,因此 CSRF 保护应该都能正常工作。

另一种解决方案是向 FakeRequest 添加一个会话,以便它包含一个令牌,例如:

FakeRequest().withSession("csrfToken" -> CSRF.SignedTokenProvider.generateToken)

显然,如果你经常这样做,你可以创建一个帮助方法来为你做这件事。

于 2013-11-08T00:20:05.723 回答
4

对 Java 感兴趣的人的奖励答案:我通过添加在 Play Framework 2.2 的 Java 版本中工作

.withSession(CSRF.TokenName(), CSRFFilter.apply$default$5().generateToken())

fakeRequest()

于 2014-03-25T08:16:02.593 回答
1

继@plade 之后,我在我的基础测试类中添加了一个辅助方法:

protected static FakeRequest csrfRequest(String method, String url) {
    String token = CSRFFilter.apply$default$5().generateToken();
    return fakeRequest(method, url + "?csrfToken=" + token)
        .withSession(CSRF.TokenName(), token);
}
于 2014-05-27T03:07:34.693 回答
1

对于那些仍然感兴趣的人:我设法通过在测试中启用 CSRF 保护来解决这个问题。然后,应用程序将为每个不包含令牌的请求创建一个令牌。看我对这个问题的回答

于 2017-04-28T13:04:55.330 回答
1

对于那些可能感兴趣的人,我为 play 2.5.x 创建了一个特征: https ://stackoverflow.com/a/40259536/3894835

然后,您可以在测试请求中使用它,例如控制器的 addToken{}:

val fakeRequest = addToken(FakeRequest(/* params */))
于 2017-05-12T14:01:25.003 回答
1

我在我的基础集成测试类中使用以下方法:

def csrfRequest(method: String, uri: String)(implicit app: Application): FakeRequest[AnyContentAsEmpty.type] = {
  val tokenProvider: TokenProvider = app.injector.instanceOf[TokenProvider]
  val csrfTags = Map(Token.NameRequestTag -> "csrfToken", Token.RequestTag -> tokenProvider.generateToken)
  FakeRequest(method, uri, FakeHeaders(), AnyContentAsEmpty, tags = csrfTags)
}

然后你可以在你的测试中使用它,你会使用FakeRequest.

于 2017-07-06T11:40:11.300 回答