3

私は bash スクリプトをできるだけ手続き型にプログラムすることを好みます。そうしようとして遭遇した問題の 1 つは、関数間で配列データを渡すときに発生します。これは、bash では十分にサポートされていないタスクです。

例として、複数のハードコーディングされた引用符付きの値を使用して bash で配列を初期化するのは簡単です。各値には複数の単語が含まれる場合があります。

declare -a LINES=( "Hello there" "loyal user" )
echo "Line 0: '${LINES[0]}'"
echo "Line 1: '${LINES[1]}'"
# Line 0: 'Hello there'
# Line 1: 'Loyal user'

ただし、そのようなハードコードされた値を関数の出力に置き換えると、うまく機能しないようです。

getLines() {
    echo "\"Hello there\" \"loyal user\""
}

local LINE_STR=$( getLines )
declare -a LINES=( ${LINE_STR} )
echo "Line 0: '${LINES[0]}'"
echo "Line 1: '${LINES[1]}'"
# Line 0: '"Hello'
# Line 1: 'there"'

この問題を克服するために、許可された bash ステートメントのほぼすべての順列を試しました。うまくいくと思われる 1 つのアプローチは「eval」です。

local LINE_STR=$( getLines )
eval declare -a LINES=( ${LINE_STR} )
echo "Line 0: '${LINES[0]}'"
echo "Line 1: '${LINES[1]}'"
# Line 0: 'Hello there'
# Line 1: 'loyal user'

ただし、このアプローチには、次のようにセキュリティ上の懸念があります。

emulateUnsafeInput() {
    echo "\"\`whoami\` just got haxxored\" \"Hahaha!\""
}

local LINE_STR=$( emulateUnsafeInput )
eval declare -a LINES=( "${LINE_STR}" )
echo "Line 0: '${LINES[0]}'"
echo "Line 1: '${LINES[1]}'"
# Line 0: 'root just got haxxored'
# Line 1: 'Hahaha!'

「read -a」は可能な解決策のように思えますが、データがパイプされたときに「read」がサブシェルで動作し、その変数スタックを呼び出しスクリプトの変数スタックから効果的に分離するため、問題があります。

「評価」アプローチのセキュリティ上の懸念を軽減するために、どのようなソリューションを検討する必要がありますか? 私が試した無数のアプローチを示す次のスクリプトを含めました。

#!/bin/bash

getLines() {
    echo "\"Hello there\" \"loyal user\""
}

emulateUnsafeInput() {
    echo "\"\`whoami\` just got haxxored\" \"Hahaha!\""
}

