使用CSRF保护测试scala Play(2.2.1)控制器

时间:2013-11-07 14:24:12

标签: scala testing playframework-2.0 csrf

我在测试使用Play的CSRF保护的控制器时遇到了一些问题。为了证明这一点,我创建了一个非常简单的Play应用程序,可以最小化地解决问题。

https://github.com/adamnfish/csrftest

完整的详细信息在该存储库的自述文件中,但总结一下:

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

这很棒!

但是,在测试中我们现在遇到了一些问题。要测试控制器,您可以在测试中向其传递虚假请求。可以通过将nocheck标头添加到伪请求来跳过CSRF检查本身,但无法呈现视图,因为没有可用于生成表单字段的标记。测试失败,出现RuntimeException,“缺少CSRF令牌(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保护表单,我会很感激。

6 个答案:

答案 0 :(得分:4)

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

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

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

显然,如果您经常这样做,您可以创建一个帮助方法来为您完成。

答案 1 :(得分:4)

对那些对Java感兴趣的人的答案:我通过添加

在Java版本的Play Framework 2.2中使用它
.withSession(CSRF.TokenName(), CSRFFilter.apply$default$5().generateToken())

fakeRequest()

答案 2 :(得分: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);
}

答案 3 :(得分:1)

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

的回答

答案 4 :(得分:1)

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

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

val fakeRequest = addToken(FakeRequest(/* params */))

答案 5 :(得分: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的测试中使用它。