Какой самый безопасный способ инициализации массивов bash с использованием значений в кавычках из выходных данных функции? - PullRequest
3 голосов
/ 25 августа 2009

Я предпочитаю программировать мои bash-скрипты как можно более процедурными. Одна попытка, с которой я столкнулся при попытке сделать это, происходит при передаче данных массива между функциями, задача, которая не очень хорошо поддерживается в 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, чтобы преодолеть эту проблему. Один подход, который, кажется, работает хорошо - это 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» будет работать в под-оболочке, когда в нее передаются данные, эффективно отделяя ее стек переменных от одного из вызывающих сценариев.

Какие решения мне следует рассмотреть, чтобы смягчить проблемы безопасности, связанные с подходом eval? Я включил следующий скрипт, который демонстрирует множество подходов, которые я пробовал:

#!/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

Ответы [ 3 ]

2 голосов
/ 04 октября 2009

Следующие правильно обрабатывают пробелы в элементах массива :

#! /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
1 голос
/ 28 ноября 2011

Если вы беспокоитесь о произвольных вредоносных данных из функций, вам следует просто ожидать и анализировать строки с разделителем \0 (он же. NUL). Это символ only , который не может быть частью каких-либо переменных, поэтому не будет никаких конфликтов с реальными данными.

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

Мне нравится, когда план объединяется!

1 голос
/ 25 августа 2009

Для функции данных используйте 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"'

Это оставляет кавычки вокруг строк, хотя.

Основано на методах из здесь .

...