20200728のPHPに関する記事は12件です。

PHPデバッグが捗るTips

この記事について

PHPのデバッグで「var_dump($test);exit;」しかしない人のために
手動デバッグで役に立ったことをまとめてみました。

デバッグおすすめリスト

やりたいこと デバッグ方法 役立つ度
配列をコピペで変数代入できる状態で出力する 「var_dump()」ではなく「var_export()」を使用する ★★☆
var_dumpの「string(5)」みたいな出力をなくす 「print_r()」で出力する ★☆☆
関数の呼び出し元の関数を特定する echo debug_backtrace()[1]['function']; ★★★
呼び出し元のクラス名を特定する echo debug_backtrace()[1]['class']; ★★★
ログを指定したファイルに出力する error_logの第2引数に「3」
第3引数に出力させたいログパスを記載する
error_log($message, 3, '/var/log/php/test_log');
★★★
var_dumpした値の文字化けを解消 chromeのテキストエンコーディングを入れる
https://chrome.google.com/webstore/detail/set-character-encoding/bpojelgakakmcfmjfilgdlmhefphglae?hl=ja

もしくは
header('Content-Type: text/html; charset=UTF-8');<br>var_dump(配列);
★☆☆
var_dump()を見やすく出力する echo '<pre>';var_dump($array); ★★★
MySQLのエラーを表示する echo mysql_error(); ★☆☆
「echo 111;exit;」を簡潔に書く(Laravel/CakePHPの場合) dd(111); ★★☆
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

React + Laravel 開発時のVS Code拡張まとめ

どちらにでも使える拡張

Bracket Pair Colorizer

メソッドや関数内で、ペアとなる括弧を色付けしてくれる拡張。どの関数内でどの処理が実行されるのか一目瞭然なので重宝しています。

インストールサイトから引用
image

Code Spell Checker

英語の表記誤りを指摘してくれる。キャメルケースやスネークケースにも対応。
ただ、サードパーティのメソッドもチェックしてくれてしまうので、うっかり直さないように注意。

インストールサイトから引用
image

Regex Previewer

正規表現のプレビュー用拡張です。JSでもphpでも正規表現はよく使うので重宝しています。

インストールサイトから引用
image

Todo Tree

コメントで

//TODO

としたものを一覧で表示してくれます。プロジェクトのコード全体のTODOを見渡したい時にとても便利です。

インストールサイトから引用
image

JS/Reactに関する拡張

Auto Rename Tag

自動で対応する閉じタグを作ってくれ、その上、タグを変更した時に閉じタグの名前も変更してくれる拡張機能。
Auto Close Tagという同じ人が作った拡張もあるが、こちらの方が上位互換という印象。

インストールサイトから引用
image

ESLint

コードを自動整形してくれる。プロジェクト内でのReactのコードを統一するために使っています。

JS Refactor

JSをリファクタリングする時に使用できる。変数名などを一括して変えたい時にとても便利。

TypeScript Import

TypeScriptimportを自動で保管してくれます。Reactを書いている時もとても良い感じに補完してくれるので便利です。

インストールサイトから引用
image

PHPやLaravelで使える拡張

DotEnv

.envのシンタックスハイライトをしてくれる。

インストールサイトから引用
image

PHP Class Generator

Laravelのartisanコマンドで賄えないようなクラスを作る時に非常に重宝しています。ディレクトリを見てnamespaceまで自動で付与してくれる機能がとても強力です。

インストールサイトから引用
image

php cs fixer

こちらはPHPソースの自動整形に使う拡張です。LaravelはPSR2に対応しているので、開発時も同様の規約でソースを書くことができます。

PHP DocBlocker

作成したメソッドに合わせてPHPDocを自動で生成してくれます。心地よいコード補完のために、PHPDocもストレスなく書きたいですね。

PHP Intelephense

PHPのコード補完用の拡張です。非常に強力で個人的には、PHP Intelesenseよりも補完機能が良かったと感じています。

その他、Laravelに関する機能

こちらに集約しています。

最後に

VS Codeの拡張は種類も多く、どれを使ったら良いのか?と思うことが多かったのでまとめてみました。
社内のプロジェクトチーム内で話しても、いろいろな発見があったので試してみると良いと思います。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Laravelの認証(JWT)

前提条件

