Проблема полягає в тих випадках, коли вміст $x
не був дезінфікований і містить дані, які потенційно можуть бути під контролем зловмисника у випадках, коли код оболонки може в кінцевому підсумку використовуватися в контексті ескалації привілеїв (наприклад, скрипт, на який посилається setuid додаток, скрипт sudoers або використовується для обробки даних, що виходять за межі мережі (CGI, DHCP гак ...) прямо чи опосередковано).
Якщо:
x='(PATH=2)'
Потім:
x=$((1-$x)))
має побічний ефект встановлення PATH
на 2
(відносний шлях, який цілком може бути під контролем нападника). Ви можете замінити PATH
на LD_LIBRARY_PATH
або IFS
... Те ж саме відбувається з x=$((1-x))
bash, zsh або ksh (не тире чи yash, які приймають лише числові константи у змінних).
Зауважте, що:
x=$((1-$x))
не працюватимуть належним чином для від'ємних значень $x
у деяких оболонках, які реалізують --
оператор ( необов'язково відповідно до POSIX) (декремент) (як у випадку x=-1
, це означає, що попросити оболонку оцінити 1--1
арифметичний вираз). "$((1-x))"
не має проблеми, оскільки x
вона розширюється як частина (не раніше) арифметичної оцінки.
В bash
, zsh
і ksh
(ні dash
або yash
), якщо x
є:
x='a[0$(uname>&2)]'
Тоді розширення $((1-$x))
або $((1-x))
спричиняє виконання цієї uname
команди (бо zsh
, a
повинно бути змінною масиву, але для цього можна використовувати, psvar
наприклад).
Таким чином, не слід використовувати неініцалізірованние або не продезінфікують зовнішні дані в арифметичних виразах в оболонках (зверніть увагу , що арифметична оцінка може бути зроблена шляхом $((...))
(він же $[...]
в bash
або zsh
) , але і в залежності від оболонки в let
, [
/ test
, declare/typeset/export...
, return
, break
, continue
, exit
, printf
, print
вбудовані, індекси масиву ((..))
та [[...]]
конструкції, щоб назвати декілька).
Щоб перевірити, чи змінна містить буквальне десяткове ціле число, ви можете використовувати POSIXly:
case $var in
("" | - | *[!0123456789-]* | ?*-*) echo >&2 not a valid number; exit 1;;
esac
Будьте уважні, що [0-9]
в деяких локальних збігах більше 0123456789. [[:digit:]]
повинно бути гаразд, але я б не став на це.
Також пам’ятайте, що числа з провідними нулями в деяких контекстах розглядаються як восьмеричні ( 010
іноді це 10, іноді 8), і будьте обережні, що перевірка вище дозволить пропустити цифри, які потенційно перевищують максимальне ціле число, що підтримується вашою системою (або будь-яке додаток, яке ви хочете використовувати це ціле число; bash, наприклад, розглядає 18446744073709551616 як 0, як це 2 64 ). Тому ви можете додати додаткові чеки у цій заяві вище, наприклад:
(0?* | -0?*)
echo >&2 'Only decimal numbers without leading 0 accepted'; exit 1;;
(-??????????* | [!-]?????????*)
echo >&2 'Only numbers from -999999999 to 999999999 supported'; exit 1;;
Приклади:
$ export 'x=psvar[0$(uname>&2)]'
$ ksh93 -c 'echo "$((x))"'
Linux
ksh93: psvar: parameter not set
$ ksh93 -c '[ x -lt 2 ]'
Linux
ksh93: [: psvar: parameter not set
$ bash -c 'echo "$((x))"'
Linux
0
$ bash -c '[[ $x -lt 2 ]]'
Linux
$ bash -c 'typeset -i a; export a="$x"'
Linux
$ bash -c 'typeset -a a=([x]=1)'
Linux
$ bash -c '[ -v "$x" ]'
Linux
$ mksh -c '[[ $x -lt 2 ]]'
Linux
$ zsh -c 'echo "$((x))"'
Linux
0
$ zsh -c 'printf %d $x'
Linux
0
$ zsh -c 'integer x'
Linux
$ zsh -c 'exit $x'
Linux
Більше читання на:
x='P=3'; : $(($x + 5))
буде встановленоP
у 8, алеx='P=3'; : $((x + 5))
встановитьсяP
у3
(вzsh
,ksh
абоbash
). "Те саме відбувається і з$((x + 1))
..." зараз невірно; він буде встановленийPATH
в2
, як у давнину.