著者アーカイブ 杉浦

著者:杉浦

【PHP】ファイルの中身からファイル形式を識別する

 webサービスではユーザがサーバにファイルを送ることはよくあります。送られたファイルの中身を扱うこともよくあります。想定していない振る舞いをするファイルを扱った際はバグに繋がりやすく、時にセキュリティの問題も引き起こします。ファイル形式の識別と識別結果による条件分岐は必要になります。
 ファイル形式の識別は簡易な方法がいくつかあります。例えば、ファイル名末尾にある拡張子から読み取れます。セキュリティ的にはとても信用できませんがファイル名のみで判別というのはとても高速で簡単です。例えば、MIMEタイプで大別できます。とはいえまだ偽装は簡単です。ファイルの中身からファイル形式を読み取る方法はこれらよりいくらか頑強な方法です。
 MIME タイプ (IANA メディアタイプ) – HTTP | MDN
 ファイルの中身の先頭にはマジックナンバーと呼ばれるバイナリ形式の署名が入っています。形式によってはテキストエディタで無理やり開くと文字列で一部が見えます。下の画像の”臼NG”のあたりです。


 ファイル形式とマジックナンバーは次のサイトらにまとめられています。
File Signatures
File Signature Database:
 これと読み取ったファイルの中身を突き合わせることでファイル形式を識別します。PNG形式ならば次の通り、16進コードで表して” 89 50 4E 47 0D 0A 1A 0A”となっています。

 PHPならば次の様に検証できます。

function isPng($content){
    $png_sig = "\x89\x50\x4e\x47\x0d\x0a\x1a\x0a";
    return strpos($content, $png_sig) === 0;
}

 ダブルクォーテーションでくくった文字列はエスケープシーケンスが使え、\xhhは”16進コードで hh の文字”を表します。
PHP: エスケープシーケンス – Manual
 先ほど得られた16進コードによるファイル先頭マジックナンバーをエスケープシーケンスによるも文字列で表し、strposで先頭からマジックナンバーが現れるかを確かめます。
PHP: strpos – Manual
 これでファイル形式を識別できます。先頭が正しくともさらに続きの部分で攻撃の余地があるかもしれませんがノーガードよりは随分ましになります。

 ファイルのバイナリはLinuxコマンドのod -h [ファイル名] | less -Fが読みやすかったです。

著者:杉浦

【Python】対話型アプリケーション自動化ツールPexpectの紹介

 Linux端末などにアクセスしてコマンドライン上で操作を行うことはよくあります。何かの作業で定型化した操作を行うこともままあります。Pexpeectでコードを組むと自動化処理を作れます。PexpectはLinuxコマンドのexpectの様なことをするPythonモジュールです。
Pexpect version 4.7 — Pexpect 4.7 documentation
 接続コードはシンプルです。ただ使うだけなら次のコードで大丈夫ですが、gitやら何やらに上げると大惨事です。そういう時はid, passwordは設定ファイルなり実行時の引数なりで渡すようにしましょう。

child = pexpect.spawn('ssh user@example.com')
child.expect('password')
child.sendline('mypassword')

 Common problems — Pexpect 4.7 documentation
 とりあえずの作りなら対話は次の様になります。コマンドを送る、入力可能状態になるまで待つ、の繰り返しです。