eclipseでLaravel開発環境を構築する。デバッグでブレークポイントをつけて止める。(WindowsもVagrantもdockerも)
本記事は上記が完了している前提で書かれています
プロジェクトの作成もapacheの設定も上記で行っています

Laravelでデータベースを扱う準備をする
Laravelでテーブル作成
Laravelで初期データ投入
Laravelでデータベースを操作する(Eloquent編)
本記事は上記ので作成したデータベースとレコード、PHPファイルを使用します

認証に必要なソース生成

コマンドラインで
cd sample
composer require tymon/jwt-auth
xdebugの設定をしているとeclipseが実行していいですかというプロンプトを出すのでOKを押します

コマンドラインで

php artisan vendor:publish --provider="Tymon\JWTAuth\Providers\LaravelServiceProvider"

eclipseプロジェクトを右クリック→リフレッシュ
/sample/config/jwt.phpが現れます

コマンドラインで
php artisan jwt:secret
eclipseプロジェクトを右クリック→リフレッシュ
/sample/.envにJWT_SECRETが記載されます

Model修正

/sample/app/Models/User.php修正
(1) use文追記
use Tymon\JWTAuth\Contracts\JWTSubject;追記
(2) implementsにJWTSubject追加
class User extends Authenticatable implements MustVerifyEmail , JWTSubject
(3) メソッド追加
public function getJWTIdentifier()
{
return $this->getKey();
}
public function getJWTCustomClaims()
{
return [];
}

auth.php修正

/sample/config/auth.php修正
guards->apiを修正

auth.php
‥‥
    'guards' => [
        ‥‥
        'api' => [
            'driver' => 'jwt',
            'provider' => 'users',
        ],
    ],
‥‥

Controller

(1) AuthController作成
コマンドラインで
cd sample
php artisan make:controller AuthController
xdebugの設定をしているとeclipseが実行していいですかというプロンプトを出すのでOKを押します
eclipseプロジェクトを右クリック→リフレッシュ
/sample/app/Http/Controllers/AuthController.phpが現れます
/sample/app/Http/Controllers/AuthController.phpを下記に修正します

AuthController.php
<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

class AuthController extends Controller
{
    public function __construct()
    {
        $this->middleware('auth:api', ['except' => ['login']]);
    }

    public function login()
    {
        $credentials = request(['email', 'password']);

        if (! $token = auth('api')->attempt($credentials)) {
            return response()->json(['error' => 'メールアドレス、またはパスワードが間違っています'], 401);
        }

        return $this->respondWithToken($token);
    }

    public function me()
    {
        return response()->json(auth('api')->user());
    }

    public function logout()
    {
        auth('api')->logout();

        return response()->json(['message' => 'Successfully logged out']);
    }

    public function refresh()
    {
        return $this->respondWithToken(auth('api')->refresh());
    }

    protected function respondWithToken($token)
    {
        return response()->json([
            'access_token' => $token,
            'token_type' => 'bearer',
            'expires_in' => auth('api')->factory()->getTTL() * 60
        ]);
    }
}

jwt-authの公式ドキュメントとほとんど同じですが、違っている箇所があります。
auth()関数に引数'api'を渡しています
jwt-authの公式ドキュメントではauth.phpのdefaultsのguardをapiにしているので、auth()に引数を渡さなくてもjwtのguardが取れますが、今回はdefaultsのguardはwebのままにしますので、auth()関数に引数'api'を渡すようにします
また、コンストラクタでmiddlewareを指定しています。middlewareは/sample/routes/web.phpや/sample/routes/api.phpの中でRoute::getやRoute::postの戻り値に対して指定することもできますが、コンストラクタで指定することもできます。この場合$this->middleware('auth:api', ['except' => ['login']]);なので、loginメソッドを除いて、メソッドにリクエストが来るとguard->apiの認証が必要になります

(2) SampleController.php修正
/sample/app/Http/Controllers/SampleController.php修正
api1メソッド、api2メソッド追加

SampleController.php
    public function api1()
    {
        return view('sample.api');
    }

    public function api2(Request $request)
    {
        $data = [
            'a' => 'a_value',
            'b' => 'b_value',
            'c' => $request->input('c')
        ];
        return $data;
    }

(3) /sample/routes/api.php修正
下記を追記

