欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

shell中實用eval命令和安全問題

 更新時間:2023年10月13日 08:30:19   作者:qq_21305943  
eval命令非常強(qiáng)大,但也非常容易被濫用,本文主要介紹了shell中實用eval命令和安全問題,具有一定的參考價值,感興趣的可以了解一下

eval命令非常強(qiáng)大,但也非常容易被濫用。

它會導(dǎo)致代碼被解析兩次而不是一次。這意味著,如果你的代碼中包含變量引用,shell解析器將評估該變量的內(nèi)容。如果變量包含一個shell命令,shell可能會運行該命令,無論你是否希望運行它。這可能會導(dǎo)致意外的結(jié)果,特別是當(dāng)變量可以從不受信任的來源(如用戶或用戶創(chuàng)建的文件)讀取時。

請注意,eval命令在編程中被廣泛認(rèn)為是危險的。它可以執(zhí)行任意的Shell代碼,包括惡意代碼,因此應(yīng)該謹(jǐn)慎使用。

Bash的名稱引用問題

Bash 4.3引入了declare -n("名稱引用")來模仿Korn shell的nameref??特性,允許變量保存對其他變量的引用。然而,Bash中使用的實現(xiàn)存在一些問題。

首先,Bash的declare -n??實際上并沒有避免名稱沖突問題:

$ foo() { declare -n v=$1; }
$ bar() { declare -n v=$1; foo v; }
$ bar v
bash: warning: v: circular name reference

換句話說,我們無法給名稱引用指定一個安全的名稱。如果調(diào)用者的變量恰好具有相同的名稱,那就麻煩了。

其次,Bash的名稱引用實現(xiàn)仍然允許任意代碼執(zhí)行:

$ foo() { declare -n var=$1; echo "$var"; }
$ foo 'x[i=$(date)]'
bash: i=Thu Mar 27 16:34:09 EDT 2034: syntax error in expression (error token is "Mar 27 16:34:09 EDT 2023")

這個例子并不優(yōu)雅,但你可以清楚地看到date??命令實際上被執(zhí)行了。這絕不是我們想要的結(jié)果。

盡管存在這些缺點,declare -n??特性是朝著正確方向邁出的一步。但你必須小心選擇一個調(diào)用者不會使用的名稱(這意味著你需要對調(diào)用者有某種控制,即使只是告訴他們“不要使用以_my_pkg??開頭的變量”),并且必須拒絕不安全的輸入。

eval的良好使用示例

eval最常見的正確使用方式是從專門設(shè)計為以這種方式使用的程序輸出中讀取變量。例如,

# 在舊系統(tǒng)上,調(diào)整窗口大小后必須運行以下命令:
eval "`resize`"
# 更高級的用法:獲取SSH私鑰的密碼短語。
# 這通常從.xsession或.profile類型的文件執(zhí)行。
# ssh-agent生成的變量將被導(dǎo)出到用戶會話中的所有進(jìn)程,以便之后的ssh命令可以繼承這些變量。
eval "`ssh-agent -s`"

eval還有其他用途,特別是在創(chuàng)建變量時(參考indirect variable references ↗)。以下是一種解析不帶參數(shù)的命令行選項的示例:

