記事一覧

PHPでデストロイコードを書いてしまった話




目次



世にも怖いコマンド

ハロー、みなさん。エジソンです。

みなさんは、世にも怖ろしいコマンドと聞いて思い浮かぶものは何があるでしょうか。僕がパッと思い浮かぶ、怖ろしいコマンドと言えば、以下のコマンドです。

rm -rf /

打つなよ…、絶対に打つなよ!(゚△゚;ノ)ノ

お笑い芸人の前振りでも何でもなく、これに関しては本当に打ち込んではいけないコマンドです。UnixやLinuxなどで、上記のコマンドを実行するとコンピュータに保存されているデータは全て、あっという間に消えてしまうことでしょう。

詳細を説明すると、このコマンドには以下のような意味があるのです。

(/)ルートディレクトリ以下を(force)強制的に(recursive)再帰的に(remove)削除する

デストロイヤー事件

僕が昔勤めていた会社で実際に起きた話らしいのですが、ある会社に出向中の下請け社員が、会社のとあるコンピュータで rm -rf / を間違って実行してしまったそうです。これを実行したが最後、破壊神の名をほしいままにすることは、間違いないでしょう。

せめてもの救いは、このコンピュータが商用環境ではなく、開発環境であったことでしょうか。

何故、その人がルートディレクトリを指定して削除コマンドを実行してしまったのかは、今となってはわかりません。それにしても、背筋が震える事件です。笑い話にするのではなく、明日は我が身と考えて謙虚に考えるべきでしょう。

どこかに潜むデストロイヤー

さて今回は、僕がPHPで引き起こしてしまったデストロイコードを紹介いたします。どうぞ、僕のしかばねを踏みしだいて、あなた自身がしかばねとならぬよう教訓として、深く胸に刻んで頂きたく思います。

<?php

// 対象となるファイルパス
$targetFilePath = '/Users/test';
・
・
・

// 対象となるファイルパスから一つ上の、otherTestディレクトリを指定
$targetOtherFilePath = $targetFilePath . '/../otherTest';

// realpath関数で相対記述を取り除く
$targetOtherFilePath = realpath($targetOtherFilePath) . '/';

// 自作した指定のディレクトリを削除する関数
deleteDirectory($targetOtherFilePath);


以上がデストロイコードとなります。前提条件として、otherTestディレクトリが存在していないものとします。そして、realpath関数の戻り値がどうなるのかを考えると自ずと答えが見えてきます。

ちなみに、realpath関数は以下のような仕様です。

PHP - realpath
string realpath ( string $path )

説明:
realpath() は、 入力 path のシンボリックリンクをすべて展開し、 「/./」「/../」「/」などの参照をすべて解決することにより、正規化した絶対パスを返します。

realpath() は、 たとえばファイルが存在しないなどの失敗時に FALSE を返します。

ファイルが存在しない場合にはrealpathからは、FALSEが返されることがわかります。

ということは、realpathの戻り値であるFALSEとスラッシュが文字列連結されることになるのです。bool値であるFALSEと文字列が連結されるとどうなるのでしょうか?

$str = FALSE . '/' ;
echo $str;

出力結果:/

これにより、deleteDirectory関数にスラッシュのみが引き渡されるため、ルートディレクトリの削除が実行されるという事件が起きてしまうわけです!

そもそも、なぜファイルパス末尾にスラッシュを連結しているのかを、疑問に思われるかもしれません。スラッシュを付けるような処理を入れなければ、そもそも問題は起きないじゃないか、と。

簡単に説明すると、ファイルパス(ディレクトリ)の末尾にスラッシュを付けるというルールを採用していたからです。ディレクトリパスは、他のファイル名と組み合わせてパス文字列を構築することが多いため、ディレクトリには漏れなく、末尾にスラッシュを付けるというルールを適用していました。

また、今回のサンプルにおいては省略されていますが、deleteDirectory関数の後に、定義した$targetOtherFilePathを利用した、別の処理を書いていたのですね。そんな理由から、スラッシュの連結は行われるべくして行っており、それ故に、問題が発生してしまったというわけなのです。

まとめ

PHPは関数の戻り値で成否を判定するようなタイプの関数ライブラリもあるし、例外を返却するモダンなライブラリもあります。例外が発生すれば、処理が中断されるため、状況は悪化することなくストップしますが、関数の戻り値で成否を判定する場合には注意が必要なわけですね。

今回紹介したケースはまれかと思いますが、関数の戻り値確認が重要であることに気づかせてくれる、ある意味好例なのかもしれません。

PHPは便利な反面、下手くそなコードを意図せず書けてしまうというデメリットも兼ね備えているため、むしろ注意深く扱わなければいけない、上級者向けの言語なのかもしれません。

関連記事

このエントリーをはてなブックマークに追加

コメント

No title

遥か昔に、OS:MS-DOS 言語:lattice-cで
指定したディレクトリ(まだフォルダという名称はなかった)配下のファイルを削除する機能をつくりました。

当然サブディレクトリに潜っていく機能です。

テスト用にディレクトリを何階層か用意し、それぞれのディレクトリにファイルを配置して
期待と不安を抱きつつ動作確認を試みました。

ですがプロンプトは戻ってきません。
しばらく待っても音沙汰なしなので Ctrl+C を試みました。
止まりません。

電源を切ろうか悩んでいると、ラップトップの画面が乱れました。
砂嵐状に。

あせりました。
電源切りました。

いっぷくしました。

電源いれました。
何も動きませんでした。
HDのカラカラいう音もなく、
ファンだけの音しかしませんでした。

別機に保存しておいたソースを先輩がみてくれて教えてくれました。
サブディレクトリとして「..」も対象にしているであろう事を。

そこそこ深かったテストディレクトリから這い上がって全てを削除したようです。
手当たり次第に全てのファイルを消し去っても動き続けた挙句、
液晶へ空のHDをイメージするかのような出力見せ付ける
あのアホなPGを作ってしまった過去を思い出してしまいました。

この経験で少しだけ慎重さが身につきました。

Re: No title

貴重な体験談を教えていただきありがとうございます。

Windowsならdirコマンド、Unixならlsコマンドで表示される . と .. の2つですね。
確かに、VBAのパス情報を返却する関数やPHPの関数においても、そのようなシェルコマンドありきの仕様になっていたように記憶しています。

ファイル操作を再帰的に処理する場合には、注意が必要だということが再確認できました。
ありがとうございます。

コメントの投稿

非公開コメント

プロフィール

EZOLABブログへようこそ。
EZOLABは、札幌のソフトウェア会社です。

http://ezolab.co.jp