算術式で連想配列を安全に使用するには?

算術式で連想配列を安全に使用するには?

Bourneなどの一部のシェルは連想配列をサポートしていksh93ます。zshbash

一般的な用途は、特定の文字列の発生回数を数えることです。

しかし、私は次のようなものを見つけました。

組版-A 個数
 ((count[$var]++))

特定の価値のために働かないでください$varすべてのコマンドを実行脆弱性コンテンツが$var攻撃者の管理下にある場合、またはその可能性がある場合。

なぜそんなことですか?問題のある値は何ですか?この問題をどのように解決できますか?

答え1

問題は、次のシェル算術式にあります。

  • 内部$((...))(POSIX)、
  • ((...))(ksh/bash/zsh)
  • 配列インデックス
  • 一部のシェル組み込み関数の一部のパラメータ
  • 内部数値比較演算子のオペランド[[...]]

単語拡張(${param}、、、、、、、実行​​$((...))$[...]$(...)`...`${ ...; }最初、結果テキストを算術式として解釈します。

この場合、$((...))これはPOSIXの要件でもあります。

これにより、このようなop=+; echo "$(( 1 $op 2 ))"操作が可能であり、出力が評価される式であるため、a=1+1; echo "$(($a * 2))"出力で3はない理由も説明します。41+1 * 2

これも一部算術式で洗練されていないデータを使用することがセキュリティの脆弱性である理由一般的に言えば。

次のようなものにも適用されることを見落としやすい。

(( assoc[$var]++ ))

上記以外ksh93編集するここでbash 5.2+では、以下を参照してください)$var最初に展開し、結果を解釈します。

これは、orが$var含まれている場合、or式が評価され、/がそこに特別な意味を持つことを意味します。ならばになります。@*assoc[@]++assoc[*]++@*$varx] + 2 + assoc[yassoc[x] + 2 + assoc[y]

現在、通常はそのようなものが含まれていて$(( $var ))も、2次拡張は発生せず実行されません。しかし、すでに見たように$var$(reboot)rebootシェル算術評価における整理されていないデータ使用のセキュリティ影響word[...]、再帰拡張を許可するために内部的に発生した場合、例外が存在します。問題の源は不幸です特徴Korn シェルの if にはvar算術式が含まれており、$((var))の算術式は再帰的に$varwhen などとvar2='var3 + 1' var='var2 + 1'評価されます。これは POSIX で許可されているが必要とされない操作です。

これは配列メンバに拡張されるため、配列インデックスの内容が最終的に再帰的に計算されることを意味します。したがって、この場合、最終的には$varに呼び出され$(reboot)ます。(( assoc[$var]++ ))reboot

ksh93ある程度の解決策があるようですが$var含まれていない場合にのみ適用されます$。したがって、ksh93はvar=']'var='@'またはで使用できますが、一緒var='`reboot`'に使用することはできません$(reboot)

たとえば、rebootharmlessに置き換えると次のようになりますuname>&2

$ var='1$(uname>&2)' ksh -c 'typeset -A a; (( a[$var]++ )); typeset -p a'
Linux
typeset -A a=([1]=1)
$ var='1$(uname>&2)' bash -c 'typeset -A a; (( a[$var]++ )); typeset -p a'
Linux
Linux
declare -A a=([1]="1" )
$ var='1$(uname>&2)' zsh -c 'typeset -A a; (( a[$var]++ )); typeset -p a'
Linux
Linux
typeset -A a=( [1]=1 )

コマンドはuname最終的に実行されました(bash(<5.2)で2回、zsh現在の値を取得するために1回、割り当てを実行するために2回目に推測)。

バージョン5.0では、bashはassoc_expand_once動作を変更するオプションを追加しました。

$ var='1$(uname>&2)' bash -O assoc_expand_once -c 'typeset -A a; ((a[$var]++)); typeset -p a'
declare -A a=(["1\$(uname>&2)"]="1" )

これで動作しますが、またはの問題を解決できないため、@解決されませ*]すべてのコマンドを実行脆弱性:

$ var='x]+b[1$(uname>&2)' bash -O assoc_expand_once -c 'typeset -A a; ((a[$var]++)); typeset -p a'
Linux
declare -A a

(今回はuname評価の一環として実施明らかにarray( b) インデックス評価)。

問題のある文字のリストはシェルによって異なります。$は3つすべての問題であり、、、、\および`に関する問題[です]。と同じで、NULL値です。また、一部のロケールでは、一部の文字のエンコーディングにエンコーディングが含まれているか、少なくとも含まれていないため、問題が発生する可能性があります。これを脱出する方法は、3つのシェルすべてで異なる方法で行う必要があります。bashzsh"'bash@*\`[]

この問題を解決するには、次の操作を行います。

assoc[$var]=$(( ${assoc[$var]} + 1 ))

代わりに。それは:

  1. 連想配列メンバーへの割り当ては行われません。の一部として算術式ですが、連想配列メンバー割り当てのみを実行します。つまり、連想配列のメンバーを対象とする、、、、、=...算術演算子を使用しないでください。++--+=/=
  2. 算術式で連想配列を参照するとき、または単純な数字ではなく算術式を含めたい場合は、assoc[$var]${assoc[$var]}または$assoc[$var]in zsh)を使用しないでください。(${assoc[$var]})

しかし、いつものように連想配列のメンバーは、ユーザーが制御する必要があり、一般的な数字が望ましく、他のパラメーター拡張と同様に、周囲にスペースを入れるのが最善です。たとえば、負の値で問題が発生する可能性がある後者((1 - $var))よりも優先されます。((1-$var))((1--1))--1

注目すべきもう1つの点は、$var空の場合、inは(( 1 + var ))まだvar算術式構文のトークンであり、その値はifです0。しかし、これ(( 1 + $var ))は算術式になりますが、1 +これは構文エラーです(ただし、単項演算子を呼び出すと(( $var + 1 ))問題になりません)。+ 1+

その他の方法bash(5.1以下、assoc_expand_onceオプションが以下の場合)いいえ有効)またはzsh(しかしそうではありません)ksh93]キャラクターには\まだ問題があります)、上記の2番目の再帰解釈が出るまで拡張が遅れます。

  • (( assoc[\$var]++ ))
  • let 'assoc[$var]++'(必ずお使いください。一つここに引用)
  • incr='assoc[$var]++'; (($incr))(でも((incr))
  • ((' assoc[$var]++ '))または(( assoc['$var']++ ))bashのみ)。

これは算術評価によって生成された終了状態を維持するという利点があります(成功0以外の場合)次のことができます。

if (( assoc[\$var]++ )); then
  printf '%s\n' "$var was already seen"
fi

これでシェル関連の問題が残りますbashbash連想配列はヌルキーをサポートしません。どちらもand(not)assoc[]=xでは失敗しますが、isemptyはorでは機能しますが、orでは機能しません。これでb​​ash-5.1でもサポートされていますが。bashzshksh93assoc[$var]$varzshksh93bashzshassoc+=('' value)bash

したがって、排他的に使用され、bashNULLキーが可能な値の1つである場合、唯一のオプションは固定プレフィックス/サフィックスを追加することです。たとえば、次のようにします。

assoc[.$var]=$(( ${assoc[.$var]} + 1 ))

または:

let 'assoc[.$var]++'
(( assoc[.\$var]++ ))
...

2023年編集

上記の回避策のほとんどは、もはやbash-5.2では動作しません。

bash-5.2.21では(( assoc[$var]++ ))(空でない場合)および、またはを含む場合でも$var安全であるようです。(( assoc[.$var]++ ))$var]\*@

これはまだACEの脆弱性の以前のバージョンです。

5.2および5.2より前のバージョンで動作する唯一の方法は次のとおりです。

これらのうち2番目だけがksh93、zsh、bashで動作します。

特に、(( assoc[\$var] ))zshで動作していたメソッドはbash-5.2では動作しなくなりました。

関連情報