execute() {
(
    echo Test 01
    declare -a LINES=( "Hello there" "loyal user" )
    echo "Line 0: '${LINES[0]}'"
    echo "Line 1: '${LINES[1]}'"
    # Line 0: 'Hello there'
    # Line 1: 'loyal user'
);(
    echo Test 02
    local LINE_STR=$( getLines )
    declare -a LINES=( ${LINE_STR} )
    echo "Line 0: '${LINES[0]}'"
    echo "Line 1: '${LINES[1]}'"
    # Line 0: '"Hello'
    # Line 1: 'there"'
);(
    echo Test 03
    local LINE_STR=$( getLines )
    declare -a LINES=( "${LINE_STR}" )
    echo "Line 0: '${LINES[0]}'"
    echo "Line 1: '${LINES[1]}'"
    # Line 0: '"Hello there" "loyal user"'
    # Line 1: ''
);(
    echo Test 04
    local LINE_STR=$( getLines )
    eval declare -a LINES=( ${LINE_STR} )
    echo "Line 0: '${LINES[0]}'"
    echo "Line 1: '${LINES[1]}'"
    # Line 0: 'Hello there'
    # Line 1: 'loyal user'
);(
    echo Test 05
    local LINE_STR=$( getLines )
    eval declare -a LINES=( "${LINE_STR}" )
    echo "Line 0: '${LINES[0]}'"
    echo "Line 1: '${LINES[1]}'"
    # Line 0: 'Hello there'
    # Line 1: 'loyal user'
);(
    echo Test 06
    local LINE_STR=$( getLines )
    declare -a LINES=( $( echo ${LINE_STR} ) )
    echo "Line 0: '${LINES[0]}'"
    echo "Line 1: '${LINES[1]}'"
    # Line 0: '"Hello'
    # Line 1: 'there"'
);(
    echo Test 07
    local LINE_STR=$( getLines )
    declare -a LINES=( $( echo "${LINE_STR}" ) )
    echo "Line 0: '${LINES[0]}'"
    echo "Line 1: '${LINES[1]}'"
    # Line 0: '"Hello'
    # Line 1: 'there"'
);(
    echo Test 08
    local LINE_STR=$( getLines )
    declare -a LINES=( $( eval echo ${LINE_STR} ) )
    echo "Line 0: '${LINES[0]}'"
    echo "Line 1: '${LINES[1]}'"
    # Line 0: 'Hello'
    # Line 1: 'there'
);(
    echo Test 09
    local LINE_STR=$( getLines )
    declare -a LINES=( $( eval echo "${LINE_STR}" ) )
    echo "Line 0: '${LINES[0]}'"
    echo "Line 1: '${LINES[1]}'"
    # Line 0: 'Hello'
    # Line 1: 'there'
);(
    echo Test 10
    local LINE_STR=$( emulateUnsafeInput )
    eval declare -a LINES=( ${LINE_STR} )
    echo "Line 0: '${LINES[0]}'"
    echo "Line 1: '${LINES[1]}'"
    # Line 0: 'root just got haxxored'
    # Line 1: 'Hahaha!'
);(
    echo Test 11
    local LINE_STR=$( emulateUnsafeInput )
    eval declare -a LINES=( "${LINE_STR}" )
    echo "Line 0: '${LINES[0]}'"
    echo "Line 1: '${LINES[1]}'"
    # Line 0: 'root just got haxxored'
    # Line 1: 'Hahaha!'
);(
    echo Test 12
    local LINE_STR=$( emulateUnsafeInput )
    declare -a LINES=( $( eval echo ${LINE_STR} ) )
    echo "Line 0: '${LINES[0]}'"
    echo "Line 1: '${LINES[1]}'"
    # Line 0: 'root'
    # Line 1: 'just'
);(
    echo Test 13
    local LINE_STR=$( emulateUnsafeInput )
    declare -a LINES=( $( eval echo "${LINE_STR}" ) )
    echo "Line 0: '${LINES[0]}'"
    echo "Line 1: '${LINES[1]}'"
    # Line 0: 'root'
    # Line 1: 'just'
)
}

execute
4

3 に答える 3

2

以下は、配列要素内のスペースを正しく処理します

#! /bin/bash

# $ ./return_an_array.sh 
# ./return_an_array.sh: line 9: declare: returned_array: not found
# declare -a returned_array='([0]="one" [1]="two three")'

return_an_array()
{
    local -a an_array=( 'one' 'two three' )
    declare -p an_array
}

declare -p returned_array
eval $(return_an_array | sed -e 's/^\(declare -a \)[^=]*=/\1 returned_array=/')
declare -p returned_array
于 2009-10-04T16:00:56.137 に答える
1

データ関数を使用echo -eし、データを改行で区切る場合:

getLines() { echo -e "\"Hello there\"\n\"loyal user\""; }

データを読み取るには、プロセス置換とリダイレクトを使用します。

i=0
while read -r
do
    arr[i++]=$REPLY
done < <(getLines)
# Line 0: '"Hello there"'
# Line 1: '"loyal user"'

ただし、これにより文字列の前後に引用符が残ります。

ここからのテクニックに基づいています。

于 2009-08-25T04:51:13.717 に答える
1

関数からの恣意的で悪意のあるデータが心配な場合は、\0(別名NUL) セパレーターを使用して文字列を単純に期待して解析する必要があります。これは変数の一部になれない唯一の文字であるため、実際のデータと衝突することはありません。

haxxorz() {
    printf '%s\0' whoami
    printf '%s\0' '\`whoami\`'
    printf '%s\0' "\`whoami\`"
    printf '%s\0' "\"\`whoami\` is haxxor-proof"'!'"\""
    printf '%s' 'Trying to poison the $REPLY variable with a missing separator'
}

index=0
while IFS= read -r -d '' || [ -n "$REPLY" ]
do
    array[index++]="$REPLY"
done < <(haxxorz)

for element in "${array[@]}"
do
    echo "$element"
done

echo "${REPLY:-REPLY is empty}"

結果:

whoami
\`whoami\`
`whoami`
"`whoami` is haxxor-proof!"
Trying to poison the $REPLY variable with a missing separator
REPLY is empty

企画がまとまった時が一番嬉しいです!

于 2011-11-28T15:04:48.477 に答える