child.expect('user@example.com.*') # 入力可能状態(左側にユーザ名とディレクトリが見えるアレ)であることを期待する
child.sendline('telnet localhost %d\r' % telnet_port# コマンドを送る

 あくまで対話なので対話先で改めて対話先のローカル線用対話プログラムを開始することもできます。というかかなり広範囲のことができます。Linuxを深く知らない自分の範囲では何でもできるぐらいの感じです。Pexpectはコマンドライン上のプログラムのテスト、サーバやマイコン設定のデプロイ、ncとか使ってペネトレーションテストなど様々な使い道があります。
 公式ドキュメントのExamples — Pexpect 4.7 documentationにはプログラムとしてほぼ完成の状態の例があるのでそれを改造すると理解が早まり、すぐ実践ができます。

著者:杉浦

【Linux】複数ディレクトリにまたがって特定の正規表現に当てはまるの最新のファイル一つのパスを得るコマンド

 Linuxのコマンドはかなり機械語に近いところで最適化されており高速です。PHPのファイルシステムを介していては遅いと言わざるを得ないような場面でもコマンドをexecすれば高速です(プログラム実行関数を扱う際は文字列のエスケープなど対策を忘れないようにする必要があります。任意コード実行のセキュリティホールに繋がるので細心の注意が必要です)。
 とある単一のディレクトリの中で最新をとってくるのは簡単です。

ls -lt ./* | grep -e \.ほしいファイルの拡張子$ | head -n 1| awk '{print $NF}'

lsコマンドのtオプションはファイルを最終更新日時順に表示します. grepでほしいファイル種別に表示を絞り込みます。headコマンドの-n 1で最初の一行のみの表示を取得します。awkの$NFには区切られた文字の内の最後が入ります。結果次の様にファイルのパスが出力されます。

複数ディレクトリはちょっと手間です。ls dirA/* dirB/*とするとディレクトリごとの結果が出てくるのでまとめてソートする必要があります。

ls -l --full-time dirA/* dirB/* dirC/* | grep -e \.ほしいファイルの拡張子$ | sort -k 6,7 -r | head -n 1| awk '{print $NF}'

lsの–full-timeオプションで日時表示をソートしやすい形(なしだと月日で手間)にして出力。sortで6,7列目(-k 6,7)の日時を降順(-r)でソートします。最後はディレクトリ一つの時同様にhead -n 1| awk ‘{print $NF}で完成です。

著者:杉浦

言語横断パッケージ検索サービスLibraries.ioの紹介

Libraries.io – The Open Source Discovery Service
Libraries.ioはパッケージ検索サービスです。パッケージ検索自体はよくありますし、大体パッケージマネージャの本元が強すぎて特色を出しにくいです。Libraries.ioは複数言語のパッケージマネージャを横断的に検索できる点で特別です。GitHubの中でリポジトリを探す感じです。

 特に有効的に使えるのは使用言語が自由で特定の目的を達成したい時でしょう。使用言語選定の一評価指標になります。例えば、dxf(図面用のフォーマットの1種。独自フォーマットA->dxf、dxf->独自Bの様に中間形式で使われやすい)で検索すると次のように出ます。

 Go言語やJavaScriptで層が厚そうです。pythonもいくつか選択肢があります。dxfを扱うプログラムを作るなら言語はこのあたりを使った方が手っ取り早くできそう、と判断できます。

著者:杉浦

【PHP】composerを使ったライセンスチェック

 composerはPHPのパッケージインストーラでその中にはいくつか機能があります。機能の一覧はcomposer listで確認できます。composer licensesはその中の機能の一つでcomposerでインストールされているパッケージ全ての名前、バージョン、ライセンス種類を表示してくれます。
 
 ライセンスは深く気にするべき時、しなくていい時があります。どこの誰にでもソースコード込みで公開するOSSの開発時は派生だろうがなんだろうが元のライセンスを継承して完全公開すれば大体OKですが、商用の場合そうもいきません。いくつか避けるべきライセンスがあります。
 避けておいた方が無難なライセンスはGPL系、特にAGPLです。AGPLはAFFERO GENERAL PUBLIC LICENSEの略です。
 GNUアフェロ一般公衆ライセンス – GNUプロジェクト – フリーソフトウェアファウンデーション
 AGPLは次の通りのことをプログラムに要求します。

GNUアフェロ一般公衆ライセンスは通常のGNU GPLバージョン3を改変したバージョンです。これには一つ要求が加わっています。サーバで改変したプログラムを動かし、そこでそのプログラムとほかのユーザに通信させる場合、サーバはユーザにそこで動いている改変バージョンに対応するソースコードのダウンロードも許可しなければいけない、というものです。

 どこまでが改変だとかの解釈は理解していませんが(継承した時点でアウト?)、AGPLのプログラムを含むプログラムを動かした場合、そこに正規でアクセスしたユーザはソースコードも手に入れられなければならない、という状況になりそうです。商用ということは扱うものがものですから悪意のあるユーザへの対策は必須です。ソースコードの公開はセキュリティホールの発見を容易にします。あるいは見つかるはずのなかった穴を見つけさせます(マジックナンバーで書かれた何かしらとか。設定ファイルを介するプログラムを組んでおけばそれだけで回避可能ですが)。
 composerのライセンスチェック機能を使うと、こういった問題を容易に回避できます。やることはシンプルで次のようにcomposer licensesの結果にgrepをかけるだけです。

composer licenses | grep -v -P ^\(?=.*[^,]\(MIT\|BSD-3-Clause\)[^,]\)


 安全安心な許容できるライセンスによるホワイトリストがよいと思います。

著者:杉浦

モデルでまとめるかモデルを分割するか

 webページを作るとき、多くはモデル、ビュー、コントローラに分けてコーディングを行えます。ざっくばらんには、ビューは与えられた値を適当な形にあてはめるのみのテンプレート、コントローラはモデルに命令を出し値を要求し結果をビューに渡す仲介者、モデルはそれ以外の画面に依らない部分、といった分類です。
 これは実際優秀なのですが、複雑な問題を取り扱うほどモデルが太っていきます。気が付いたら頭の中のハッシュマップが頼りになり出します。単純なCRUDの様なものを扱う程度ならばフレームワークのORM任せで”モデル”と一言で済ませてもあっさり済みますが、そうならない時もあります。そうなる時はモデルを更に分割していく必要があります。モデルの一言でまとめられた要素をhogeモデル、fugaモデルと分解していきます。
 分解の仕方は様々です。様々なので後や他にコードを触る人のために分解の仕方や指針をドキュメントに残すべきです。部分的に何かを用いた場合など、特殊なパターンならばなおさらです。
 分解の仕方は信頼できる先達のモノが良いでしょう。本や説明書などドキュメントされているものが望ましいです。例えば、エリック・エヴァンスのドメイン駆動設計 | Eric Evans, 和智右桂, 牧野祐子, 今関剛 | 工学 | Kindleストア | Amazonです。ドメイン駆動設計ではドメイン(業務知識)に従ってモデルを定義していくやり方を示しています。フレームワークもあると思ったのですが、ORMは用意したから他で自由に書け、といった具合でした。

著者:杉浦

値オブジェクトを作った方が良い時悪い時

 値オブジェクトはエリック・エヴァンスのドメイン駆動設計 | Eric Evans, 和智右桂, 牧野祐子, 今関剛 | 工学 | Kindleストア | Amazonの中で次の様に述べられています。

あるオブジェクトが、ドメインにおける記述的な側面を表現し、概念的な同一性を持たない場合、そういうオブジェクトは、値オブジェクトと呼ばれる。値オブジェクトがインスタンス化される際に表現しようとするのは、何であるかだけが問題となり、誰であるか、あるいはどれであるかは問われないような設計の要素である。

Eric Evans. エリック・エヴァンスのドメイン駆動設計 (Kindle の位置No.2480-2483). 翔泳社. Kindle 版.

 乱暴な言い方をすれば任意の型で定義されたインスタンスを実現するオブジェクトです。例えば、PHPならば次のような実装ができます。

class GeoPoint
{
    private $lat;// 緯度
    private $lng;// 経度
    
    /**
     * GeoPoint constructor.
     * @param $lat
     * @param $lng
     */
    public function __construct($lat, $lng)
    {
        $this->setLat($lat);
        $this->setLng($lng);
    }
    
    /**
     * privateプロパティを読み取り可能にする.
     * @param $name
     * @return mixed
     */
    public function __get($name)
    {
        return $this->$name;
    }

    /**
     * 一部のprivateプロパティを書き込み可能にする.
     * @param $name
     * @param $value
     * @return mixed
     */
    public function __set($name, $value)
    {
        $setter = 'set'.ucfirst(camel_case($name));

        return $this->$setter($value);
    }

    /**
     * privateプロパティにissetできるようにする.
     * @param $name
     * @return bool
     */
    public function __isset($name)
    {
        return isset($this->$name);
    }

    /**
     * 存在しないメソッドが呼ばれたらnullを返す.
     * @param $methodName
     * @param $arguments
     * @return null
     */
    public function __call($methodName, $arguments)
    {
        return null;
    }

    /**
     * __setで呼ばれるprivateプロパティへのsetter
     * @param $value
     */
    private function setLat($value)
    {
        if (-90 < $value && $value < 90) {
            $this->lat = $value;
        }
    }

    /**
     * __setで呼ばれるprivateプロパティへのsetter
     * @param $value
     */
    private function setLng($value)
    {
        if (-180 < $value && $value < 180) {
            $this->lng = $value;
        }
    }
}

 上コードならば緯度経度で地点を表現するオブジェクトです。インスタンス化時、プロパティ変更時に必ずsetHoge()を通し、setHoge()の中で不適正な値が代入されることを防ぎます。例ならば緯度は必ず-90度から90度の範囲であるし、経度は-180度から180度の範囲と限っています。setに失敗したら何も起きない作りですが、もっと厳格に作って、失敗したら即座に例外をthrowするのも全然ありです。
 値オブジェクトは極端なカプセル化の実現です。上記コードの様に好き勝手ルールを増やしても外からはルールは変えられず、見えません。必要なプロパティだけが見えます(キャストとか便利コンストラクタとかもありな気がします)。
 上記の例は緯度経度のみのざっとした作りですが、プログラムで扱う領域に複雑な概念や特殊な値の振る舞いがある程、値オブジェクトは強力になります。
 値オブジェクトを導入していないコードで値オブジェクトを導入した方が見通しがよくなる時、よくならない時があります。よくならない時は単純な種類のデータを扱っている時です。上の例にある通り、ちょっとしたものでも数十行コードが増えます。増えた分読みにくくなります。よくなる時は色々ですが、同じキーセットの配列やオブジェクトが至る所で現れるコードはよくなりやすいです。そういった時は特定のキー名で同じ概念を表現していることが多いです($hoge[‘lat’], $hoge[‘lng’]で検索すると恐ろしい数が出たり)。そういったキーをそのままプロパティにしてプロパティに適したルールをつけるだけで安全で読みやすいコードになります。

