5年ほどROM専だったけど気が向いたので投稿。
最近業務でLaravel使うこと多くなってきた。
前提
- Laravel 5.4
- PHP7.1
- AWS + CloudFront(CDN)
- Port443空いてる Port80閉じてる
解決したいこと
まーあるあるネタ
- 普通にLaravel使ってると、リダイレクト時やblade でリンク生成時、
最終的に
UrlGenerator
でURL生成してることがほとんど -
UrlGenerator
はアクセス元のスキームやホストからフルURLを生成する - Proxy環境下ではクライアントからのアクセスはHTTPSでも、エンドポイントから見るとHTTPなこと多し
- よって
UrlGenerator
がクライアント側がHTTPSでもhttpなURLを生成しちゃう
解決方法
ミドルウェアでリクエストを確認して、HTTPSなアクセスだったらスキーマを書き換える
ミドルウェア作成
app/Http/Middleware/SecureAccess.php
<?php
namespace App\Http\Middleware;
use \Symfony\Component\HttpFoundation\Request;
class SecureAccess
{
public function handle($request, \Closure $next, $guard = null)
{
$is_secure = $request->server('HTTPS') === 'on'
|| $request->server('HTTPS') === '1'
|| $request->server('SSL') === 'on'
|| $request->server('HTTP_X_FORWARDED_PROTO') === 'https'
|| $request->server('HTTP_CLOUDFRONT_FORWARDED_PROTO') === 'https'
; // ※後述1
if (! $is_secure) {
return $next($request);
}
\URL::forceScheme('https'); // ※後述2
Request::setTrustedProxies([
'0.0.0.0/0'
]); // ※後述3
return $next($request);
}
}
- 大抵のProxy環境下では
HTTP_X_FORWARDED_PROTO
でいけるはずだけど、
CloudFront経由時は更にHTTP_CLOUDFRONT_FORWARDED_PROTO
を見ないとダメだった。
HTTPSかどうかの判定は他にもあるかも。 -
UrlGenerator::forceScheme()
のエイリアス。これでUrlGenerator
が返すURLが(ほぼ)httpsになる。 - 上記2の例外の対処で一部のURLを生成する関数は、
forceScheme関係なくSymfonyのRequestオブジェクトからURLを生成している。
そやつは$_SERVER['REMOTE_ADDR']
とここでセットしたIPを判定しマッチしてればhttps、
そうでなければ$_SERVER['HTTPS']
のみを見てスキーマを返している。
(正しくIP指定しないとローカル開発時の直HTTPSアクセスでProxy経由と判定されちゃうよ☆)
ミドルウェアの登録
あとは普通にミドルウェア登録。
app/Http/Kernel.php
<?php
namespace App\Http;
use Illuminate\Foundation\Http\Kernel as HttpKernel;
class Kernel extends HttpKernel
{
protected $middleware = [
/* ~~ その他ミドルウェア ~~ */
\App\Http\Middleware\SecureAccess::class,
/* ~~ その他ミドルウェア ~~ */
];
}
所感
- forceScheme 強制スキーマ(強制とは言ってない)
- SymfonyのRequestクラスはホストは
X_ORIGINAL_URL
やらX_REWRITE_URL
etc...見て大分がんばってるのに、
スキーマだけはこういう判定方法にしてるのはなんか理由あるんかな?