使用 CSRF 保护测试 scala Play (2.2.1) 控制器
Posted
技术标签:
【中文标题】使用 CSRF 保护测试 scala Play (2.2.1) 控制器【英文标题】:Testing scala Play (2.2.1) controllers with CSRF protection 【发布时间】:2013-11-19 05:46:18 【问题描述】:我在测试使用 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 保护的表单,我将不胜感激。
【问题讨论】:
【参考方案1】:一种解决方案是使用浏览器进行测试,例如 Fluentlenium,因为这将管理 cookie 等,因此 CSRF 保护应该都能正常工作。
另一种解决方案是在 FakeRequest 中添加一个会话,使其包含一个令牌,例如:
FakeRequest().withSession("csrfToken" -> CSRF.SignedTokenProvider.generateToken)
显然,如果你经常这样做,你可以创建一个帮助方法来为你做这件事。
【讨论】:
我正在使用模拟和依赖注入直接测试控制器,因此这里不能选择硒风格的集成测试。向会话添加一个有效的、生成的令牌确实会阻止错误的发生。非常感谢!我仍然觉得这揭示了 Play 运行测试的方式中的一个错误,但我可以将它放在邮件列表中。再次感谢。 有没有办法在 Java 中做到这一点?【参考方案2】:对于那些对 Java 感兴趣的人的奖励答案:我通过添加使其在 Play Framework 2.2 的 Java 版本中工作
.withSession(CSRF.TokenName(), CSRFFilter.apply$default$5().generateToken())
致fakeRequest()
【讨论】:
感谢分享。我不喜欢 Java 版本的 Play 进行测试,所以我会相信你的话 :-)【参考方案3】:从@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);
【讨论】:
玩 2.5.X 你需要使用CSRFFilter.apply$default$3().generateToken();
和CSRFFilter.apply$default$1().tokenName()
【参考方案4】:
致那些仍然感兴趣的人:我设法通过在测试中启用 CSRF 保护来解决这个问题。然后,应用程序将为每个不包含令牌的请求创建一个令牌。查看我对this question的回复
【讨论】:
【参考方案5】:对于那些可能感兴趣的人,我为 play 2.5.x 创建了一个 trait: https://***.com/a/40259536/3894835
然后您可以在您的测试请求中使用它,例如控制器的 addToken:
val fakeRequest = addToken(FakeRequest(/* params */))
【讨论】:
【参考方案6】:我在我的基础集成测试类中使用以下方法:
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
的测试中使用它。
【讨论】:
以上是关于使用 CSRF 保护测试 scala Play (2.2.1) 控制器的主要内容,如果未能解决你的问题,请参考以下文章
Play 2.2:使用 Play Caching (Scala) 对代码进行单元测试时出现问题
使用ScalaTest和Selenium测试Scala.JS + Play