api.php
    Route::post('auth/login', 'AuthController@login');
    Route::post('auth/logout', 'AuthController@logout');
    Route::post('auth/refresh', 'AuthController@refresh');
    Route::post('auth/me', 'AuthController@me');
    Route::post('sample/api2', 'SampleController@api2')->middleware('auth:api');

/sample/routes/api.phpにルーティングを書いた場合、app/Providers/RouteServiceProvider.php#mapApiRoutesによって自動的に/apiがURLの先頭につきます
AuthControllerの各メソッドはAuthControllerのコンストラクタ内で認証の要、不要が設定されていますが、SampleControllerのapi2はここで認証が必要なようにmiddlewareを付けました

(4) /sample/routes/web.php修正
下記を追記
Route::get('sample/api1', 'SampleController@api1');

要ログインURLへ未認証ユーザーがアクセスした場合のエラーメッセージ修正

/sample/app/Http/Middleware/Authenticate.php修正
unauthenticatedメソッド追加

    protected function unauthenticated($request, array $guards)
    {
        throw new AuthenticationException(
            'ログインしてください', $guards, $this->redirectTo($request)
            );
    }

view作成

/sample/resources/views/sample/api.blade.php

api.blade.php
<html>
    <head>
        <title>sample</title>
        <script type="text/javascript">

            function login(){

                var form = document.getElementById("sampleForm1");

                var reqData = {"email": null, "password": null};
                reqData.email = form.email.value;
                reqData.password = form.password.value;

                var req = new XMLHttpRequest();
                req.open(form.method, form.action);
                req.setRequestHeader('Content-Type', 'application/json; charset=utf-8');
                req.responseType = 'json';
                req.send(JSON.stringify(reqData));
                req.onload = function () {
                    var json = req.response;
                    if (json['error']) {
                        alert("error:" + json['error']);
                    } else {
                        localStorage.setItem('access_token', json['access_token']);
                        alert('ログイン');
                    }
                }
            }
            function ajax(){

                var form = document.getElementById("sampleForm2");

                var reqData = {"c": null};
                reqData.c = form.c.value;

                var req = new XMLHttpRequest();
                req.open(form.method, form.action);
                req.setRequestHeader('Authorization', 'Bearer ' + localStorage.getItem('access_token'));
                req.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
                req.setRequestHeader('Accept', 'application/json');
                req.setRequestHeader('Content-Type', 'application/json; charset=utf-8');
                req.responseType = 'json';
                req.send(JSON.stringify(reqData));
                req.onload = function () {
                    var json = req.response;
                    if (json['message']) {
                        alert("message:" + json['message']);
                    } else {
                        alert("a:" + json['a']  + "\n" + "b:" + json['b']+ "\n" + "c:" + json['c']);
                    }
                }
            }
            function logout(){

                var form = document.getElementById("sampleForm3");

                var req = new XMLHttpRequest();
                req.open(form.method, form.action);
                req.setRequestHeader('Authorization', 'Bearer ' + localStorage.getItem('access_token'));
                req.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
                req.setRequestHeader('Accept', 'application/json');
                req.setRequestHeader('Content-Type', 'application/json; charset=utf-8');
                req.responseType = 'json';
                req.send();
                req.onload = function () {
                    var json = req.response;
                    alert("message:" + json['message']);
                }
            }
        </script>
    </head>
    <body>

        <form id="sampleForm1" action="{{ url('api/auth/login') }}" method="post"  onsubmit="login(); return false;" style="border: #000000 solid 1px; padding: 10px;">
            <div>email:<input type="text" name="email" ></div>
            <div>password:<input type="password" name="password" ></div>
            <input type="submit" value="ログイン">
        </form>

        <br>
        <br>

        <form id="sampleForm2" action="{{ url('api/sample/api2') }}" method="post"  onsubmit="ajax(); return false;" style="border: #000000 solid 1px; padding: 10px;">
            <input type="text" name="c" >
            <input type="submit" value="要認証リクエスト">
        </form>

        <br>
        <br>

        <form id="sampleForm3" action="{{ url('api/auth/logout') }}" method="post"  onsubmit="logout(); return false;" style="border: #000000 solid 1px; padding: 10px;">
            <input type="submit" value="ログアウト">
        </form>

    </body>
</html>

動作確認