著者:杉浦

【PHP】のスターリンソートがけっこうそれっぽい

 スターリンソートとはジョークソートアルゴリズムの一つです。

I came up with asingle pass O(n) sort algorithm I call StalinSort.
You iterate down the list of elements checking if they’re in order.
Any element which is out of order is eliminated.
At the end you have asorted list.
gustavo-depaula/stalin-sort: Add a stalin sort algorithm in any language you like ❣️ if you like give us a ⭐️

 スターリンソートは、要素を先頭から順番に見ていき、ソートされていない要素を発見する度に粛清することによって昇順ソートを実現します。全要素を一つずつ見るだけなので計算量はO(n)であり、高速です。

3, 6, 2, 1, 7, 10, 1, 10
↓
3, 6,       7, 10,    10
↓
3, 6, 7, 10, 10

 アルゴリズム完了時に昇順で要素が並んでいるので昇順ソートと言い張れます。入力時から要素が減っていようと昇順ソートです。(ソートの要件満たしていないし実際にはフィルターに分類されます)
 多くの言語は配列を詰めていくのでスターリンソート完了時には粛清された要素なんて最初からいなかったと思わされます。

 ですがPHPはそうなりません。

 PHPの配列は順番づけられたマップに過ぎず、キーと値はがっちり結びついており、配列が詰められることはありません。そのため次の様なことにもなります。

 紐づいた値に基づいて複数の名前が消されました。消された要素は戻ってきません。

