つらつらと書いていますが、仕様としては、middlewareを作成後、「### 5.Middleware(ForceHttpProtocol.php)にformatSchemeをoverrideする」に飛んで頂いて構いません。
# 背景
ACMで証明書を取得後、ALBでattachし、挙動テストすると、「完全には保護されていません」と表示される
->PRDのソースを確認すると、プロトコルがhttpで出力されている
<link rel="stylesheet" href="http://${domain}/css/main.css">
ソースコードではasset()で記載
<link rel="stylesheet" href="{{ asset('css/main.css') }}">
原因は、assetが、httpsではなく、httpを読みにいっているから。
# 要件
- 商用環境でhttp通信のリクエストの場合は、https通信にリダイレクト
- ただし、ローカル環境はhttp通信
- 商用環境リンクをフォームを含めて全てhttpsに変更
## 前準備
middlewareの追加
$ php artisan make:middleware ForceHttpProtocol
app/Http/kernel.php
protected $middleware = [ //省略 \App\Http\Middleware\ForceHttpProtocol::class, ];
### 1. secure()に書き換えるやり方
app/Http/Middleware/ForceHttpProtocol.php
public function handle($request, Closure $next) { if(!$request->secure() && env('APP_ENV') === 'production'){ return redirect()->secure($request->getRequestUri()); } return $next($request); }
上記では永久リダイレクトとなり、タイムアウトになる。
### 2. ‘https://’.$_SERVER[‘HTTP_HOST’].$_SERVER[‘REQUEST_URI’]にリダイレクト
public function handle($request, Closure $next) { if(!$this->is_ssl() && config('app.env') === 'production'){ return redirect('https://'.$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI']); } return $next($request); } public function is_ssl() { if ( isset($_SERVER['HTTPS']) === true ) { return ( $_SERVER['HTTPS'] === 'on' or $_SERVER['HTTPS'] === '1' ); } elseif ( isset($_SERVER['SSL']) === true ) { return ( $_SERVER['SSL'] === 'on' ); } elseif ( isset($_SERVER['HTTP_X_FORWARDED_PROTO']) === true ) { return ( strtolower($_SERVER['HTTP_X_FORWARDED_PROTO']) === 'https' ); } elseif ( isset($_SERVER['HTTP_X_FORWARDED_PORT']) === true ) { return ( $_SERVER['HTTP_X_FORWARDED_PORT'] === '443' ); } elseif ( isset($_SERVER['SERVER_PORT']) === true ) { return ( $_SERVER['SERVER_PORT'] === '443' ); } return false; }
これでhttpリクエストの場合も、httpsにリダイレクトされるようになる。
ただし、asset、routeなどのリンクが、httpsではなく、httpを読みにいく事象は解消されないまま
->urlのhelperを確認
Illuminate/Foundation/helper.php
function url($path = null, $parameters = [], $secure = null)
URLやViewのasset, redirectを全て書き換える?
嘘だろ?
### 3. パスをassetからurlに書き換える
AppServiceProvicerで商用ならtrue, 商用以外ならfalseのメソッドを作成
app/Providers/AppServiceProvider.php
use View; public function boot() { $is_production = env('APP_ENV') === 'production' ? true: false; View::share('is_production', $is_production); }
続いて、viewで、assetをurlに変更して、上記の変数を第3パラメータに追加
resources/view/auth/login.blad.php
<form method="POST" action="{{ url('login', null, $is_production) }}">
一箇所修正して挙動確認。
URLがhttpsに変わっていることを確認
3~4ページぐらい修正を繰り返していると、早くも絶望的な気分になってくる
### 4. Laravelコレクティブ&Controllerでのhttpsパラメータの渡し方を確認
laravel collective
{!! Form::open(['method'=>'POST', 'action'=>['HogeController@confirm',null,$is_production] ]) !!}
controller
protected $is_production; public function __construct(){ $this->is_production = env('APP_ENV') === 'production' ? true: false; } // 戻るボタン処理 if($action == '戻る'){ return redirect()->action('HogeController@create', 302, null, $this->is_production)->withInput($inputs); }
うまくいかない。
縷々調査すると、ssetやrouteのプロトコルはformatSchemeで生成しているとのこと。
Illuminate/Routing/UrlGenerator.php
public function formatScheme($secure = null) { if (! is_null($secure)) { return $secure ? 'https://' : 'http://'; } if (is_null($this->cachedScheme)) { $this->cachedScheme = $this->forceScheme ?: $this->request->getScheme().'://'; } return $this->cachedScheme; }
### 5.Middleware(ForceHttpProtocol.php)にformatSchemeをoverrideする
use URL; public function handle($request, Closure $next) { if(!$this->is_ssl() && config('app.env') === 'production'){ return redirect('https://'.$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI']); } if (env('APP_ENV') === 'production') { URL::forceScheme('https'); } return $next($request); } public function is_ssl() { if ( isset($_SERVER['HTTPS']) === true ) { return ( $_SERVER['HTTPS'] === 'on' or $_SERVER['HTTPS'] === '1' ); } elseif ( isset($_SERVER['SSL']) === true ) { return ( $_SERVER['SSL'] === 'on' ); } elseif ( isset($_SERVER['HTTP_X_FORWARDED_PROTO']) === true ) { return ( strtolower($_SERVER['HTTP_X_FORWARDED_PROTO']) === 'https' ); } elseif ( isset($_SERVER['HTTP_X_FORWARDED_PORT']) === true ) { return ( $_SERVER['HTTP_X_FORWARDED_PORT'] === '443' ); } elseif ( isset($_SERVER['SERVER_PORT']) === true ) { return ( $_SERVER['SERVER_PORT'] === '443' ); } return false; }
このように書くことで、要件を満たすことができる
- 商用環境でhttp通信のリクエストの場合は、https通信にリダイレクト
- ただし、ローカル環境はhttp通信
- 商用環境リンクをフォームを含めて全てhttpsに変更