http://localhost/laravelSample/sample/api1
a.png
・要認証リクエストの左にあるテキストフィールドに何か入力し、要認証リクエストボタンをクリックします
ログインしてくださいとalertがでました
・メールアドレスとパスワードを入力してログインボタンをクリックします
ログインできました
・要認証リクエストの左にあるテキストフィールドに何か入力し、要認証リクエストボタンをクリックします
SampleController.php#api2のレスポンスが表示されました
・ログアウトボタンをクリックします
ログアウトしました

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

PHPでアクセスされているURLを取得する方法

現在のURLを取得する方法です。
必要なのは【プロトコル】【ホスト】【パス】の3つで、それぞれを組み合わせてURLを表示させます。

【プロトコル】 (HTTP or HTTPS)の判定

HTTPでのアクセスか、HTTPSでのアクセスなのかを判定。

echo $_SERVER['HTTPS'];

実行結果

'on'

HTTPSでのアクセスの場合は'on'
HTTPでのアクセスの場合はNULLとなります。

アクセスされているページの【プロトコル】を取得

httpやhttpsなどのプロトコルを取得します。
HTTPSではなかったら、$_SERVERのHTTPSは空になりhttpを表示、そうではなかったらhttpsを表示。

echo (empty($_SERVER['HTTPS']) ? 'http://' : 'https://')

実行結果

'https://'

アクセスされているページの【ホスト】を取得

ホスト名(ドメイン部分)を取得します。

echo $_SERVER['HTTP_HOST'];

実行結果

'localhost'

アクセスされているページの【パス】を取得

httpやhttpsなどのプロトコル名やサーバーのドメイン名(ホスト名)を除いた、ページURLを取得します。

echo $_SERVER['REQUEST_URI'];

実行結果

'/index.php'

実際に現在のURLを取得する!

上記で書いた【プロトコル】【ホスト】【パス】がの組み合わせが現在のURLになるため、下記のように書くことで、URLを取得することができます。

echo (empty($_SERVER['HTTPS']) ? 'http://' : 'https://') . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];

実行結果

'http://localhost/index.php'

まとめ

複数ページのSNSシェア設定部分のテンプレ化をしたくてURLの取得をしてみました。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

PHPのセキュリティ関係

この記事は以下の書籍のアウトプットとして執筆しました。

Webアプリケーションの入力は何をするか

入力処理では入力値に対して以下の処理を行う

a. 文字エンコーディングの妥当性検証
→文字エンコーディングを使った攻撃対策ため

b. 文字エンコーディングの変換
→HTTPメッセージとプログラム内部でも自演コーディグが異なるため
c. 入力値の妥当性検証
→セキュリティ上の保険的な対策

文字エンコーディングの検証

PHPではmb_check_encoding関数が使える

文字エンコーディングの変換

PHPはphp.iniでの設定で自動変換と明示的な返還を選べる。

入力値検証

バイナリセーフとヌルバイト攻撃

バイナリセーフ

入力値がどんなバイト列であっても正しく扱えることを意味する。
典型亭には値ゼロのバイト(ヌルバイト、PHP言語では\0と表記)が現れても正しく処理することを指す。

ヌルバイトが特別扱いされるのはC言語及びUnixやウィンドウズのAPIでは、ヌルバイトを文字列の終端とみなす取り決めがある。

C言語で開発されたPHPは塗るバイトを文字列の終端としてそれ以降を切り詰める関数がある。
このような関数をバイナリセーフでない関数という。

eregの検査を回避

この関数は PHP 5.3.0 で 非推奨 となり、 PHP 7.0.0 で 削除 されました。

<body>
<?php
  $p = $_GET['p'];
  if (ereg('^[0-9]+$', $p) === FALSE) {
    die('整数値を入力してください');
  }
  echo $p;
?>
</body>

出典:体系的に学ぶ 安全なWebアプリケーションの作り方 第2版[固定版] 脆弱性が生まれる原理と対策の実践より

このコードに対して以下のようにアクセスするとeregを回避される。
?p=1%00<script>alert('XSS')</script>

回避される理由

URLの中に%00がある。これは値ゼロのバイトすなわちヌルバイトでereg関数はバイナリセーフの関数ではないため、塗るバイトがあるとそこで文字列が終わっていると判断してしまう。

ヌルバイト対策はバイナリセーフの関数のみを用いてアプリを開発すればいいが、それは困難

