有時,Bash 指令碼需要建立臨時檔案或臨時目錄。
常見的做法是,自己在/tmp
目錄裡面生成一個檔案,這樣做有很多弊端。本文介紹如何安全地處理臨時檔案。
一、臨時檔案的安全問題
直接建立臨時檔案,尤其在/tmp
目錄裡面,往往會導致安全問題。
首先,/tmp
目錄是所有人可讀寫的,任何使用者都可以往該目錄裡面寫檔案。建立的臨時檔案也是所有人可讀的。
$ touch /tmp/info.txt $ ls -l /tmp/info.txt -rw-r--r-- 1 ruanyf ruanyf 0 12月 28 17:12 /tmp/info.txt
上面命令在/tmp
目錄直接建立檔案,該檔案預設是所有人可讀的。
其次,如果攻擊者知道臨時檔案的檔名,他可以建立符號連結,連結到臨時檔案,可能導致系統執行異常。攻擊者也可能向指令碼提供一些惡意資料。因此,臨時檔案最好使用不可預測、每次都不一樣的檔名。
最後,臨時檔案使用完畢,應該刪除。但是,指令碼意外退出時,往往會忽略清理臨時檔案。
二、臨時檔案的最佳實踐
指令碼生成臨時檔案,應該遵循下面的規則。
- 建立前檢查檔案是否已經存在。
- 確保臨時檔案已成功建立。
- 臨時檔案必須有許可權的限制。
- 臨時檔案要使用不可預測的檔名。
- 指令碼退出時,要刪除臨時檔案(使用
trap
命令)。
三、mktemp 命令的用法
mktemp
命令就是為安全建立臨時檔案而設計的。雖然在建立臨時檔案之前,它不會檢查臨時檔案是否存在,但是它支援唯一檔名和清除機制,因此可以減輕安全攻擊的風險。
直接執行mktemp
命令,就能生成一個臨時檔案。
$ mktemp /tmp/tmp.4GcsWSG4vj $ ls -l /tmp/tmp.4GcsWSG4vj -rw------- 1 ruanyf ruanyf 0 12月 28 12:49 /tmp/tmp.4GcsWSG4vj
上面命令中,mktemp
命令生成的臨時檔名是隨機的,而且許可權是隻有使用者本人可讀寫。
Bash 指令碼使用mktemp
命令的用法如下。
#!/bin/bash TMPFILE=$(mktemp) echo "Our temp file is $TMPFILE"
為了確保臨時檔案建立成功,mktemp
命令後面最好使用 OR 運算子(||
),指定建立失敗時退出指令碼。
#!/bin/bash TMPFILE=$(mktemp) || exit 1 echo "Our temp file is $TMPFILE"
為了保證指令碼退出時臨時檔案被刪除,可以使用trap
命令指定退出時的清除操作(詳見後文)。
#!/bin/bash trap 'rm -f "$TMPFILE"' EXIT TMPFILE=$(mktemp) || exit 1 echo "Our temp file is $TMPFILE"
四、mktemp 命令的引數
-d
引數可以建立一個臨時目錄。
$ mktemp -d /tmp/tmp.Wcau5UjmN6
-p
引數可以指定臨時檔案所在的目錄。預設是使用$TMPDIR
環境變數指定的目錄,如果這個變數沒設定,那麼使用/tmp
目錄。
$ mktemp -p /home/ruanyf/ /home/ruanyf/tmp.FOKEtvs2H3
-t
引數可以指定臨時檔案的檔名模板,模板的末尾必須至少包含三個連續的X
字元,表示隨機字元,建議至少使用六個X
。預設的檔名模板是tmp.
後接十個隨機字元。
$ mktemp -t mytemp.XXXXXXX /tmp/mytemp.yZ1HgZV
五、trap 命令的用法
trap
命令用來在 Bash 指令碼中響應系統訊號。
最常見的系統訊號就是 SIGINT(中斷),即按 Ctrl + C 所產生的訊號。trap
命令的-l
引數,可以列出所有的系統訊號。
$ trap -l 1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL 5) SIGTRAP 6) SIGABRT ... ...
trap
的命令格式如下。
$ trap [動作] [訊號]
上面程式碼中,"動作"是一個 Bash 命令,"訊號"常用的有以下幾個。
- HUP:編號1,指令碼與所在的終端脫離聯絡。
- INT:編號2,使用者按下 Ctrl + C,意圖讓指令碼中止執行。
- QUIT:編號3,使用者按下 Ctrl + 斜槓,意圖退出指令碼。
- KILL:編號9,該訊號用於殺死程式。
- TERM:編號15,這是
kill
命令發出的預設訊號。- EXIT:編號0,這不是系統訊號,而是 Bash 指令碼特有的訊號,不管什麼情況,只要退出指令碼就會產生。
trap
命令響應EXIT
訊號的寫法如下。
$ trap 'rm -f "$TMPFILE"' EXIT
上面命令中,指令碼遇到EXIT
訊號時,就會執行rm -f "$TMPFILE"
。
trap 命令的常見使用場景,就是在 Bash 指令碼中指定退出時執行的清理命令。
#!/bin/bash trap 'rm -f "$TMPFILE"' EXIT TMPFILE=$(mktemp) || exit 1 ls /etc > $TMPFILE if grep -qi "kernel" $TMPFILE; then echo 'find' fi
上面程式碼中,不管是指令碼正常執行結束,還是使用者按 Ctrl + C 終止,都會產生EXIT
訊號,從而觸發刪除臨時檔案。
注意,trap
命令必須放在指令碼的開頭。否則,它上方的任何命令導致指令碼退出,都不會被它捕獲。
如果trap
需要觸發多條命令,可以封裝一個 Bash 函式。
function egress { command1 command2 command3 } trap egress EXIT
六、參考連結
- Working with Temporary Files and Directories in Shell Scripts, Steven Vona
- Using Trap to Exit Bash Scripts Cleanly, Steven Vona
- Sending and Trapping Signals
(完)