• PHP
【php8上級/準上級試験】模擬問題解説 問題24. escapeshellarg() escapeshellcmd()

「第1回 PHP 8 上級 模擬試験」を解説していきます

本記事ではひたすら下記記事の模擬試験の解説をしていきたいと思います!

第1回 PHP 8 上級 模擬試験
https://study.prime-strategy.co.jp/study/ph8ex1/

解説記事一覧

模擬問題 24

関数 に関する説明の中で、誤っているものを1つ選びなさい。
なお「\」はバックスラッシュに読み替えること。
また、すべてのコードには下記のコードが適切な箇所に書かれているものとする。

declare(strict_types=1);
error_reporting(-1);
下記はマニュアルから一部引用した内容である。

exec ( string $command , array &$output = null , int &$result_code = null ) : string|false
shell_exec ( string $cmd ) : string
system ( string $command , int &$result_code = null ) : string|false

escapeshellarg ( string $arg ) : string
escapeshellarg() は、文字列をシングルクオート で括り、既存のシングルクオートを全てクオート/エスケープします。これにより、文字列を直接シェル関数に渡し、単一の安全な引数として処理することを可能にします
escapeshellcmd ( string $command ) : string
escapeshellcmd() は、文字列中においてシェルコマンドを だまして勝手なコマンドを実行する可能性がある文字をエスケープします。
(中略)
&#;`|*?~^()[]{}$\、\x0A および \xFF については、その文字の前に \ (バックスラッシュ) が追加されます。’ および ” は、対になっていない場合にのみエスケープされます。

proc_nice ( int $increment ) : bool
posix_kill ( int $pid , int $sig ) : bool