ファイル名のように仕様上ヌルバイトを許容しないパラメータがあるから。

このため、アプリの入り口で倍ナチセーフを使って入力値の塗るバイトをチェックして、塗るバイトがあればエラーにする方法がある

制御文字のチェック

制御文字とは改行やタブ等通常表示されない文字のことでヌルバイトも含まれる。

文字数のチェック

すべてのパラメータで最大文字数をチェックスべき。
これをチェックしておくとセキュリティ上で保険になる。
攻撃は文字列が長いことが多いため。

PHPの正規表現

入力値検証には正規表現が便利。

関数 説明
perg 文字エンコーディングがUTF-8の場合のみ日本語が使える
mb_ereg 様々な文字エンコーディングに使える

1文字以上5文字以下の英数字をチェックする正規表現

preg_match('/A[a-z0-9]{1,5}\z/ui',$p);

出典:体系的に学ぶ 安全なWebアプリケーションの作り方 第2版[固定版] 脆弱性が生まれる原理と対策の実践より

名前 説明
u修飾子 UTF-8エンコーディングであることを示す。
i修飾子 大文字小文字を区別しない

u修飾子は不要の場合があるが。正規表現の「.」なドアが意図しない結果になるため、常にu修飾子を指定しておくほうがいい

全体一致は\Aと\zで示す

データの先頭は\Aデータの末尾は\zで示す。
^$は「行の」先頭と末尾を示すものだから$は改行にマッチする。
このため^$をデータの先頭・末尾として使うと不具合が生じる場合がある。

参考文献

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

入力処理とセキュリティ

書籍のアウトプットとして

Webアプリケーションの入力は何をするか

入力処理では入力値に対して以下の処理を行う

a. 文字エンコーディングの妥当性検証
→文字エンコーディングを使った攻撃対策ため

b. 文字エンコーディングの変換
→HTTPメッセージとプログラム内部でも自演コーディグが異なるため
c. 入力値の妥当性検証
→セキュリティ上の保険的な対策

文字エンコーディングの検証

PHPではmb_check_dncoding関数が使える

構文
mb_check_encoding ([ mixed $var = NULL [, string $encoding = mb_internal_encoding() ]] ) : bool

文字エンコーディングの変換

PHPはphp.iniでの設定で自動変換と明示的な返還を選べる。

入力値検証

バイナリセーフとヌルバイト攻撃

バイナリセーフ

入力値がどんなバイト列であっても正しく扱えることを意味する。
典型亭には値ゼロのバイト(ヌルバイト、PHP言語では\0と表記)が現れても正しく処理することを指す。

ヌルバイトが特別扱いされるのはC言語及びUnixやウィンドウズのAPIでは、ヌルバイトを文字列の終端とみなす取り決めがある。

C言語で開発されたPHPは塗るバイトを文字列の終端としてそれ以降を切り詰める関数がある。
このような関数をバイナリセーフでない関数という。

eregの検査を回避

この関数は PHP 5.3.0 で 非推奨 となり、 PHP 7.0.0 で 削除 されました。

<body>
<?php
  $p = $_GET['p'];
  if (ereg('^[0-9]+$', $p) === FALSE) {
    die('整数値を入力してください');
  }
  echo $p;
?>
</body>

このコードに対して以下のようにアクセスするとeregを回避される。
?p=1%00<script>alert('XSS')</script>

回避される理由

URLの中に%00がある。これは値ゼロのバイトすなわちヌルバイトでereg関数はバイナリセーフの関数ではないため、塗るバイトがあるとそこで文字列が終わっていると判断してしまう。

ヌルバイト対策はバイナリセーフの関数のみを用いてアプリを開発すればいいが、それは困難

ファイル名のように仕様上ヌルバイトを許容しないパラメータがあるから。

このため、アプリの入り口で倍ナチセーフを使って入力値の塗るバイトをチェックして、塗るバイトがあればエラーにする方法がある

制御文字のチェック

制御文字とは改行やタブ等通常表示されない文字のことでヌルバイトも含まれる。

文字数のチェック

すべてのパラメータで最大文字数をチェックスべき。
これをチェックしておくとセキュリティ上で保険になる。
攻撃は文字列が長いことが多いため。

PHPの正規表現

入力値検証には正規表現が便利。

