【问题标题】:Laravel DecryptException - The payload is invalidLaravel DecryptException - 有效负载无效
【发布时间】:2017-11-22 22:43:17
【问题描述】:

我正在向我的 Laravel API 发送 AJAX 发布请求并收到此错误消息:

compiled.php 第 13235 行中的 DecryptException: 负载无效。

我正在从 cookie 中读取 XSRF-TOKEN,并将其作为名为 X-XSRF-TOKEN 的请求标头发送。

该站点与 Laravel API 完全独立,但共享相同的会话,这就是我从 cookie 获取值的原因。

奇怪的是,有时它会起作用。任何想法是什么原因造成的?

【问题讨论】:

  • 您发送的是XSRF-TOKEN 还是X-CSRF-TOKEN
  • 我发送一个header名字:X-XSRF-TOKEN,值取自名为XSRF-TOKEN的cookie
  • 发送X-CSRF-TOKEN 标头。 XSRF-TOKEN cookie 未加密,但 laravel 期望 X-XSRF-TOKEN 标头被加密。
  • 查看这篇可能对您有帮助的帖子stackoverflow.com/questions/42408177/…
  • 文档说明了这一点:Laravel 还将 CSRF 令牌存储在 XSRF-TOKEN cookie 中。您可以使用 cookie 值来设置 X-XSRF-TOKEN 请求标头。一些 JavaScript 框架,例如 Angular,会自动为您执行此操作。 - 这正是我正在做的事情。

标签: php laravel api cookies


【解决方案1】:

如果您从 JavaScript 发送 X-XSRF-TOKEN,您可以使用 decodeURIComponent() 对其进行解码。它将%3D 转换为=

【讨论】:

  • 我用过:document.cookie.split('XSRF-TOKEN=').join('').split('%3D').join('')
  • 是的,这可行,但并非在所有情况下。最好使用decodeURIComponent()
【解决方案2】:

我找到了问题的原因。 XSRF-TOKEN cookie 值有时会在末尾附加一个恶意字符:'%3D' - 有时最后会有两个。不知道他们是如何到达那里的,但是当他们出现时,验证失败。

如果你对 cookie 值进行 base64_decode,你会得到一个 json 字符串,它的末尾附加了流氓字符:'7',因此 Laravel 的解密方法失败。

我最终不得不编写自己的 CSRF 验证函数:

$payload = base64_decode($request->header('X-XSRF-TOKEN'));

            //Remove any rogue chars from the end of the json  
            for($i=0; $i<strlen($payload); $i++){
                $lastChar = substr($payload, -1);
                if($lastChar != '}'){
                    $payload = substr($payload, 0, -1);
                } else {
                    break;
                }
            }

            //Needs to be base64 encoded when passed to decrypt
            $payload = base64_encode($payload);

            $headerToken = decrypt($payload);
            $cookieToken = $request->cookie('XSRF-TOKEN');

            //Compare tokens
            if($headerToken == $cookieToken){
                return true;
            } else {
                return false;
            }

【讨论】:

  • 我还在 XSRF-TOKEN cookie 值的末尾附加了这个奇怪的字符。谁知道是什么原因造成的?
  • 我认为为 url 编码 cookie 只是默认的浏览器行为。问题是 laravel 没有预先对其进行 urldecoding
  • 因此,我建议您在 server.php 顶部添加 $_COOKIE = array_map('urldecode', $_COOKIE);
【解决方案3】:

我遇到了同样的问题,这确实是由于 cookie 是 url 编码的,而 laravel 没有正确解码

你可以在解密前通过url解码cookie来解决

app/Http/Middleware/EncryptCookies.php

<?php

namespace App\Http\Middleware;

use Illuminate\Cookie\Middleware\EncryptCookies as Middleware;

class EncryptCookies extends Middleware {
   /**
     * Decrypt the given cookie and return the value.
     *
     * @param  string  $name
     * @param  string|array  $cookie
     * @return string|array
     */
    protected function decryptCookie($name, $cookie)
    {
        return is_array($cookie)
                        ? $this->decryptArray($cookie)
                        : $this->encrypter->decrypt(urldecode($cookie), static::serialized($name));
    }
}

确保您在app/Http/Kernel.php 中使用了正确的中间件,您应该寻找EncryptCookies 并将其替换为新类

例如:

    protected $middlewareGroups = [
        'web' => [
            \App\Http\Middleware\EncryptCookies::class,
    //...

【讨论】:

    【解决方案4】:

    它也可能由于加密/解密错误而发生,其中来自数据库的纯文本被解密方法/函数解密,因为您会将一些数据直接添加到数据库中,这些数据需要加密机器人作为纯文本插入,所以它可能与加密/解密问题有关。

    【讨论】:

      【解决方案5】:

      我遇到了类似的问题,但似乎只与 Google Chrome 有关。每当遇到解密异常时,我都会将 EncryptCookies 修改为 dd():

      protected function decrypt(Request $request)
      {
          foreach ($request->cookies as $key => $c) {
              if ($this->isDisabled($key)) {
                  continue;
              }
      
              try {
                  $request->cookies->set($key, $this->decryptCookie($c));
              } catch (DecryptException $e) {
                  dd('exception: ', $e, $key, $c, $request); // added by me
                  $request->cookies->set($key, null);
              }
          }
      
          return $request;
      }
      

      奇怪的是,每当我刷新页面时,有时会抛出 DecryptException,但大多数时候 try 语句都会成功。当我在 IE 和 Firefox 中测试时,try 语句总是成功。这似乎与我的请求标头中的数据量有关,但问题是不确定的。

      【讨论】:

      • 嗨,我正在使用 Mozilla 浏览器,但仍然遇到同样的问题,有什么帮助吗??
      • 不幸的是我从来没有解决这个问题。我遇到了与 ezero 提到的流氓角色最后出现的问题相同的问题。这似乎与请求标头中的数据量有关。
      • 我同意 chrome 似乎会在超过 2Kb 时对 cookie 进行 urlencode
      【解决方案6】:

      这是在 Laravel 中对我有用的解决方案和步骤:

      1. 将 CSRF 令牌作为名为 csrf-token 的元标记包含在文档的标题中:

      <!-- CSRF Token -->
       <meta name="csrf-token" content="{{ csrf_token() }}">
      1. 创建一个 JavaScript 函数来检索 csrf-token 元标记的值:

      function csrf(name="csrf-token"){
          const metas = document.getElementsByTagName('meta');
          for (let i = 0; i < metas.length; i++) {
              if (metas[i].getAttribute('name') === name) {
                  return metas[i].getAttribute('content');
              }
          }
          
          return null;
      }
      1. 为您的请求设置 X-CSRF-TOKEN 标头,为其指定 csrf() 函数的值。使用 Fetch API 的示例:

      let params = {headers:{}};
      params.headers["Content-Type"] = "application/json; charset=UTF-8";
      params.headers["X-CSRF-TOKEN"] = csrf(); // call your csrf() function
      params.mode = "same-origin";
      params.method = "POST";
      let response = await fetch('/your-url', params);
      // handle the response object

      【讨论】:

      • 问题明确指出前端与 Laravel API 后端完全分离,因此他们不能使用 {{ csrf_token() }}
      【解决方案7】:

      +1 Tofandel 的回答,我删除了'XSRF-TOKEN' cookie 的加密。

      app/Http/Middleware/EncryptCookies.php 中添加:

      protected $except = [
         'XSRF-TOKEN',
      ];
      

      【讨论】:

        猜你喜欢
        • 2018-02-22
        • 2018-02-14
        • 1970-01-01
        • 2018-05-07
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多