著者:杉浦

テーブルと1対1になるモデル以外のモデルを増やす時

 webプログラミングのMVCデザインパターンにおいてモデルとは大体DB(データベース)のテーブルそのものです。しかもモデルとテーブルは1対1のことがほとんどです。これはフレームワークがそう扱った方がやりやすい様に自動生成コマンドやクラス、メソッドを仕込んだり、例を用意しているからというフレームワークの用意した道筋が一因です。これは悪いことでなく実際、簡単なロジックを扱うプログラムならばモデル1つはテーブル1つとした方がシンプルにまとまり直感的なコードで要件を満たすことになり、いいことづくめです。無暗にリポジトリやらエンティティやらの概念を持ち込んで一つのテーブルにアクセスするのに二つ三つもクラスやメソッドを経由させられるより遥かに賢明です。
 しかしデータベースのテーブルと1対1だとどうにも収まりが悪い時があります。

 例えば、1つのユーザアカウントで様々なサービスの恩恵を受けられるプログラムの時です。こういった時、ユーザは何かを購入する、何かにメールを送る、何かを出品する、ブログを書く、などさまざまな操作をします。これらの操作がある上で、usersテーブルは一つだからモデルはUser一つだけ、とするとUserモデルは大爆発します。こういった時には各処理におけるUserモデルである購入者モデル、メール送信者モデル、出品者モデル、ブロガーモデル、を用意して全てusersテーブルに対応させる様にすることで対応できます。