関数 説明
perg 文字エンコーディングがUTF-8の場合のみ日本語が使える
mb_ereg 様々な文字エンコーディングに使える

1文字以上5文字以下の英数字をチェックする正規表現

preg_match('/A[a-z0-9]{1,5}\z/ui',$p);
名前 説明
u修飾子 UTF-8エンコーディングであることを示す。
i修飾子 大文字小文字を区別しない

u修飾子は不要の場合があるが。正規表現の「.」なドアが意図しない結果になるため、常にu修飾子を指定しておくほうがいい

全体一致は\Aと\zで示す

データの先頭は\Aデータの末尾は\zで示す。
^$は「行の」先頭と末尾を示すものだから$は改行にマッチする。
このため^$をデータの先頭・末尾として使うと不具合が生じる場合がある。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

CORS

この記事は以下の書籍を参考にして執筆しました。

CORS

異なるオリジンとの通信を可能にする。

Access-Control-Allow-Origin

クロスオリジンからの読み出しを居するための仕掛け。
アクセスされる側がHTTPレスポンスヘッダとして出力する。

シンプルなリクエスト

特定の条件を見たすシンプルなリクエストの場合、XMLHttpRequestで異なるオリジンにHTTPリクエストを送ることが許可になしに可能。

http://example.jsからhttp://example.comへのXMLHttptRequestを許可する場合は
http://example.comが以下のようにHTTPレスポンスヘッダを送信する。

Access-Control-Allow-Origin:http://example.js

出典:体系的に学ぶ 安全なWebアプリケーションの作り方 第2版[リフロー版] 脆弱性が生まれる原理と対策の実践

プリフライトリクエスト

シンプルナリクエストでないクロスサイトオリジンアクセスでは、ブラウザはプリフライトリクエストというHTTPリクエストを送信する。

このリクエストを受けたサーバは以下のヘッダに応答する必要がある。

揚州の種類 リクエスト レスポンス
メソッドに対する許可 Access-Control-Request-Method Access-Control-Allow-Method
ヘッダに対する許可 Access-Control-Request-Headers Access-Control-Allow-Headers
オリジンに対する許可 Orijin Access-Control-Allow-Orijin

プリフライトリクエストの受け取り

if ($_SERVER['REQUEST_METHOD'] === "OPTIONS") {
  //プリフライトリクエスト受取時の処理
}elseif ($_SERVER['REQUEST_METHOD'] === "POST") {
  //ポスト処理
}

出典:体系的に学ぶ 安全なWebアプリケーションの作り方 第2版[リフロー版] 脆弱性が生まれる原理と対策の実践

プリフラを受け取って、ヘッダに返答したら次は正常のPOSTが送信されるから条件付を記載する。
その際にheader('Access-Control-Allow-Origin: URL');
を忘れないように。

認証情報を含むリクエスト

デフォルトではクロスサイトオリジンに対するリクエストにはHTTP認証やクッキーなどの認証に用いられるリクエストヘッダは自動的に動診されない。

XMLHttpRequestのプロパティにwithCredentialsをセットする必要がある。

withCredentialsをセットする

req.open('GET', URL);
req.withCredentials = true;

出典:体系的に学ぶ 安全なWebアプリケーションの作り方 第2版[リフロー版] 脆弱性が生まれる原理と対策の実践

withCredentialsをtrueにするとAccess-Control-Allow-Credentials: true'というレスポンスヘッダを返す必要がある。

まとめ

クッキー等認証用ヘッダを用いるクロスオリジンリクエストは書きを満たす必要がある。

  • withCredentialsをtrueにする
  • レスポンスヘッダとしてAccess-Control-Allow-Credentials: trueを返す

参考文献

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

php用のgRPCソースを生成するDockerfile

gRPCのプラグインをbrew等で入れることができなかったので、Dockerコマンド化しました.

ディレクトリ構成

├── Dockerfile
├── protos/
│   └── sample.proto
└── gen/
  • protos: protoファイルを置いておくディレクトリ
  • gen: 生成されたコードが配置されるディレクトリ

Dockerfile

FROM php:7.2.32-cli

WORKDIR /tmp
RUN apt-get update -y && apt-get install -y wget zip git automake zlib1g-dev libtool

