Laravel 403无效签名仅在Nginx Web服务器上

时间:2019-06-21 14:02:52

标签: php laravel nginx https

问题

我正在通过从laravels signed routes文档复制并粘贴的laravels'temporarySignedRoute'函数创建一个签名URL,在我的Web服务器上,URL出现403无效签名。签名的URL在我的本地计算机上有效,但是将其上传到我的测试服务器后,会显示以下错误。

enter image description here

我尝试过的

一段时间以来,我一直在寻找解决方案,并尝试了许多已发布到堆栈溢出的解决方案,但是似乎没有任何解决方案可以解决我的问题。

我尝试了以下所有操作:

  
      
  • 通过AppServiceProvider强制所有到https的路由,这会强制所有url为https,但结果相同。
  •   
  • 我尝试按照here所述更改Nginx的配置。

  •   
  • 我还尝试添加TrustProxies中间件,并将其代理配置为'*'或全部,如laravel在Trust Proxies上的文档中所述,结果相同。

  •   
  • 阻止Web服务器强制将域强制为https以测试是否只是由https引起的,结果相同。

  •   

-更新-

在尝试搜索项目中的任何线索时,我进入了vendor/laravel/framework/src/Illuminate/Routing/UrlGenerator.php。我决定将其在本地和托管项目中比较的令牌进行数据转储(dd),这就是结果。代码也如下所示。

public function hasValidSignature(Request $request, $absolute = true)
{
    $url = $absolute ? $request->url() : '/'.$request->path();

    $original = rtrim($url.'?'.Arr::query(
        Arr::except($request->query(), 'signature')
    ), '?');

    $expires = $request->query('expires');

    $signature = hash_hmac('sha256', $original, call_user_func($this->keyResolver));
    dd($signature . " :: " . $request->query('signature', ''));
    return  hash_equals($signature, (string) $request->query('signature', '')) &&
           ! ($expires && Carbon::now()->getTimestamp() > $expires);
}

本地项目

local project

托管项目

remote machine

因此,令牌未在我的Web服务器上传递,而是在我的本地服务器上传递。我还注意到的另一件事是,Web服务器令牌始终是相同的,并且无论如何都不会改变。

我当前的Nginx配置可以查看here

我当前的虚拟主机配置为here

3 个答案:

答案 0 :(得分:2)

我认为这可能是问题所在。 尝试将配置文件中的位置块更改为

document.addEventListener("message", function (event) { window.postMessage(event.data); });

我认为您错过了索引和查询字符串之间的try_files $uri $uri/ /index.php?$query_string;

更新此内容,并确保重新加载Nginx服务器。 ?

让我知道是否可行。

答案 1 :(得分:0)

这不是Nginx错误。如果您检查Illuminate\Routing\Middleware\ValidateSignature并抛出Illuminate\Routing\Exceptions\InvalidSignatureException,laravel就是这样处理laravel处理无效的签名url的,该请求中止具有403状态的请求。我做了什么, 创建了一个自定义签名中间件,该中间件扩展了实际的ValidateSignature

<?php

namespace App\Http\Middleware;

use App\Exceptions\InvalidEmailVerificationException;
use Closure;
use Illuminate\Routing\Middleware\ValidateSignature as MiddleWare;

class ValidateSignature extends Middleware
{
    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */
    public function handle($request, Closure $next)
    {
        if ($request->hasValidSignature()) {
            return $next($request);
        }

        throw new InvalidEmailVerificationException;
    }
}

会引发自定义App\Exceptions\InvalidEmailVerificationException

<?php

namespace App\Exceptions;

use Symfony\Component\HttpKernel\Exception\HttpException;;

class InvalidEmailVerificationException extends HttpException
{
    /**
     * Create a new exception instance.
     *
     * @return void
     */
    public function __construct()
    {
        parent::__construct(403, 'Your email verification token seems to be invalid.');
    }
}

然后将App\Http\Kernel.php $routeMiddleware中已签名的中间件更改为'signed' => \App\Http\Middleware\ValidateSignature::class,

在403.blade.php视图中,您可以将消息显示为

@if ($exception instanceof \App\Exceptions\InvalidEmailVerificationException)
                            <h1 class="text-ginormous">Snap!</h1>
                            <h4 class="quick-error-description">Verification token expired.</h4>
                            @if (session('resent'))
                                <div class="alert alert-success" role="alert">
                                    {{ __('A fresh verification link has been sent to your email address.') }}
                                </div>
                            @endif
                            <p class="error-description">{{ $exception->getMessage() }}</p>

                            <a href="{{ route('verification.resend') }}" class="button-solid button-error mb-3" data-no-pjax>Email me a new link</a>
@endif

答案 2 :(得分:0)

当时Kumar Barnwal王子的帖子解决了我的问题,最近,我在尝试在同一设置中使用docker时重新发现了该问题的另一个版本,我想如果有人遇到同样的问题,我会在这里发布。

我们正在使用代理服务器并在其上认证我们的docker laravel项目,所有项目均具有TrustProxies设置,然后我们在AppServiceProvider.php中强制使用HTTPS,这使AJAX调用可以正常工作。这会导致问题,因为项目本身未加密,但是它生成的所有URL都认为这是因为我们强制执行了。当代理在请求中传递URL时,它是http,即项目需要HTTPS时。

我找到了一个解决方案,它不是最干净的,并且可能是更好的解决方案,但是我制作了一个中间件,该中间件可以用于所有项目签名的路由,基本上,它只是将请求中的url更改为安全的如果项目正在生产中。这不是不安全的,因为代理可以处理安全性,并且仅在签名的路由上存在。

下面是仅在签名路由上使用的中间件。

<?php

namespace App\Http\Middleware;

use Closure;

class SecureSignedURL
{
    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */
    public function handle($request, Closure $next)
    {
        if(config('app.env') === 'production' || config('app.env') === 'staging') {
            $request->server->set('HTTPS', 'ON');
        }
        return $next($request);
    }
}

我的web.php和中间件,在我的secure_signed_url中定义为Kernel.php

Route::group(['middleware' => ['secure_signed_Url', 'signed']], function () {
        route::get('/exampleurl1', 'TestController@route_exampleurl1_signed')->name('exampleurl1.signed');
        route::get('/exampleurl2', 'Template\TemplateController@route_exampleurl2_signed')->name('exampleurl2.signed');
});

同样,我不知道这是否是最好的解决方法,这是Laravel中一个非常模糊的错误。如果您有更好的解决方法的建议,欢迎提出建议。

我的旧Github问题可能会有所帮助:https://github.com/laravel/framework/pull/31434