役割駆動設計で巨大クラスを爆殺する – Qiita
 例えば、しょっちゅうまとめて参照されるテーブルがある時です。こういった時は複数のテーブルをselect, joinするクエリを必ず発行するモデルを作ることで対応できます(DB上でview作ってそれを読みこむのもありかも)。エンティティ(参照オブジェクト)の考え方はこういった時、役に立ちます。
【PHP】Adapterパターンであるカラムの値によって大きく振る舞いを変えるテーブルの扱いを多少マシにする – 株式会社シーポイントラボ | 浜松のシステム開発会社
 例えば、そもそもDBを参照しないがデータを参照する時です。ある外部サービスから吐き出されたファイルを読みこんで扱う時にはDBに関係ないモデルを用意してファイルの情報を持たせる様にするべきでしょう。
【Laravel】データベースと無関係のEloquentっぽいモデルを作るためのFluentクラス – 株式会社シーポイントラボ | 浜松のシステム開発会社
 例の様に単にデータを扱うモデルを考えるだけでも、こういった複雑な時にはテーブルと1対1になるモデル以外のモデルを作った方がやりやすいです。
 原義に基づくならばMVCのモデルとは”業務領域のデータと振る舞い”です。データベース設計が優れていてそれで設計が完結できる時は、モデル=DB関連の操作をまとめたモノとするのが正しいし適した形ですが、DB上で扱うとどうにも複雑になる時は”振る舞い”も別途モデル化するのが良いでしょう。
 余談ですが、振る舞いのモデル化はサービス化です。振る舞いの含意は広い為、多くの動作がサービスと言えなくもないです。サービスと定義できるからと何でもかんでもサービス化すると、オブジェクト指向を実現するためのMVCなのにオブジェクト指向から離れて手続きプログラミングと化します。なるべくサービスにせず、より適した場所を見つけたいものです。

著者:杉浦

カプセル化となんちゃってカプセル化のアンチパターン

 カプセル化はオブジェクト指向の中で述べられている手法の一つです。オブジェクトに関する詳しい処理や情報をオブジェクト内部に閉じ込めておき、外部からは必要な分だけのまとまった簡単な部分にのみにアクセスできるようにする方法です。よくある例えに車の例えがあります。運転手はアクセルを踏めるしギアも変えられるが、エンジンのシリンダー一つ一つや空気の流入量を操作することはできない、というものです。これは動力のカプセル化で、車の内部を深く知らず瞬時に燃料の爆発に最適な環境を計算できない多くの人間からエンジン内部の詳しい動作を隠し、まとまった操作であるギアとアクセルにだけアクセスできる様にしています。こうすることで誤った操作を減らし、操作の混乱を防ぎます。
 コーディングにおけるカプセル化は例えば次の様になります。

/**
 * @property-read int    $id    読み取りのみ可能
 * @property      string $mail  読み取りと特定形式の書き込みのみ可能
 * @property      string $name  読み書き自由
 */
class User
{
    private $id;
    private $mail;
    public  $name;
    private $secretProperty;  // 内部処理の何かしらにだけ使う完全に外から隠れるプロパティ

