安全な一時ファイルの作成と削除の方法 - 拡張 POSIX シェルスクリプト Advent Calendar 2013 - ダメ出し Blog

2013-12-06(Fri) [sh][shell][security] [更新履歴]

拡張 POSIX シェルスクリプト Advent Calendar 2013、6日目の記事です。

本日のお題は「一時ファイルの作成と削除」ですが、 よくあるシェルスクリプトのダメ出しネタになってしまいました。

安全な一時ファイルの作成方法

たとえば、こんな感じのありがち(?)なスクリプトがあったとします。 意味のない処理内容ですが、雰囲気だけ察してください。

#!/bin/bash

tmpfile=/tmp/words.tmp
rm -f $tmpfile

echo 'しにたい' >>$tmpfile
echo 'とりあえずねよう' >>$tmpfile

sleep 3

echo -n '今どんな気持ち? '
read feeling
echo $feeling >>$tmpfile

cat $tmpfile
rm $tmpfile

全然駄目ですね。 もし「何にも問題ないじゃないか!」という感想をお持ちの方は、 ぜひ悔い改めてください。

肝心の「一時ファイルの作成」だけでも、次の問題があります。

競合の問題は運用で回避すれば発生しないので、それでよければ無視できます。(酷い)

ファイル内容漏洩の問題は、この例のように機密性のないどうでもいい内容であったり、 悪意ある者がローカルユーザーなどの参照する手段・権限を持たない、 かつほかの脆弱性を突かれてそれを奪取されたときのリスクを考慮しなくてよいのであれば、 無視できます。(これも酷い)

シンボリックリンク攻撃の問題も、ファイル内容漏洩と同様です。 (一時ファイルの参照ではなく、同名のシンボリックリンクを作れるかどうかの問題)

これらの問題の対処方法ですが、素人は黙って mktemp(1) を使ってください。 それだけで競合も内容漏洩もシンボリックリンク攻撃も避け、 安全にファイルを作成することができます。

#!/bin/bash

tmpfile=$(mktemp)

echo 'しにたい' >>$tmpfile
echo 'とりあえずねよう' >>$tmpfile

sleep 3

echo -n '今どんな気持ち? '
read feeling
echo $feeling >>$tmpfile

cat $tmpfile
rm $tmpfile

mktemp は次のような仕様でファイルを作成します。

よくありがちなシェルスクリプトのダメ出し

一時ファイルの作成は問題なくなりましたが、まだ駄目な点があります。

さらに修正してやります。

#!/bin/bash
# or
#!/bin/ksh
#!/bin/zsh

if [[ -n ${ZSH_VERSION-} ]]; then
  emulate -R ksh
  set -o BSD_ECHO
fi

set -e

tmpfile=$(mktemp "/tmp/${0##*/}.tmp.XXXXXX")

echo 'しにたい' >>"$tmpfile"
echo 'とりあえずねよう' >>"$tmpfile"

sleep 3

echo -n '今どんな気持ち? '
IFS= read -r feeling
echo "$feeling" >>"$tmpfile"

cat "$tmpfile"
rm "$tmpfile"

簡単に解説します。

これで概ね問題のないシェルスクリプトになりました。

安全な一時ファイルの削除方法

残る問題が一つあります。

これは先日紹介した方法でスクリプト終了イベントとシグナルを捕捉 すればいいですね。

#!/bin/bash
# or
#!/bin/ksh
#!/bin/zsh

if [[ -n $ZSH_VERSION ]]; then
  emulate -R ksh
  set -o BSD_ECHO
fi

set -e

unset tmpfile

atexit() {
  [[ -n ${tmpfile-} ]] && rm -f "$tmpfile"
}

trap atexit EXIT
trap 'rc=$?; trap - EXIT; atexit; exit $?' INT PIPE TERM

tmpfile=$(mktemp "/tmp/${0##*/}.tmp.XXXXXX")

echo 'しにたい' >>"$tmpfile"
echo 'とりあえずねよう' >>"$tmpfile"

sleep 3

echo -n '今どんな気持ち? '
IFS= read -r feeling
echo "$feeling" >>"$tmpfile"

cat "$tmpfile"

これで完成です。


AIX など mktemp コマンドがない環境の場合は、 別途用意することをお勧めします。

ちなみに mktemp 相当の機能はシェルだけでも実装可能です。 もし機会があれば、実装の紹介とともに、 どのようにして安全に一時ファイルを作成すればよいかを解説したいと思います。

作りました


ところで、12月25日はクリスマスな上に、 OSS 界隈で地味に活躍されているふみやすさんの誕生日ですね!
http://www.amazon.co.jp/registry/wishlist/27M7TV8CEEF6G?sort=priority

逆に、あなたの書いた OSS や Blog や Advent Calendar が気に入ったら何か送りたく なってしまうかもしれないので、プロフィールや Web サイトに あなたの Amazon 欲しいものリストの URL を貼っておいてくださいね!


私が勤める OSSTech っていう某弊社で社員募集しているようです。 人材紹介会社を介さなければ、入社後に 20万円のボーナス! 「ふみやすっていう人に紹介された」と言ってもらえると私にもボーナス!! → https://www.osstech.co.jp/recruit/


よろしければ、これまで参加した/参加予定のほかの Advent Calendar もどうぞ。