「composer install」と「composer update」、「composer.json」と「composer.lock」の違い

CakePHPではcomposer使いますよね。

初心者で何かよくわからず使っている方、多いんじゃないかと思ってます。

「composer install」と「composer update」の違いがよくわからない…とかあるあるです。

なんかこの違いを間違えただけでシステムが動かなくなる…とか聞いたことあって、勝手に怯えてました。

なのでcomposerは大事です。

【自分の環境】
macOS Catalina
PHP7.4.2
CakePHP3.8
MAMP5.7
Apache2.2
MySQL5.7

「composer install」と「composer update」の違いをざっくり

ざっくりですが、以下の違いです。

「composer install」をする

「composer.json」は無視され、「composer.lock」の内容を元にライブラリがインストールされます。

この時「composer.lock」はもちろん内容を参照されますが、更新はされません。

「composer update」をする

「composer.json」の内容を元にライブラリがインストールまたはアップデートされます。

またその結果で「composer.lock」の内容が更新されます。

「composer install」と「composer update」の使いどころの違い

composer installは0から環境を作りたいときだと思います。

他人の環境を持ってきたい、別のプロジェクトでも読み込ませたい…などでしょうか。

composer updateはライブラリを追加したいときだと思います。

何かを作っている途中に使われますね。

ここで疑問、「composer.json」と「composer.lock」の違いは?

そういう疑問が生まれると思います。

違いをざっくりですが、以下にまとめました。

json
・自分で記述して作成
・1回目のcomposer installをするとlockを生成
・composer updateでこの内容を元にライブラリをインストールやアップデート

◎lock
・1回目のcomposer installで自動作成
・composer installでこの内容を元にインストール
・composer updateでこの内容が更新

「composer.json」と「composer.lock」の違いの要点

jsonは手動で作る、lockは自動で作られる

jsonを元にlockを作成、lockはjsonを元に作られる

③「composer update」ではjsonを元に動き、lockは更新されるだけ

こんなかんじでしょうか。

終わりに

以上となります。

かなりざっくりなのですが、要点をまとめてみました。

自分も実践の機会が少なく、まだわかっていません。

間違いがあればコメントで指摘していただきたいです。

これが勉強のきっかけになれば嬉しいです。

ありがとうございました。

CakePHP3をHerokuでデプロイしたい③

前回の続きの記事となります。

(前回の記事)

CakePHP3をHerokuでデプロイしたい② - 29歳からエンジニア目指すブログ

以下のQiitaを参考にしてCakePHP3をHerokuでデプロイしていました。

CakePHP3をHerokuで動かす設定 - Qiita

しかしデプロイ後にデバッグすると、Memcachedという分散キャッシュサーバが機能してないと怒られてしまいます。

Memcachedを拡張しないといけないみたいでした。

その方法を見ていきます。

【自分の環境】
macOS Catalina
PHP7.4.2
CakePHP3.8
MAMP5.7
Apache2.2
MySQL5.7

Herokuでのデプロイ後のエラー

デプロイ後にデバッグキットを起動させると、こんなエラーが出ています。

Warning (512): Cache engine Cake\Cache\Engine\MemcachedEngine is not properly configured. [CORE/src/Cache/Cache.php, line 178]

これは翻訳すると、「MemcachedEngine が正しく設定されていない」ということです。

Memcachedをcomposerで入れる

どうやらMemcached拡張機能をcomposerで入れればいいらしいです。

composer.jsonに以下のように書き込みます。

{
    "require": {
       ...,
        "ext-memcached": "*"
    }
}

「ext-memcached」は「Memcached拡張機能」という意味ですね。

これでcomposer updateしていきます。

ところがcomposer updateが通らない

通らないですね。

ターミナルで以下のような注意をされます。

Root composer.json requires PHP extension ext-memcached * but it is missing from your system. Install or enable PHP's memcached extension.

翻訳すると「Root composer.jsonにはPHPのext-memcached拡張機能が必要ですが、あなたのシステムにはありません。PHPmemcached拡張機能をインストールするか、有効にしてください。」と出ます。

自分の解釈では、「ローカル環境にPHPmemcached拡張機能をインストールしないとダメ」ということです。

ローカル環境にPHPmemcached拡張機能をインストールしていく

それではやっていきましょう。

How to install memcached module for php@7.1 on MacOS High Sierra? - Stack Overflow

このスタックオーバーフローに救われました。

