つらつらと書いていますが、仕様としては、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に変更