# POSIX
#
# 動態(tài)創(chuàng)建選項變量。嘗試調(diào)用:
#
#    sh -x example.sh --verbose --test --debug
for i; do
    case $i in
       --test|--verbose|--debug)
            shift                   # 從命令行中移除選項
            name=${i#--}            # 刪除選項前綴
            eval "$name=\$name"    # 創(chuàng)建*新*變量
            ;;
    esac
done
echo "verbose: $verbose"
echo "test: $test"
echo "debug: $debug"

那么,為什么這個版本是可接受的呢?這是因為我們限制了eval命令的使用,只有在輸入是一組有限的已知值之一時才會執(zhí)行。因此,用戶無法濫用它以導(dǎo)致任意命令執(zhí)行——任何包含奇怪內(nèi)容的輸入都不會匹配三個預(yù)定的可能輸入之一。

請注意,這仍然是不推薦的:這是一條很陡峭的道路,稍后的維護(hù)很容易將這段代碼變成危險的內(nèi)容。例如,你想要添加一個功能,允許傳遞一堆不同的--test-xyz選項。你將--test更改為--test-*,而不費力地檢查腳本的其他部分的實現(xiàn)。你測試你的用例,一切正常。不幸的是,你剛剛引入了任意命令執(zhí)行:

$ ./foo --test-'; ls -l /etc/passwd;x='
-rw-r--r-- 1 root root 943 2007-03-28 12:03 /etc/passwd

再次強(qiáng)調(diào):允許eval命令在未經(jīng)過濾的用戶輸入上使用會導(dǎo)致任意命令執(zhí)行。

盡一切可能避免將數(shù)據(jù)傳遞給eval,即使你的代碼似乎處理了所有邊界情況。

如果你經(jīng)過深思熟慮并向#bash尋求了替代方法,但沒有找到任何方法,請?zhí)?quot;Robust eval usage"部分。

使用declare的問題

使用declare能更好地完成這個任務(wù)嗎?

for i in "$@"; do
    case "$i" in
        --test|--verbose|--debug)
            shift                   # 從命令行中移除選項
            name=${i#--}            # 刪除選項前綴
            declare $name=Yes       # 設(shè)置默認(rèn)值
            ;;
        --test=*|--verbose=*|--debug=*)
            shift
            name=${i#--}
            value=${name#*=}        # value是第一個單詞后面的內(nèi)容和=
            name=${name%%=*}        # 僅限于第一個單詞(即使值中有另一個=)
            declare $name="$value"  # 創(chuàng)建*新*變量
            ;;
    esac
done

請注意,--name用于默認(rèn)值,--name=value是必需的格式。

以下是eval的一個良好使用示例,用于從專門設(shè)計為以這種方式使用的程序輸出中讀取變量:

# 在舊系統(tǒng)上,調(diào)整窗口大小后必須運行以下命令:
eval "`resize`"
# 更高級的用法:獲取SSH私鑰的密碼短語。
# 這通常從.xsession或.profile類型的文件執(zhí)行。
# ssh-agent生成的變量將被導(dǎo)出到用戶會話中的所有進(jìn)程,以便之后的ssh命令可以繼承這些變量。
eval "`ssh-agent -s`"

eval還可以用于創(chuàng)建變量時,尤其是在創(chuàng)建間接變量引用時。下面是一個解析不帶參數(shù)的命令行選項的示例:

# POSIX
#
# 動態(tài)創(chuàng)建選項變量。嘗試調(diào)用:
#
#    sh -x example.sh --verbose --test --debug
for i; do
    case $i in
       --test|--verbose|--debug)
            shift                   # 從命令行中移除選項
            name=${i#--}            # 刪除選項前綴
            eval "$name=\$name"    # 創(chuàng)建*新*變量
            ;;
    esac
done
echo "verbose: $verbose"
echo "test: $test"
echo "debug: $debug"

盡管這個示例中的eval使用看起來安全,但仍然不推薦廣泛使用eval命令,因為它需要非常小心的輸入過濾和驗證,以避免任意命令執(zhí)行漏洞。盡量避免將數(shù)據(jù)傳遞給eval,并尋找替代方案,以增加腳本的安全性。

使用declare存在的問題

難道使用declare不能更好地解決這個問題嗎?

for i in "$@"; do
    case "$i" in
        --test|--verbose|--debug)
            shift                   # 從命令行中移除選項
            name=${i#--}            # 刪除選項前綴
            declare $name=Yes       # 設(shè)置默認(rèn)值
            ;;
        --test=*|--verbose=*|--debug=*)
            shift
            name=${i#--}
            value=${name#*=}        # 值是等號后面的內(nèi)容
            name=${name%%=*}        # 僅限于第一個單詞的名稱(即使值中還有另一個等號)
            declare $name="$value"  # 創(chuàng)建*新的*變量
            ;;
    esac
done

請注意,默認(rèn)情況下,--name和--name=value是必需的格式。

對于某些輸入,declare確實可以更好地工作:

griffon:~$ name='foo=x;date;x'
griffon:~$ declare $name=Yes
griffon:~$ echo $foo
x;date;x=Yes

但它仍然會導(dǎo)致數(shù)組變量中的任意代碼執(zhí)行:

attoparsec:~$ echo $BASH_VERSION
4.2.24(1)-release
attoparsec:~$ danger='( $(printf "%s!\n" DANGER >&2) )'
attoparsec:~$ declare safe=${danger}
attoparsec:~$ declare -a unsafe
attoparsec:~$ declare unsafe=${danger}
DANGER!

這段代碼展示了使用declare可能引發(fā)的安全問題。在某些情況下,使用declare可能會導(dǎo)致任意代碼執(zhí)行,從而產(chǎn)生潛在的安全漏洞。在這個例子中,變量的值包含了一個命令,當(dāng)使用declare聲明變量時,該命令將被執(zhí)行。這可能導(dǎo)致不受信任的代碼執(zhí)行,從而引發(fā)安全問題。

為了確保腳本的安全性,應(yīng)該避免將不受信任的數(shù)據(jù)傳遞給declare命令。如果需要動態(tài)創(chuàng)建變量,可以考慮使用其他安全的方法或?qū)ふ姨娲桨?,以避免潛在的安全風(fēng)險。

強(qiáng)大的eval用法

幾乎總是(至少在Bash中99%或更多的時間內(nèi),但也適用于更簡潔的shell),正確地使用eval的方式是在庫代碼中生成隱藏在函數(shù)背后的抽象層。這允許函數(shù)具有以下功能:

  • 向函數(shù)的調(diào)用者呈現(xiàn)一個明確定義的接口,指定哪些輸入必須由程序員嚴(yán)格控制,哪些可能是不可預(yù)測的,例如受用戶輸入影響的副作用。重要的是要記錄哪些選項和參數(shù)在沒有控制的情況下是不安全的。
  • 對某些類型的輸入進(jìn)行輸入驗證,如果可行,例如整數(shù)。在這種情況下,可以輕松地退出并返回一個錯誤狀態(tài),該錯誤狀態(tài)可以由函數(shù)的調(diào)用者處理。
  • 創(chuàng)建隱藏使用eval的丑陋實現(xiàn)細(xì)節(jié)的抽象。

通常,當(dāng)滿足以下至少全部條件時,eval是正確的:

  • 可能的所有eval參數(shù)都保證不會在任何情況下產(chǎn)生有害的副作用或?qū)е氯我獯a的執(zhí)行。這些輸入是靜態(tài)編碼的,不與不受控制的動態(tài)代碼交互,并且/或經(jīng)過徹底驗證。這就是為什么函數(shù)很重要,因為你不一定需要自己保證這個保證。只要您的函數(shù)記錄了哪些輸入可能是危險的,您就可以將這個任務(wù)委托給函數(shù)的調(diào)用者。
  • eval用法向用戶或程序員呈現(xiàn)了一個清晰的接口。
  • eval使得原本不可能的事情成為可能,而無需編寫更大、更慢、更復(fù)雜、更危險、更丑陋、更不實用的代碼。

如果出于某種原因仍然需要動態(tài)構(gòu)建Bash代碼并評估它,請確保采取以下預(yù)防措施:

  • 始終引用eval表達(dá)式:eval 'a=b'
  • 始終使用單引號引用代碼,并使用printf的%q將數(shù)據(jù)擴(kuò)展到其中:eval "$(printf 'myvar=%q' "$value")"
  • 不要使用動態(tài)變量名。即使使用了小心的%q用法,這也可能會被利用。

為什么要注意?如果未能遵循上述建議,以下是腳本可能會受到利用的示例:

  • 如果不對代碼進(jìn)行單引號引用,則存在將數(shù)據(jù)擴(kuò)展到其中而沒有進(jìn)行%q處理的風(fēng)險。這意味著該數(shù)據(jù)可以自由執(zhí)行:
name='Bob; echo I am arbitrary code'; eval "user=$name"
  • 即使在對輸入數(shù)據(jù)進(jìn)行%q處理之后再將其視為變量名進(jìn)行處理,如果賦值中存在非法變量名,Bash將會在PATH中搜索命令:
echo 'echo I am arbitrary code' > /usr/local/bin/a[1]=b; chmod +x /usr/local/bin/a[1]=b; var='a[1]' value=b; eval "$(printf '%q=%q' "$var" "$value")"

到此這篇關(guān)于shell中實用eval命令和安全問題的文章就介紹到這了,更多相關(guān)shell eval命令 內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評論