英語ですが、2本指クリックで「日本語に翻訳」をすれば全然読めます。

※homebrewが入っていない方は入れておいてください。

①ターミナルで「pecl bundle memcached」と打つ

memcachedディレクトリを作りたい場所で行います。

自分はよくわからずアプリのルートディレクトリで行っていました。

動いているのでよしとします。

②ターミナルで「cd memcached」と打つ

出力したディレクトリ(memcached)に移動します。

③ターミナルで「phpize」と打つ

ググったのですが、正直、意味はよくわかってません。

たくさん記事があるので、ググってみてください。

④ターミナルで「brew install libmemcached zlib」と打つ

libmemcachedとzlibの2つがインストールされます。

すでにインストールされていたら、「インストールされてるよ」と言われます。

⑤ターミナルで「brew list zlib」と打つ

zlibディレクトリを取得します。

以下のかんじで出てきます。

/usr/local/Cellar/zlib/1.2.11/include/ (2 files)
/usr/local/Cellar/zlib/1.2.11/lib/libz.1.2.11.dylib
/usr/local/Cellar/zlib/1.2.11/lib/pkgconfig/zlib.pc
/usr/local/Cellar/zlib/1.2.11/lib/ (3 other files)
/usr/local/Cellar/zlib/1.2.11/share/man/man3/zlib.3

これは「/usr/local/Cellar/zlib/1.2.11」が共通していますね。

これを次に使います。

⑥ターミナルで「./configure --with-zlib-dir=/usr/local/Cellar/zlib/1.2.11/」と打つ

zlibパスを前のコマンドのパスに置き換えてます。

最後の方のパスやバージョンが違ったら揃えてください。

⑦ターミナルで「make」と打つ

何かが作られます。

⑧ターミナルで「make install」と打つ

何かがインストールされます。

⑨該当のphp.iniファイルに拡張子行を追加する

該当のphp.iniファイルがわからなかったら、ターミナルに「php -i」と打って調べてください。

以下の行を探します。

Configuration File (php.ini) Path => /usr/local/etc/php/7.3

パスは自分のものなので、それぞれの環境のものが出ると思います。

そのphp.iniを開いて、以下のように書き加えます。

;;;;;;;;;;;;;;;;;;;;;;
; Dynamic Extensions ;
;;;;;;;;;;;;;;;;;;;;;;

(中略)

[memcached] // この行を追加
extension=memcached.so // この行を追加
⑩ターミナルで「heroku restart」

そしてターミナルで「heroku restart」(php.iniの変更を反映させる)してください。

これで反映されると思います。

最後にチェック、ターミナルで「php -m」

php -m」して、リストに「memcached」があれば行けてます。

これでcomposer updateができるようになります。

再びcomposer update

composer.jsonに「"ext-memcached": "*"」を書き込んだら、composer updateします。

上記をしっかりやっていれば、これで行けると思います。

ところが「1 is not a valid serializer engine for Memcached

本番画面を見ると、「1 is not a valid serializer engine for Memcached」というエラーが出るかもしれません。

またエラーか…と思いますが、app.phpで以下の記述をコメントアウトすれば大丈夫です。

'serialize' => true, // コメントアウトする行

これをコメントアウトしてください。

ちなみにapp.phpに複数(3つか4つ)あるので、全部コメントアウトしてください。

よくわかってないですが、これを消せばエラーがなくなります。

終わりに

以上となります。

だいぶ長くなりましたが、これでHerokuの本番画面がエラーなしになるはずです。

CakePHP3をHerokuで動かす設定 - Qiita

上記のQiitaの記事はとても良いのですが、ちらほらこの通りに設定してMemcachedで詰まってteratailで聞いている方がいたので、記録に残しました。

お役に立てば幸いです。

ありがとうございました。

CakePHP3をHerokuでデプロイしたい②

CakePHP3をHerokuでデプロイしたい②です。

前回の「CakePHP3をHerokuでデプロイしたい①」の続きになります。

今回はHeroku用のapp.phpの設定についてです。

こちらのQiitaが参考になりました。

CakePHP3をHerokuで動かす設定 - Qiita

とても参考になったんですが、初心者にとっては完璧ではありません。

そこを補足する形で書いていきます。

【自分の環境】
macOS Catalina
PHP7.4.2
CakePHP3.8
MAMP5.7
Apache2.2
MySQL5.7

参考にしながらapp_heroku.phpの設定…の下準備

