Подписанный маршрут для проверки электронной почты не проходит проверку подписи - PullRequest
0 голосов
/ 26 сентября 2018

Я недавно обновил свой проект с Laravel 5.6 до 5.7 и добавил шаги проверки электронной почты, описанные в документах Laravel, в мой проект.Все отлично работает на моей машине разработки (это http), но когда я обновляю свой рабочий сервер (который https) со всеми изменениями, то когда laravel отправляет мне электронное письмо со ссылкой (подписанный маршрут), сгенерированной для меня, чтобы нажать кнопку или вставитьв моем браузере laravel, похоже, не может проверить созданную им подпись.Побочный эффект - каждый раз, когда я нажимаю кнопку или вставляю ссылку в браузер, я получаю сообщение об ошибке:

403 Извините, у вас нет прав на доступ к этой странице.

Что я отследилДо сих пор я нашел код в классе ValidateSignature.php laravel и добавил несколько сообщений журнала.

public function handle($request, Closure $next)
{
    Log::info('checking signature');
    if ($request->hasValidSignature()) {
        Log::info('signature is valid');
        return $next($request);
    }

    Log::info('throwing InvalidSignatureException');
    throw new InvalidSignatureException;
}

А более конкретно, я проследил точную проблему внутри модуля laravel UrlGenerator.php. Я добавил журналы вследующий метод:

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

    $expires = Arr::get($request->query(), 'expires');

    $signature = hash_hmac('sha256', $original, call_user_func($this->keyResolver));

    Log::info('url: '.$original);
    Log::info('expire: '.$expires);
    Log::info(' new signature: '.$signature);
    Log::info('link signature: '.$request->query('signature', ''));
    Log::info('hash equals: '.hash_equals($signature, $request->query('signature', '')));
    Log::info('expired: '.!($expires && Carbon::now()->getTimestamp() > $expires));

    return  hash_equals($signature, $request->query('signature', '')) &&
           ! ($expires && Carbon::now()->getTimestamp() > $expires);
}

Когда я нажимаю кнопку или вставляю ссылку в браузере и нажимаю ввод, я получаю следующие сообщения журнала: (Я сменил реальный домен по понятным причинам .... не пытайтесь продать свойсайт или что-то еще)

checking signature
url: http://www.example.com/email/verify/2?expires=1538012234
expire: 1538012234
new signature: 1326b9e7402a51e0f05ddf1cb14f1e14852b4c5f0d1d6e726554806e7d85b4b1
link signature: e1d3ad5dc88faa8d8b0e6890ef60e216b75d26ef7ed5c6ab1cc661548e0ad8df
hash equals:
expired: 1
throwing InvalidSignatureException

Так что я не знаю, есть ли ошибка в логике, когда laravel создает начальную подпись или когда она пытается ее проверить.Однако, как я уже сказал, все отлично работает на моей машине для разработки.Я очистил кеш, очистил маршруты, обновил до последнего кода, перезагрузил сервер, все, что я могу придумать.Любая помощь будет принята с благодарностью.

**** ОБНОВЛЕНИЕ *****

Я вырыл немного глубже и сузил проблему.Я не могу поверить, что я не видел это прошлой ночью.Если мы внимательно посмотрим на выходные журналы, перечисленные выше, то одно сообщение журнала

url: http://www.example.com/email/verify/2?expires=1538012234

показывает нам проблему.Итак, как я уже говорил, моей машиной для разработки является http, а моим живым сервером - https.Сегодня утром (после хорошего 4-часового сна) я вижу, что журнал показывает нам, что каким-то образом логика метода hasValidSignature () получает маршрут с http вместо https.Поэтому, когда я возвращаюсь к своей электронной почте, ссылка в электронной почте - https, если я вставляю URL-адрес в своем браузере, он имеет https, а в моем браузере после этой логики возвращает ошибку 403, браузер по-прежнему показывает https.Итак, теперь мы можем сосредоточиться на том, как мой маршрут / URL конвертируется в http?Я действительно изо всех сил здесь, потому что я понятия не имею, как этот URL обрабатывается так или иначе, так как / email / verify даже не указан ни в одном из моих файлов маршрутов (о которых я знаю), и я не могу сказать, что понимаю, что искать вкапот для этого либо, поэтому я действительно надеюсь на некоторую помощь здесь.

Также вот настройки в моем файле .env:

APP_USE_HTTPS=true
APP_URL=https://www.example.com
APP_ENV=production

И в методе загрузки AppServiceProvider Iиметь

public function boot()
{
    Schema::defaultStringLength(191);

    if (env('APP_USE_HTTPS'))
    {
        Log::info('forcing URLs to use https');
        \URL::forceScheme('https');
    }

1 Ответ

0 голосов
/ 23 апреля 2019

Если у вас есть приложение Laravel за прокси-сервером Apache, это также происходит.В нашем случае у нас более или менее такая же конфигурация .env, и у нас также есть

URL::forceScheme('https'); 

в нашем AppServiceProvider.

Это создает следующие URL: при подписании подписи: https: //..../email/verify/174? expires = 1556027661 При проверке подписи: http: //..../email/verify/174

наш обходной путь - заменить «подписанное» промежуточное ПО: в app / Http / Kernel.php используйте 'signed' => \App\Http\Middleware\ValidateHttpsSignature::class, а затем создайте этот класс со следующим кодом:

namespace App\Http\Middleware;

use Closure;
use Illuminate\Routing\Exceptions\InvalidSignatureException;
use Illuminate\Http\Request;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\App;
use Illuminate\Support\Carbon;

class ValidateHttpsSignature
{
    var $keyResolver;

    public function __construct()
    {
        $this->keyResolver = function () {
            return App::make('config')->get('app.key');
        };
    }

    /**
     * gebaseerd op vendor/laravel/framework/src/Illuminate/Routing/Middleware/ValidateSignature.php
     * maar zorgt er voor dat een url altijd als https behandeld wordt. dit fixt het feit dat
     * laravel achter een rewrite proxy draait en urls binnenkrijgt als http.
     *
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */
    public function handle($request, Closure $next)
    {
        if ($this->hasValidSignature($request)) {
            return $next($request);
        }
        throw new InvalidSignatureException;

    }

    /**
     * Determine if the given request has a valid signature.
     * copied and modified from
     * vendor/laravel/framework/src/Illuminate/Routing/UrlGenerator.php:363
     * @param  \Illuminate\Http\Request  $request
     * @param  bool  $absolute
     * @return bool
     */
    public function hasValidSignature(Request $request, $absolute = true)
    {
        $url = $absolute ? $request->url() : '/'.$request->path();

        // THE FIX:
        $url = str_replace("http://","https://", $url);

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

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

        $signature = hash_hmac('sha256', $original, call_user_func($this->keyResolver));

        return  hash_equals($signature, (string) $request->query('signature', '')) &&
            ! ($expires && Carbon::now()->getTimestamp() > $expires);
    }

}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...