【问题标题】:Play Framework CSRF error "[CSRF] Check failed because no token found in headers"播放框架 CSRF 错误“[CSRF] 检查失败,因为在标头中找不到令牌”
【发布时间】:2017-07-02 15:49:23
【问题描述】:

我是 Play Framework 的新手,正在尝试提交表单,但是 收到此错误:“p.filters.CSRF - [CSRF] Check failed because no token found in headers”。我正在使用 Play 2.6,这是我的控制器代码:

    package controllers;

import play.libs.Json;
import play.mvc.*;

import views.html.*;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

public class HomeController extends Controller {

    public Result index() {
        return ok(index.render("Your new application is ready."));
    }

    public Result test() {
        List<String> list = new ArrayList<>();
        list.add("Jobs");
        list.add("Work");
        list.add("Say");
        list.add("Stop");
        return ok(test.render("test", list));
    }

    public Result testPost() {
       Map<String,String[]> form =  request().body().asFormUrlEncoded();
       return ok(Json.toJson(form)).as("application/json");
    }

}

模板:

@(title: String, list: List[String])

@import helper._

<!DOCTYPE html>
<html lang="en">
    <head>
        <title>@title</title>
    </head>
    <body>
        <form action="/test" method="post">
            <textarea name="raw_text">

            </textarea>
            @CSRF.formField
            <input type="submit" value="Submit" />
        </form>
    </body>
</html>

我做错了什么?

【问题讨论】:

    标签: java playframework


    【解决方案1】:

    我已经遇到过。我很简单地解决了它。只需添加到您的 application.conf

    play.filters.enabled += "play.filters.csrf.CSRFFilter"
    

    routes 文件中,在你的路由前添加 nocsrf 修饰符标签:

    +nocsrf
    POST        /login                     controllers.AuthController.authorize()
    

    就这样.. 希望我的回答对你有所帮助。谢谢!

    【讨论】:

    • /login 听起来这个控制器将接收来自用户的表单。禁用该路由上的 CSRF 过滤器完全破坏了使用该过滤器的目的。但是当这是一个受保护的 API 端点时很方便。
    • 如果其他人刚刚使用 Silhouette 框架遇到了这个问题,您至少可以使用这个答案来确认您是否可以解决这个问题。在我的情况下,POST /signIn 通话令人窒息。一旦新的会话令牌发出,就不会再有问题了。
    【解决方案2】:

    通过 Duman Zhanbolatov 的回答可以避免错误,但这不是答案。它会将应用程序推向安全漏洞。

    应用程序面临问题的原因是表单页面 requestPLAY_SESSION(或您的自定义会话名称)cookie 为空,其中 csrfToken=value 应该有被加密了。因为不知何故,cookie 中的 csrfToken=value 已被删除。如果您发送带有新会话或丢弃 cookie 的 响应,则可能会发生这种情况。 因此,如果您丢弃 cookie 或使用新会话发送响应,请使用 csrfToken=value 会话发送它。

    def logout: Action[AnyContent] = Action {
        implicit request =>
          val csrfToken = CSRF.getToken.get.value
          
          Ok(logIn("Please log in")).withNewSession.withSession("csrfToken"->csrfToken)
    
      }  
    

    那么你就可以摆脱这个问题了。

    第二个原因是:您在 domain/localhost/Ip 下运行了另一个播放应用程序。所以那个域/localhost/Ip 的 Cookie 还在浏览器的缓存中。当您尝试通过新的 play 应用程序创建 post req 时,cookie 被传递到服务器,结果出现 csrf 验证问题。在这种情况下,只需清除浏览器中的 cookie。

    【讨论】:

      【解决方案3】:

      我没有发现您的代码有任何问题,因此请在本地设置一个相同的 Play 项目。 提交表单时,我没有遇到这个问题。 这是通过以下方式创建的基础项目:

      sbt new playframework/play-java-seed.g8
      

      将“Testing testing”作为 textArea 的内容发布会返回以下 Json(由浏览器显示):

      raw_text    
      0   "            Testing testing"
      csrfToken   
      0   "8fdc72b2d628e4fc0d8f6359f2ea247cc0f22e5c-1499016237415-3c043ab2a02c18020df21a1f"
      

      我从自己的项目中得到的一件事是修改后的过滤器类,如下所示:

      import play.mvc.EssentialFilter;
      import play.filters.cors.CORSFilter;
      import play.http.HttpFilters;
      import play.filters.csrf.CSRFFilter;
      import play.filters.headers.SecurityHeadersFilter;
      import java.util.List;
      import javax.inject.Inject;
      import javax.inject.Named;
      import java.util.Arrays;
      public class Filters implements HttpFilters {
          @Inject 
          CSRFFilter csrfFilter;
          @Inject
          CORSFilter corsFilter;
          @Inject 
          SecurityHeadersFilter secHeaders;
      
          @Override
          public List<EssentialFilter> getFilters() {
      
              return Arrays.<EssentialFilter>asList(new EssentialFilter[] { corsFilter.asJava(),secHeaders.asJava(), csrfFilter.asJava()});
                      }
      }
      

      另外我的路由在conf/routes中如下:

      # Map static resources from the /public folder to the /assets URL path
      GET     /assets/*file               controllers.Assets.versioned(path="/public", file: Asset)
      GET / controllers.HomeController.test()
      POST /test controllers.HomeController.testPost()
      

      删除 @CSRF.formField 会导致 CSRF 未经授权,就像你一样。

      如果您的设置有任何明显差异,我很乐意进一步挖掘,但这似乎不是您的代码的直接问题。

      【讨论】:

      • 我以同样的方式创建了项目,现在一切正常。但是当我从这里playframework.com/download#starters 使用 play-java-starter-example 时,仍然会出现此错误。
      • 那么这可能是过滤器配置问题,从页面下方:“如果您已经了解 Play 并想立即创建新项目,这是一个不错的选择。请注意,种子模板已经配置了 CSRF 和安全标头过滤器,而其他项目并未专门针对开箱即用的安全性进行设置。”明天我会看看基础设置。答案中的 Sbt 命令使用种子模板。干杯。
      • 我对从 play-scala-seed.g8 模板创建的 scala 项目有同样的问题。我通过了文档中指定的 CSRF 令牌,但我得到了 Unauthorize 并且在控制台中我看到:p.a.m.DefaultJWTCookieDataCodec - decode: could not decode JWT: JWT signature does not match local computed signature。 JWT 有效性不能被断言,也不应该被信任。 io.jsonwebtoken.SignatureException:JWT 签名与本地计算的签名不匹配。 JWT 有效性不能被断言并且不应该被信任并且 [CSRF] 检查失败,因为在标头中找不到令牌。
      • 我使用@CSRF.formField 的东西。
      • 好的,我重新开始游戏后它开始工作了 - 很奇怪。
      【解决方案4】:

      这可能不适用于所有情况,但在我的情况下,原因是我正在重置控制器中的会话,因此 CSRF 令牌不知何故变得无效。

      如果您在代码中调用“withNewSession”并出现此错误,请尝试将其删除并查看错误是否消失。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2020-09-19
        • 2020-10-12
        • 1970-01-01
        • 2014-12-25
        • 2021-03-18
        • 2017-09-21
        • 2020-12-02
        • 2014-02-26
        相关资源
        最近更新 更多