引き続き先ほどのQiitaを参考にしながら書きます。

開発環境と違って、Heroku用のapp.phpが欲しくなってきます。

そこで環境変数 CAKE_ENV=heroku が設定されている場合だけ、app.php をオーバーライドする app_heroku.php を読み込むようにします。

まずターミナルでこのようなコマンドを打ちます。

$ heroku config:add CAKE_ENV="heroku"

これで環境変数CAKE_ENVにherokuが設定されました。

これはHerokuのSettingsの中のConfigVarsで見ることができます。

またbootstrap.phpで以下を記述します。

try {
    Configure::config('default', new PhpConfig());
    Configure::load('app', 'default', false);
} catch (\Exception $e) {
    exit($e->getMessage() . "\n");
}

/*
 * Load an environment local configuration file to provide overrides to your configuration.
 * Notice: For security reasons app_local.php will not be included in your git repo.
 */
if (isset($_ENV['CAKE_ENV'])) { // このif文を追加
    Configure::load('app_' . $_ENV['CAKE_ENV'], 'default'); // このif文を追加
}

「このif文を追加」とあるところを追加してください。

これでCAKE_ENV=herokuだった時にだけapp_heroku.phpを読み込む設定ができました。

いよいよapp_heroku.phpの設定

次にCakePHPのconfigにapp_heroku.phpを作成します。

これは最初はapp.phpを丸々っとコピーで大丈夫です。

そこから少し書き換えていきます。

以下の部分を書き換えます。