    /**
     * 主キーで検索.
     * @param int $id
     * @return User
     */
    public static function find(int $id): User
    {
        return DB::select()
            ->from('users')
            ->where('id', $id)
            ->first();
    }

    /**
     * 任意のプロパティにまとめて代入. 代入後の$thisと代入予定の引数が一致すればtrue, しなければfalse.
     * @param array $values
     * @return boolean
     */
    public function fill(array $values): bool
    {
        $this->name = $values['name'] ?? $this->name;
        isset($values['mail']) ? $this->setMail($values['mail']) : null;

        return $values === array_intersect_key(['name' => $this->name, 'mail' => $this->mail], $values);
    }

    /**
     * データベースに保存.
     * @return boolean
     */
    public function save(): bool
    {
        return DB::select()
            ->from('users')
            ->where('id', $this->id)
            ->updete([
                'mail' => $this->mail,
                'name' => $this->name,
            ]);
    }

    /**
     * プロパティの読み取り範囲を通常の可視性を超えて操作.
     * @param $propertyName
     * @return mixed
     */
    public function __get($propertyName)
    {
        return $propertyName === 'secretProperty' ? null : $this->$propertyName;
    }

    /**
     * setterがあるプロパティならばprivateでも書き込める.
     * setterがないプロパティならば新規追加も許さない.
     * @param $propertyName
     * @param $value
     * @return bool
     */
    public function __set($propertyName, $value): bool
    {
        $setMethodName = 'set'.ucfirst(camel_case($propertyName));

        return $this->$setMethodName($value);
    }

    /**
     * 存在しないメソッドが呼ばれたらfalseを返す.
     * @param $methodName
     * @param $arguments
     * @return bool
     */
    public function __call($methodName, $arguments):bool
    {
        return false;
    }

    /**
     * 存在するprivateプロパティに対してisset()をかまされた時にtrueを返すために必要.
     * @param $propertyName
     * @return bool
     */
    public function __isset($propertyName): bool
    {
        return isset($this->$propertyName);
    }

    /**
     * メールとして認められる文字列ならばsetして良し.
     * @param $value
     * @return bool
     */
    private function setMail($value): bool
    {
        if(preg_match('/メールアドレスを表す適当な正規表現/u', $value)){
            $this->mail = $value;

            return true;
        }

        return false;
    }
}

 id, mail, nameの三つの要素を持つユーザクラスです。それぞれの可視性と代入時の操作を設定することでカプセル化の利点の誤った操作の軽減を実現しています。プロパティの増減まで縛っているのはわかりやすさの点で微妙ですが。大体のフレームワークのモデル周りの機能にもっと洗練したものが載っているので手書きで作るよりそちらを使った方が良いです。
 カプセル化をやりたかった時に起きるアンチパターン対策として重要なのは上記の内のmailプロパティのset機能です。mailプロパティに代入する際にバリデーションが働き、mailプロパティに入っている文字列はメールアドレスとして認められる文字列だと整合性を保っています。データベースの型のみではできないバリデーションで特に有効です。この手のプロパティにアクセスする際にわざわざメソッドを増やす意味を無視するとただただカプセル化処理のめんどくささだけが残ります。
 ありがちなカプセル化っぽいことをするアンチパターンが次です。

class Engine
{
    private $gear
    
    public function setGear($value)
    {
        $this->gear = $value;
    }

    public function getGear($value)
    {
        return $this->gear;
    }
}

 ただprivateプロパティを書き換えてるだけで、こんなことをするぐらいならpublicで宣言すれば良いです。set, getメソッドの長さの分むしろ読み難くなります。これがあるあるの間違いになるのはset, getを自動で生やす機能がIDEには大体備わっているためです。適切に使えば便利なのですが、とりあえずメソッドを介してプロパティにアクセスすればカプセル化になると考える人とこの機能が組み合わせると上記アンチパターンが発生し、後に続く人がコードを読む際に苦しみます。

 正しくオブジェクト内部に留めておくと変更も楽です。これはオブジェクト内部しか影響しない、とわかりきっているコード部が増えるためです。