PHP で外部プログラムを動かすためには、 exec() 関数、passthru() 関数、shell_exec() 関数、system() 関数、バックティック演算子 ` などを使う。
これらのうち、shell_exec() 関数とバックティック演算子は「同じもの」である。
また、exec() 関数と system() 関数は返り値として「コマンド結果の最後の行を返す」、passthru() 関数は「未整形の出力を表示(stdout に出力)する」ところに差異がある。
そのため、以下のコード

$cmd = 'vmstat 1 1';

echo "exec()\n";
$s = exec($cmd, $output, $return_var);
echo $s, PHP_EOL;

echo PHP_EOL;
echo "shell_exec()\n";
$s = shell_exec($cmd);
echo $s, PHP_EOL;

echo PHP_EOL;
echo "system()\n";
ob_start();
$s = system($cmd, $return_var);
ob_end_clean();
echo $s, PHP_EOL;

を実行すると、結果は次のような表記となる。

exec()
1  0  67236  87052      0 606796    0    0     0     1    1    1  0  0 100  0  0

shell_exec()
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st
1  0  67236  86728      0 606796    0    0     0     1    1    1  0  0 100  0  0

system()
1  0  67236  86564      0 606796    0    0     0     1    1    1  0  0 100  0  0

問題文の内容は正しい⭕です

シェル引数として使われる文字列をエスケープするための関数として、escapeshellarg() 関数と escapeshellcmd() 関数がある。

どちらも「エスケープをする」処理を行うため、どちらを使っても意味合いは同じなので、どちらを使ってもよい。

しかし、外部入力をシェル引数に使う場合は、必ずどちらかの関数でエスケープをする必要がある。

そのため、以下のコード

$arguments = 'ab&c "d ef" > \'gh\'';
$e = escapeshellarg($arguments);
echo $e, PHP_EOL;

$e = escapeshellcmd($arguments);
echo $e, PHP_EOL;

を実行すると、結果は次のとおりとなる。

‘ab&c “d ef” > ‘\”gh’\”’
‘ab&c “d ef” > ‘\”gh’\”’

⚠️ escapeshellarg() と escapeshellcmd() は意味が異なる

  • escapeshellarg() は シェル引数 を安全にエスケープする。
  • escapeshellcmd() は シェルコマンド全体 をエスケープする。

🔥 まとめ目的使用用途
escapeshellarg($arg)シェル引数をエスケープ'user input'exec(), shell_exec(), system()引数に使う
escapeshellcmd($cmd)シェルコマンド全体をエスケープls \& rm -rf /コマンド自体をエスケープする

「どちらを使ってもよい」という説明は 間違い なので注意しましょう! 🚀

よって問題文は誤り❌です

mail() 関数 メール送信処理 additional_headers ヘッダ情報 不正なデータ ヘッダインジェクション X-test: test X-test2: test Bcc: attacker@example.com 対策: ヘッダインジェクションを防ぐ str_replace([“\r”, “\n”], ”, $value) filter_var($value, FILTER_SANITIZE_STRING) mb_send_mail() を使用 不正な改行を取り除くことで、ヘッダインジェクションを防止できる

「システムコールとライブラリ関数を規定した POSIX.1 (IEEE Std 1003.1)」標準ドキュメントで 定義された関数へのインターフェイスが提供されている、POSIX 関数がある。

デフォルトで有効になっているが、Windows 環境では利用できないので注意が必要である。

現在のプロセスのグループ ID を返す posix_getpgrp()、 現在のプロセス ID を返す posix_getpid()、 親プロセスの ID を返す posix_getppid()、 プロセス時間を得る posix_times() などがある。

そのため、以下のコード

var_dump( posix_getpgrp() );
var_dump( posix_getpid() );
var_dump( posix_getppid() );
var_dump( posix_times() );

を実行すると、結果は次のとおりとなる。(プロセスID、時間のため、値は実行毎に変わる)

int(694)
int(694)
int(523)
array(5) {
["ticks"]=>
int(1358853909)
["utime"]=>
int(0)
["stime"]=>
int(0)
["cutime"]=>
int(0)
["cstime"]=>
int(0)
}

この説明に誤りはありません。内容は⭕です

プロセスに働きかける関数として、proc_nice() 関数や posix_kill() 関数がある。

proc_nice() は自プロセスの優先度を変更し、posix_kill() は指定したプロセスにシグナルを送信する。

シグナル SIGKILL は「プロセスの強制終了」なので、SIGKILL を送信されたプログラムはそこで実行を停止する。

そのため、以下のコード

var_dump( trim(`nice`) );
proc_nice(10);
var_dump( trim(`nice`) );

posix_kill(posix_getpid(), SIGKILL);
echo 'test', PHP_EOL;

を実行すると、結果は次のとおりとなる。

string(1) “0”
string(2) “10”
Killed

よって選択肢の内容は正しい⭕です

問題番号正解の選択肢
1PHPの「最新以外の(古い)コード」は、公式サイトでの提供は全くしていない。そのため、古いバージョンのコードが必要な場合、別途「非公式の外部サイト」からソースコードを入手する必要がある。
2論理型 (boolean) は「真偽値」とも呼ばれ、値は true か false か null のいずれかになる。なお、true、false、null の文字は、大文字でも小文字でもよい。
3抽象クラスから継承する際、親クラスで abstract としてマークされた全てのメソッドは子クラスで定義する必要があり、可視性は同等(またはより緩い制約)で、必須引数の数は同じであれば型宣言が異なってもかまわない。
4PHP において、デストラクタは __destruct() メソッドで実装される。親クラスのデストラクタは暗黙的に呼ばれ、呼び出し順序は「子クラスのデストラクタ → 親クラスのデストラクタ」となる。
5__get() はアクセス不能(protected または private)または存在しないプロパティからデータを読み込む際に使用される。なお、__getStatic() は存在せず、オブジェクトや静的コンテキストで動作しない。
6PHP のリファレンス渡しを使用すると、関数内で引数を修正できる。(※※正解テキストでは「呼び出す側で変数に & を付ける必要がある」と記載されています。)
7名前空間は namespace キーワードで宣言する。通常はファイルの先頭に記述する必要があり、名前空間宣言前に書かれたクラスはその名前空間に含まれない。
8PHP 5 では Exception クラスが全例外の基底クラスだったが、PHP 7 以降は Exception クラスは Throwable インタフェースを基底とし、Error クラスが内部エラーの基底クラスとして導入された。
9ArrayAccess インターフェイスは、オブジェクトを配列としてアクセスするための機能を提供する。
10SplFileInfo クラスは、ファイルの情報取得や操作を行うためのクラスである。
11$_COOKIE は、HTTP クッキーから渡された値が連想配列として格納され、また設定も可能である。
12PHP 7.1.x では、文字列操作関数で負のオフセット指定が可能となり、[] や {} による文字単位のアクセスも文字列の末尾からのオフセットとして解釈される。
13可変変数は、スーパーグローバル変数にも使用できる。
14PHP 7.4.x では、波括弧を使った配列や文字列のオフセットアクセスの文法は非推奨となった。
15PHP 8 では、private メソッドの継承に関するルールが変更され、親クラスの同名メソッドの可視性に関係なく子クラスでオーバーライド可能となった。
16break は、現在実行中の for, foreach, while, do-while, switch 構造の実行を終了し、オプションでネストしたループの何段分を抜けるか指定できる。
17XSS 対策として、htmlentities() を適切に使用することで、文字列や配列の入力に対して安全な出力が可能となる。
18アップロードされたファイルの元のファイル名は、$_FILES[‘{formのnameの値}’][‘name’] に格納され、move_uploaded_file() の第二引数として利用できる。
19uniqid() と mt_rand() を組み合わせることで、暗号論的にランダムな(推測困難な)トークンを生成できる。
20PHP のセッションでは、セッション ID がクッキーに保存され、session_set_cookie_params() によりそのクッキーのパラメータを設定できる。
21PHP の変数の参照カウントは、xdebug_debug_zval() で確認でき、オブジェクトを clone した場合は内部的に参照が使われ、一時的に参照カウントが増加する。
22DirectoryIterator クラスは、ディレクトリ内のファイルやサブディレクトリの情報を取得するシンプルなインターフェイスを提供する。
23mail() 関数は、メールを送信するための関数で、第四引数で追加のヘッダー情報を指定できる。
24escapeshellarg() と escapeshellcmd() は、外部入力をシェルコマンドの引数として使用する際に必要なエスケープ処理を行い、どちらを使用しても意味合いは同じである。
25stream_wrapper_register() 関数を使用すると、新しいストリームラッパー(プロトコルハンドラ)を登録できるが、既に存在する場合は失敗する。
26strpos() 関数は、文字列内で指定した部分文字列の最初の出現位置を返し、見つからなければ false を返す。ただし、先頭位置の場合は 0 が返るため注意が必要。
27Phar のスタブには __HALT_COMPILER() が使用され、これ以降のコードはコンパイルされない。
28function_exists() 関数は、指定された関数が定義されているかどうかをチェックし、存在すれば true を返す。
29openssl_decrypt() 関数は、openssl_encrypt() で暗号化されたデータを正しく復号し、元のデータを取り戻す。
30strtotime() 関数は、英語形式の日付文字列を Unix タイムスタンプに変換し、無効な日付の場合は false を返す。