# install protoc command
# releases: https://github.com/protocolbuffers/protobuf/releases
ENV proto_version 3.12.3
RUN wget https://github.com/protocolbuffers/protobuf/releases/download/v${proto_version}/protoc-${proto_version}-linux-x86_64.zip \
    && unzip -d protoc protoc-${proto_version}-linux-x86_64.zip \
    && mv protoc/bin/* /usr/local/bin/ \
    && mv protoc/include/* /usr/local/include/

# install grpc php plugin
# releases: https://github.com/grpc/grpc/releases
ENV grpc_version v1.30.2
WORKDIR /var/local
RUN git clone -b ${grpc_version} https://github.com/grpc/grpc --depth=1
WORKDIR /var/local/grpc
RUN git submodule update --init \
    && make grpc_php_plugin \
    && cp -p ./bins/opt/grpc_php_plugin /usr/local/bin/

WORKDIR /var/local
RUN mkdir protos \
    && mkdir gen

ENTRYPOINT ["protoc", "--proto_path=/var/local/protos", "--php_out=/var/local/gen", "--grpc_out=/var/local/gen", "--plugin=protoc-gen-grpc=/usr/local/bin/grpc_php_plugin"]
CMD ["hello.proto"]

コード生成コマンド

  1. コンテナの中にボリュームマウントでprotoファイルを埋め込む
  2. コンテナ内でソースが生成される
  3. コンテナ内に生成されたファイルをボリュームマウントでローカルにコピーする

の流れです。

$ docker build -t grpc-php-gen -f docker/grpc-php-gen/Dockerfile .
$ docker run --rm -v $(PWD)/protos:/var/local/protos -v $(PWD)/gen:/var/local/gen grpc-php-gen sample.proto
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

受動的攻撃と同一オリジンポリシー

この記事は以下の書籍を参考にして執筆しました。

ブラウザ側のセキュリティ

サンドボックス

サンドボックスの中ではプログラムができることが制限されいる。

  • ローカルファイルへのアクセス禁止
  • プリンタなどの資源の利用禁止(画面表示は可能)
  • ネットワークアクセスの制限(同一オリジンポリシー)

同一オリジンポリシー

サイトをまたがったアクセスを禁止する

XMLHttpRequestでは相手の許可があれば同一オリジンでなくても通信できるCORSという規格が策定された。

アプリケーション脆弱性と受動的攻撃

XSS

iframeの外側のjavascriptで内側をアクセスすると同一オリジンポリシー違反だが
何らかの方法でiframeの内側にjavascriptを送り込んで実行させたらどうか

内側ならポリシー違反にならないため、アクセスができる。
これがクロス・サイトスクリプティング(XSS)

参考文献

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【PHP】クッキーとセッション管理

この記事は以下の書籍を参考にして執筆しました。

クッキーとセッション管理

クッキー

クッキーはサーバ側からブラウザ側に「名前=変数」の組を覚えておくように指示する。

クッキーの発行

session_start()

出典:体系的に学ぶ 安全なWebアプリケーションの作り方 第2版[リフロー版] 脆弱性が生まれる原理と対策の実践

この画面を表示させるとブラウザに以下のようなレスポンスが返る。

レスポンスヘッダのSet-Cookieにより、サーバはブラウザにクッキー値を覚えるように指示する。

以降、同じサイトにリクエスト送信するときにはこの覚えたクッキーを送信する。
有効期限がないクッキーはブラウザを閉じるまで有効。

セッションID

PHPSESSIDのクッキー値のこと

このセッションIDを$session[名前]に格納しておく

クッキーは整理番号として使う

クッキーそのものに値を格納するは良くない。

セッションIDEAに求められる要件

要件1 第三者がセッションIDを推測できないこと

要件2 第三者からセッションIDを強制されないこと

要件3 第三者にセッションIDが漏洩しないこと

セッションID漏洩の原因

  • クッキー発行の際の属性に不備がある。
  • ネットワーク的に盗聴されてる。
  • クロスサイトスクリプティングなどアプリの脆弱性で漏洩
  • プラットフォームで漏洩
  • Refererヘッダから漏洩

公衆無線LANは盗聴の可能性が高い。
セッションIDをネットワーク頭頂から保護するにはTLSによる暗号化が有効だがクッキーを発行する際の属性指定に注意がいる。

クッキーの属性

先のクッキーの発行には「path=/」があったがこれも属性の1種

属性一覧
属性 意味
Domain ブラウザがクッキー値を送信するサーバのドメイン
Path ブラウザがクッキーを送信するURLのディレクトリ
Expires クッキー値の有効期限。
Secute HTTPSの場合のみクッキーを送信
HttpOnly この属性が指定されたクッキーはjavascriptからアクセスできない

Domain属性

クッキーはデフォでクッキーをセットしたサーバのみに送信される。
でも複数のサーバに送信されるクッキーを生成したい場合がある時にDomain属性が使える。

異なるドメインへのクッキー設定は出来ない

a.example.jpがSet-Cookieの際にDomain=example.comとしてもブラウザでは無視される。

通常はDomain属性を指定しなものとおぼえておくといい

Secure属性

SecureをつけていないクッキーはHTTPS通信かどうかに関係なく常にサーバに送信される。
この属性はHTTPS送信を保証する目的で使用される。

HttpOnly属性

クッキーとして格納されたセッションIDを盗み出す典型としてクロスサイトスクリプティングでjavascriptで抜き出すがこの属性を指定すればjavascriptが使えなくなる。
完全には防げないが難しくできる。

これをつけていても悪影響はないためHttpOnly属性を杖kておくように知ればいい。

HttpOnly属性をつけるにはphp.iniで設定をする。

php.ini
session.cookie_http_only = On

出典:体系的に学ぶ 安全なWebアプリケーションの作り方 第2版[リフロー版] 脆弱性が生まれる原理と対策の実践

まとめ

  • セッションの発行はsession_start()でできる。これをするとブラウザのレスポンスとしてSet-Cookieが設定される。
  • セッション発行には属性がある。とりあえずDomain属性はつけずHttpOnly属性を設定しておくようにすればいい

参考文献

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

phpのフレームワークLaravelを触ってみた。

環境構築

※ローカル上で環境構築可能ですが、今回はdocker上で動く様にしてみました。

dockerのディレクトリ、ファイル構成

.(任意の作業ディレクトリ)
├── docker
│   ├── php
│   │   └── Dockerfile
│   └── web
│       └── default.conf
├── docker-compose.yml
├── index.html
├── index.php

phpモジュールのビルド

  • build
$ docker-compose build

Laravelのプロジェクト新規作成

以下コマンドを実行する。

$ cd (任意の作業ディレクトリ)
$ laravel new sample-larabel

※プロジェクト作成後のディレクトリ構成

.(任意の作業ディレクトリ)
├── docker
│   ├── php
│   │   └── Dockerfile
│   └── web
│       └── default.conf
├── docker-compose.yml
├── index.html
├── index.php
├── sample-larabel

Nginxの設定ファイル(default.conf)の修正

※rootディレクトリの修正

server {
    listen 80;

    root /var/www/html/my-laravel-app/public;
    index index.php index.html index.htm;

    access_log /var/log/nginx/access.log;
    error_log  /var/log/nginx/error.log;

    location / {
        try_files $uri $uri/ /index.php$is_args$args;
    }
    location ~ \.php$ {
        fastcgi_split_path_info ^(.+\.php)(/.+)$;
        fastcgi_pass app:9000;
        fastcgi_index index.php;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param PATH_INFO $fastcgi_path_info;
    }
}

コンテナ起動及び動作確認

  • コンテナ起動
$ docker-compose up -d
  • 起動確認
$ docker-compose ps

※この様に表示されればOK。

             Name                           Command              State                 Ports              
----------------------------------------------------------------------------------------------------------
docker-compose-laravel_app_1     docker-php-entrypoint php-fpm   Up      9000/tcp                         
docker-compose-laravel_mysql_1   docker-entrypoint.sh mysqld     Up      0.0.0.0:3306->3306/tcp, 33060/tcp
docker-compose-laravel_web_1     nginx -g daemon off;            Up      0.0.0.0:8000->80/tcp ```

画面表示確認

新規ページ作成

  • 後日アップします。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

phpと.datファイルのか

ファイルハンドルに test.dat ファイルをひも付け、ファイルオープン。値を取り出して、インクリメントして閉じる式。
スクリーンショット 2020-07-28 6.25.47.png

これと、test.dat ファイルに、0と入力し、FTPソフトでアップロード。
ファイルパーミッションを、php=705,dat=706にセットして完成。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む