(app_heroku.php

use Cake\Mailer\Transport\MailTransport;

$db = parse_url($_SERVER['CLEARDB_DATABASE_URL']); // この行を追加

return [

(中略)

'Security' => [
        'salt' => env('SALT'), // 書き換え
    ],

(中略)

// キャッシュに関する部分を丸っと書き換え
'Cache' => [
        'default' => [
            'className' => 'Memcached',
            'prefix' => 'myapp_cake_',
            'servers' => [env('MEMCACHIER_SERVERS')],
            'username' => env('MEMCACHIER_USERNAME'),
            'password' => env('MEMCACHIER_PASSWORD'),
            'duration' => '+1440 minutes',
        ],

        'session' => [
            'className' => 'Memcached',
            'prefix' => 'myapp_cake_session_',
            'servers' => [env('MEMCACHIER_SERVERS')],
            'username' => env('MEMCACHIER_USERNAME'),
            'password' => env('MEMCACHIER_PASSWORD'),
            'duration' => '+1440 minutes',
        ],

        '_cake_core_' => [
            'className' => 'Memcached',
            'prefix' => 'myapp_cake_core_',
            'servers' => [env('MEMCACHIER_SERVERS')],
            'username' => env('MEMCACHIER_USERNAME'),
            'password' => env('MEMCACHIER_PASSWORD'),
            'duration' => '+1 years',
        ],

        '_cake_model_' => [
            'className' => 'Memcached',
            'prefix' => 'myapp_cake_model_',
            'servers' => [env('MEMCACHIER_SERVERS')],
            'username' => env('MEMCACHIER_USERNAME'),
            'password' => env('MEMCACHIER_PASSWORD'),
            'duration' => '+1 years',
        ],
    ],
// キャッシュはここまで

(中略)

// ClearDB用にもろもろ書き換え(MAMP設定の名残あり)
'Datasources' => [
        'default' => [
            'className' => Connection::class,
            'driver' => Mysql::class,
            'persistent' => false,
            'url' => env('CLEARDB_DATABASE_URL'),
            'host' => $db['host'],
            /*
             * CakePHP will use the default DB port based on the driver selected
             * MySQL on MAMP uses port 8889, MAMP users will want to uncomment
             * the following line and set the port accordingly
             */
            //'port' => '8889',
            /*
             * It is recommended to set these options through your environment or app_local.php
             */
            'username' => $db['user'],
            'password' => $db['pass'],
            'database' => substr($db['path'], 1),
            /*
             * You do not need to set this flag to use full utf-8 encoding (internal default since CakePHP 3.6).
             */
            'encoding' => 'utf8mb4',
            'timezone' => '+09:00',
            'flags' => [],
            'cacheMetadata' => true,
            'log' => false,

            /*
             * Set identifier quoting to true if you are using reserved words or
             * special characters in your table or column names. Enabling this
             * setting will result in queries built using the Query Builder having
             * identifiers quoted when creating SQL. It should be noted that this
             * decreases performance because each query needs to be traversed and
             * manipulated before being executed.
             */
            'quoteIdentifiers' => false,

            /*
             * During development, if using MySQL < 5.6, uncommenting the
             * following line could boost the speed at which schema metadata is
             * fetched from the database. It can also be set directly with the
             * mysql configuration directive 'innodb_stats_on_metadata = 0'
             * which is the recommended value in production environments
             */
            //'init' => ['SET GLOBAL innodb_stats_on_metadata = 0'],

            'url' => env('DATABASE_URL', null),
            // 'unix_socket' => '/Applications/MAMP/tmp/mysql/mysql.sock'
        ],
// DBはここまで

(中略)

// ログをファイルログからコンソールログに書き換え
'Log' => [
        'error' => [
            'className' => 'Cake\Log\Engine\ConsoleLog',
            'stream' => 'php://stderr',
            'levels' => ['warning', 'error', 'critical', 'alert', 'emergency'],
        ],
        // 以下を新しく追加
        'debug' => [
            'className' => 'Cake\Log\Engine\ConsoleLog',
            'stream' => 'php://stdout',
            'levels' => ['notice', 'info'],
        ],
// ログはここまで

(中略)

// セッションも書き換え
'Session' => [
        'defaults' => 'cache',
        'handler' => [
            'config' => 'session'
        ]
    ],
];

ちょっと長かったですが、こんなかんじでapp_heroku.phpを書き換えます。

いろいろ新出単語が出てきましたが、とりあえずはこのまま設定してみてください。

DB設定

まずClearDBのURLが出てきています。

これは前回のQiitaを踏んでいれば、勝手にClearDBの環境変数ができているので、大丈夫かと思います。

これを利用して、Datasourcesでもろもろ設定しています。

以下の記事の手順11にあたる部分をやっていますね。

PHPで作ったWebサービスをHerokuでデプロイするまでの手順書 - Qiita

SALTの設定

途中でSALTが出てきています。

これはご自身でターミナルで以下のコマンドを打ち、設定してください。

$ heroku config:add SALT="1234567890987654321"

「1234567890987654321」は適当な文字列なので、ご自身の考えた文字列に変えてください。

キャッシュの設定

キャッシュはデフォルトだとローカルファイルを使うようになっているので、Memcachedを使うようにします。

Memcachedの導入がなかなか難しいので、次回の記事「CakePHP3をHerokuでデプロイしたい③」で述べます。

セッションの設定

デフォルトだとローカルファイルになっているので、先に設定したMemcachedを使うようにします。

ログの設定

デフォルトだとローカルファイルにログを出力するようになっているので、標準出力に出力するように変更します。

ログエンジンにConsoleLogを使います。

ファイルログで見たければ、元々の設定でも大丈夫です。

Herokuでのログコンソールの見方、ログファイルの出力は後日記事にします。

終わりに

とりあえずは以上となります。

まだキャッシュが設定できてないですが、一応これでデプロイはいけます。

PHPで作ったWebサービスをHerokuでデプロイするまでの手順書 - Qiita

今回はこちらのQiitaで言う手順11はクリアしたので、デプロイまで行っちゃってください。

キャッシュ(Memcached)の導入については、次回「CakePHP3をHerokuでデプロイしたい③」で話します。

ありがとうございました。

CakePHP3をHerokuでデプロイしたい①

CakePHP3をHerokuでデプロイしたいです。

ここはかなり時間を要しました。

参考にしたサイトやその途中で詰まったところを説明していこうと思います。

悩んでいる方、詰まっている方の助けになれば幸いです。

【自分の環境】
macOS Catalina
PHP7.4.2
CakePHP3.8
MAMP5.7
Apache2.2
MySQL5.7

参考になるサイト

まず以下のQiitaです。

PHPで作ったWebサービスをHerokuでデプロイするまでの手順書 - Qiita

ここではCakePHP3ではないのですが、PHPでのデプロイ手順が載っています。

かなり参考になりました。

CakePHP3では手順10までは、このQiitaを見てやるだけで何とかなります。

手順11に関しては「CakePHP3をHerokuでデプロイしたい② - 29歳からエンジニア目指すブログ」で書きます。

上記のQiitaで1点補足するとすれば、手順6でcomposer.jsonを作ってcomposer installしているところです。

これはCakePHPを使っている我々の場合、いらない手順です。

CakePHPでcomposer系はすでにあるので、結局手順12のディレクトリに移すところで上書きしてしまいます。

これでデプロイまでできれば、あとはエラーを消すだけとなります。

上のQiitaで詰まった場所

以下は個人的にですが、かなり詰まった場所があります。

手順10です。

phpMyAdminからMySQLWorkbenchへテーブル情報をコピーするのですが、全然入らなかったです。

エラーは3つ出ました。

MySQLWorkbenchでのエラー①

まずエラーの内容は忘れてしまったのですが、以下のQ&Aで解決しました。

[Q&A] Herokuでのデプロイ中につまづいてます。。 - Qiita

内容はDB指定が甘かったからでした。

>エクスポートした SQL に CREATE DATABASE IF NOT EXISTS `studydiary` が含まれていそうです。 ClearDB では既定の heroku_xyz... 以外に新しいデータベースを作ることはできません。 phpMyAdmin で単一のデータベースについてエクスポートするか、 SQL ファイルを直接編集するといいでしょう。

以上のアンサーのように、違うDBの内容も一緒にエクスポートしていたことから発生しました。

phpMyAdminでエクスポートする際に、単一のDBを選択してエクスポートしたら、エラーが変わりました。

MySQLWorkbenchでのエラー②

エラーは「ERROR 1050 (42S01) at line 29: Table 'comments' already exists」です。

これは見たまんまのエラーです。

きっと途中までdumpファイルが実行されてしまったことによるエラーです。

MySQLWorkbenchのテーブルを見て、既存のテーブルたちを削除してやるとエラーが変わります。

自分はとあるteratailで「phpMyAdminSSL化していないから」という記述に踊らされて、必死でMAMPSSL化していました。

勉強になったのでよかったのですが、ちゃんとエラーを見極めるのは大事ですね。

MySQLWorkbenchでのエラー③

これまたエラーを残してないので忘れてしまったのですが、絵文字対応に関するエラーでした。

utf8mb4にしては文字数が多いよ、みたいなエラーでした。

全部のstring(varcher)の文字数を191文字以内にすることで解決しました。

app.phpのDB情報の書き換え

そしてClearDBを使っているので、CakePHP3の設定も書き換えなければなりません。

先ほどのQiitaで言う手順11です。

ただこれに関しては「CakePHP3をHerokuでデプロイしたい② - 29歳からエンジニア目指すブログ」の記事で書こうと思います。

次の記事で書きますので、公開をお待ちください。

終わりに

一旦これで終わりにしようと思います。

app.phpの設定が必要なので、それは「CakePHP3をHerokuでデプロイしたい② - 29歳からエンジニア目指すブログ」に続きます。

ありがとうございました。

CakePHP3で絵文字を表示したい

絵文字、表示したいですよね。

今はスマホ主体だと思うので、絵文字が使えるに越したことないです。

感情が伝わりやすいですからね。

そこで絵文字表示するちょっとした設定を見ていきます。

【自分の環境】
macOS Catalina
PHP7.4.2
CakePHP3.8
MAMP5.7
Apache2.2
MySQL5.7

デフォルトでは絵文字が無効になる

自分はMAMP環境でphpMyAdminを使っていたので、それで説明していきます。

CakePHP3のデフォルト設定では無効になってるんですね。

たぶん照合順序というものが「utf8_general_ci」とか「utf8_unicode_ci」とかになってます。

ざっくり言うと「utf8」系ではなく「utf8mb4」系にしましょう。

自分は「utf8mb4_general_ci」にしています。

問題点…ただ照合順序を変えるだけではダメ

照合順序を変えると、文字データの大きさが4/3倍になるらしいです。

だいたいデフォルトだとstring(varcher)は255文字だと思うんですけど、それでは後々DBをインポートできないなどの痛い目に遭います。

なので文字数を191にします。

なぜ191文字かと言うと、4バイトの文字が191文字分なので764バイト…これはMySQLの限界は最大767 バイトを下回るからだそうです。

自分の場合はユーザー名でもコメントでも何でも191より下の数にしました。

あとで整合性がなくなるので、phpMyAdminをいじるのではなく、マイグレーションでいじりましょう。

また人によってはMySQLの限界を変える方法を取る方もいます。

ですが自分は設定が上手くいかず、本番環境でインポートできなかったので、191文字に削減で行っています。

コードを書き換えていく

以下の2つをやっていきます。

①app.phpでの設定

②ビューテンプレート(レイアウトテンプレート)を書き換え

①app.phpでの設定

まずapp.phpですが、以下に書き換えます。

'Datasources' => [
        'default' => [
           (中略)
            /*
             * You do not need to set this flag to use full utf-8 encoding (internal default since CakePHP 3.6).
             */
            'encoding' => 'utf8mb4', // コメントアウトする行
            'timezone' => '+09:00',
            'flags' => [],
           (中略)

コメントアウトする行と書きましたが、元々「'encoding' => 'utf8mb4'」となっているので、そう書きました。

コメントアウトしてあげると、データベースの設定がutf8mb4になるみたいです。

②ビューテンプレート(レイアウトテンプレート)を書き換え

残りはビューテンプレート(レイアウトテンプレート)を書き換えです。

いつものようにだと文字コードを「utf-8」にしていると思います。

それを「utf-8mb4」にしていきます。

以下はレイアウトテンプレートの一部です。

参照してください。

<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8mb4"> // この行が大事
    <title><?= "サービス名" ?></title>

mb4を付け足すかんじですね。

これで表示ができます。

終わりに

以上となります。

絵文字表示はできればやりたいですよね。

ぜひやってみてください。

ありがとうございました。

CakePHP3でファイルログ出力をしたい

セキュリティのブログでログが大事という話をしました。

CakePHP3でのやり方を書いていきます。

当たり前の内容だからかわからないですが、やり方があまりネットで調べても出てこず、結構悩んだので書きたい内容でした。

【自分の環境】
macOS Catalina
PHP7.4.2
CakePHP3.8
MAMP5.7
Apache2.2
MySQL5.7

デフォルト設定の確認

たぶんデフォルトの内容でもデバッグログとエラーログは出ていると思います。

以下の内容でapp.phpに設定されているからです。

'Log' => [
        'debug' => [
            'className' => FileLog::class,
            'path' => LOGS,
            'file' => 'debug',
            'url' => env('LOG_DEBUG_URL', null),
            'scopes' => false,
            'levels' => ['notice', 'info', 'debug'],
        ],
        'error' => [
            'className' => FileLog::class,
            'path' => LOGS,
            'file' => 'error',
            'url' => env('LOG_ERROR_URL', null),
            'scopes' => false,
            'levels' => ['warning', 'error', 'critical', 'alert', 'emergency'],
        ],
        // To enable this dedicated query log, you need set your datasource's log flag to true
        'queries' => [
            'className' => FileLog::class,
            'path' => LOGS,
            'file' => 'queries',
            'url' => env('LOG_QUERIES_URL', null),
            'scopes' => ['queriesLog'],
        ],
    ],

これで十分という方はそれでもOKです。

ただ自分の場合はアクセスログだけ取りたいっていう願望がありました。

そこでカスタムログ(custom.log)っていうログを出力させています。

名前が微妙ですが、気にしないでください。

新たなログファイルを作る

設定は以下です。

// エラーログの記述の下に以下を新しく追加
        'custom' => [
            'className' => FileLog::class,
            'path' => LOGS,
            'file' => 'custom',
            'levels' => ['notice', 'info'],
        ],

何てことない記述ですが、ポイントは2つです。

①fileをcustomにしている

これはcustom.logというファイルが新たに出来上がるようにするためです。

②levelsをnoticeとinfoだけにしている

levelsというのはログレベルのことです。

公式ドキュメントに一覧が載ってますが、深刻なものから軽微なものまで確か8種類くらいあります。

自己判断ですが、アクセスログの場合は失敗・成功でそれぞれnotice・infoにしようとしたので、この2つになっています。

ログ出力の記述をしていく

それではログ出力の記述をしていきます。

ここがあんまりネットになくて苦労しました。

①出力内容を書き換える、②ログ出力ポイントに書いていく、の2ステップです。

①出力内容を書き換える

これはファイルログの場合、/Applications/MAMP/htdocs/[アプリ名]/vendor/cakephp/cakephp/src/Log/Engineディレクトリの中にあるFileLog.phpをいじります。

かなり下層にあり、パスが長いですね…。

この中にあるlogファンクションをいじります。

自分の場合、以下のようになっています。

public function log($level, $message, array $context = [])
    {
        $message = $this->_format($message, $context);
        $output = date('Y-m-d H:i:s') . ' ' . ucfirst($level) . ': ' . $message . ' IPアドレス: ' . env('REMOTE_ADDR') .
                    ' アクセス対象URL: ' . env('REQUEST_URI') . ' リソースURL: ' . env('HTTP_REFERER') . "\n"; // 注目する行
        $filename = $this->_getFilename($level);
        if ($this->_size) {
            $this->_rotateFile($filename);
        }
      (中略)

注目する行と書いてある行を見てください。

変数outputをいろいろ書き加えています。

1つ1つ説明すると、

・date('Y-m-d H:i:s')…日時

・ucfirst($level)…エラーレベル

・$message…コントローラで記述できるメッセージ

・env('REMOTE_ADDR')…IPアドレス

・env('REQUEST_URI')…アクセス対象URL

・env('HTTP_REFERER')…アクセス元URL

以上になります。

これらがわかれば、だいぶアクセスしたユーザーの情報が分かります。

セキュリティのブログでお話した内容をほぼ網羅できています。

これで出力内容は定義できました。

②ログ出力ポイントに書いていく

残りはログ出力ポイントに書いていくだけです。

これはそのWebサービスによって異なってきますが、ログイン機能は大体あると思うので、そこを例に記述します。

loginアクションのあるコントローラに書いていきます。

以下のようになっています。

// ログイン処理
    function login(){
        // レイアウトを使わない
        $this->layout = '';
        
        // POST時の処理
        if($this->request->isPost()){
            $user = $this->Auth->identify();
            // Authのidentifyをユーザーに設定
            if(!empty($user)){
                $this->Auth->setUser($user);
                $this->log('ユーザーID' . $this->Auth->user()['id'] . 'によるログイン/成功', 'info'); // 注目する行
                return $this->redirect($this->Auth->redirectUrl());
            }
            $this->log('何者かによるログイン/失敗', 'notice'); // 注目する行
            $this->Flash->error('メールアドレスかパスワードが間違っています。');
        }
    }

また注目する行と書きました。

2つあります。

まずログイン成功のログです。

「$this->log('ユーザーID' . $this->Auth->user()['id'] . 'によるログイン/成功', 'info');」となっています。

ざっくり言うと、$this->log(メッセージ , ログレベル);というかんじです。

ユーザーIDを上手く引っ張れなかったので、自分はコントローラでメッセージとしていちいち記述しています。

また操作が成功したか失敗したかもメッセージで書いています。

そして成功の時はログレベルを「info」にしています。

逆に失敗に分岐した時は以下のログが出ます。

「$this->log('何者かによるログイン/失敗', 'notice');」です。

ここでもできればユーザーIDを取りたいですが、認証前なのでユーザーIDは取れません。

何者かが失敗して、noticeログを出そう、というかんじです。

これでcustom.logというファイルにアクセスログがズラズラっと出ているはずです。

場所は/Applications/MAMP/htdocs/[アプリ名]/logsに出ます。

終わりに

以上となります。

ログは結構奥が深いと思います。

まだ使いこなせてないですが、しっかり勉強して監視してこうと思います。

ありがとうございました。

CakePHP3で作ったWebサービスをHerokuでデプロイするまでにセキュリティとしてやったこと

CakePHP3でWebサービスを公開するまでのセキュリティ、どこまでやれば安心なんでしょうか。

いや正直、やるべきことが全然わからないです。

上記のような方、いらっしゃるんじゃないでしょうか。

自分がそうでした。

少しでもそういう方の道標になれば良いなと思って書きます。

具体的な手段というよりも、概要をまとめていきます。

【自分の環境】
macOS Catalina
PHP7.4.2
CakePHP3.8
MAMP5.7
Apache2.2
MySQL5.7

大前提

プラットフォーム(サーバーやネットワークなど)はHerokuを使ってます。

Herokuを使ってデプロイしています。

めちゃくちゃ簡単にデプロイできると呼び声が高いので使いましたが、それ以上にセキュリティをほとんどやってくれているというのも大きいですよね。

Herokuに頼るんかい、と思われるかもしれませんが、割と問題を起こしてからでは取り返しがつかないです。

最初は補助輪付きで…それでいいんじゃないでしょうか。

セキュリティをやる前に

セキュリティを締める前におすすめのアクションですが、「徳丸本」を読んだ方がいいと思います。

体系的に学ぶ 安全なWebアプリケーションの作り方 第2版[リフロー版] 脆弱性が生まれる原理と対策の実践 | 徳丸 浩 | コンピュータ・IT | Kindleストア | Amazon

※上記は「リフロー版」で、電子書籍にはもう1つ「固定版」というのもあるみたいです。お気をつけて。

通称が徳丸本です。

徳丸さんが書いているから徳丸本らしいです。

Webアプリのセキュリティならこれを読め、的な本です。

とても体系的に詳しくWebアプリのセキュリティのことが載っています。

正直ネットの情報って断片的なものが多いですし、1度はこの本を読むのがいいと思います。

地味に自分は2度読みました。

正直1周目ではとても入ってこない、掴みきれないくらいの情報量でした。

2周目読んだら「あ、あれあそこに書いてあったやつだ」みたいなかんじで、ようやく咀嚼できました。

聞いたかんじWeb系のエンジニアならみんな読んでるので、読んだ方がいいです。

本題…CakePHP3(アプリ側)でやること

行きます。

と言ってもフレームワークをちゃんと使っていると、セキュリティって割としっかりするものなんです。

例えば、SQLインジェクションというものがありますが、素のSQL文(SELECT * FROM users;みたいな)で書かなければ防げています。

CakePHP3で言うと$this->Users->find();みたいなチェーンメソッドで書いていれば、防げています。

ということで、「Herokuでデプロイする」+「フレームワークをちゃんと使う」これだけでセキュリティにおいて、かなりのショートカットができる認識です。

セキュリティに詳しい人に怒られそうですが、そう思っています。

その上で気をつけたことを書いていきます。

htmlspecialchars()をつける

htmlspecialchars()ファンクションはCakePHPで言う「h()ファンクション」です。

データを出力する時に使います。

<p>あなたの写真ID</p>
<td><?= h($photo->id) ?></td>

上記みたいなかんじです。

初心者なら普通に「$photo->id」でいいじゃん、と思うと思います。

ですが、これではもしも出力するデータにHTMLやJavaScriptなどが混入していた場合、変な文字に化けてしまうことがあります。

いわゆるクロスサイトスクリプティングXSS)です。

また文字コード脆弱性も避けることができます。

文字を出力する際は必ずh()ファンクションをつけるようにします。

バリデーションを見直す

バリデーションは皆さんつけていると思います。

ですが甘い付け方だとそこに付け入られてしまう可能性があります。

例えば、ファイルアップロード時に拡張子のチェックをつけておかないと、危ないphpファイルがDBに保存される可能性があります。

仮にこれをユーザーが閲覧できると、そのファイルを見たことをきっかけにWebサーバーに攻撃を食らったりします。

適切な拡張子のファイルしか保存できないようになっているか、いま一度確認しましょう。

実際に画像の拡張子を検査するバリデーションをつけたので、別記事で紹介します。

アクセス制御

これはセキュリティと呼ぶのか分かりませんが、やったので説明します。

例えば管理者画面にユーザーが入れてしまうと、一巻の終わりです。

そこから情報を閲覧されたり、情報をいじられたり…とにかく終わりです。

なのできっちりアクセス制御しておきましょう。

手段としてはadmin、userと利用者の役割を分けてアクセス制御することや、管理者画面ではパスワードを要求するようにすることとかです。

アクセスログなどを出力する

アクセスログなども残しておいたほうがいいです。

いつ、誰が、どこで、何を、どのように…。

これらの情報を管理者が拾えるくらいのログを出力したほうがいいらしいです。

具体的には、イベントとしては「ログイン・ログアウト」「ユーザー登録・削除」「物品購入・送金」などなど、重要な操作でログを出すようにします。

またログの内容ですが、「アクセス日時」「リモートIPアドレス」「アクセス対象URL」「操作内容」「操作が成功したかどうか」などです。

これらを記録することで、いつ深刻なアクセスや操作があったか見つけられ、IPアドレスなどでアクセス禁止にすることでサイトを守ることができます。

ログ出力に関しては、長くなるので別の記事で説明させてください。

終わりに…の前に

生半可な知識で書いてて感じましたが、セキュリティに関しては徳丸本を読むことが1番のショートカットだと思います。

重大な不備があったとしたら、時間的にも金額的にもたぶん考えられないくらいの損失になります。

もちろん読んで終わりではないですが、読んで自分のサイトに落とし込んでいくのがいいです。

一旦、体系的に学んで備えることがあらゆる面で1番のショートカットだと思います。

終わりに

以上となります。

書きながら中途半端なものだなと思ってしまって公開するのを少し躊躇いましたが、公開したいと思います。

自分ももっと勉強していきます。

ありがとうございました。