O problema está em casos em que o conteúdo de $x
não foi higienizado e contém dados que poderiam estar sob o controle de um invasor, em casos em que o código shell pode acabar sendo usado em um contexto de escalonamento de privilégios (por exemplo um script invocado por um aplicativo setuid, um script sudoers ou usado para processar dados fora da rede (CGI, DHCP hook ...) direta ou indiretamente).
Se:
x='PATH=2'
Então:
x=$((1-$x))
tem o efeito colateral de configurar PATH
para 2
(um caminho relativo que poderia muito bem estar sob controle do invasor). Você pode substituir PATH
por LD_LIBRARY_PATH
ou IFS
... O mesmo acontece com x=$((1-x))
em bash, zsh ou ksh (não traços nem yash que aceitam apenas constantes numéricas em variáveis).
Em bash
, zsh
e ksh
(não dash
ou yash
), se x
for:
x='a[0$(uname>&2)]'
Em seguida, a expansão de $((1-$x))
ou $((1-x))
faz com que o comando uname
seja executado (para zsh
, a
precisa ser uma variável de matriz, mas é possível usar psvar
, por exemplo ).
Observe que:
x=$((1-$x))
não funcionará corretamente para valores negativos de $x
em alguns shells que implementam o operador (opcional como per capix) --
(decrement) (como em x=-1
, isso significa pedir ao shell para avaliar o 1--1
expressão aritmética). "$((1-x))"
não tem o problema, pois x
é expandido como parte da avaliação aritmética (não antes).
Em resumo, não se deve usar dados externos não inicializados ou não-higienizados em expressões aritméticas em shells (note que a avaliação aritmética pode ser feita por $((...))
(também conhecido como $[...]
em bash
ou zsh
) mas também dependendo do shell nos índices let
, [
/ test
, declare/typeset/export...
, return
, break
, continue
, exit
, printf
, print
builtins, matriz , ((..))
e [[...]]
constrói para citar alguns).
Para verificar se uma variável contém um número inteiro decimal literal, você pode usar POSIXly:
case $var in
("" | - | *[!0123456789-]* | ?*-*) echo >&2 not a valid number; exit 1;;
esac
Tenha em atenção que [0-9]
em alguns locais corresponde a mais de 0123456789. [[:digit:]]
deve estar OK, mas não apostaria nisso.
Lembre-se também que os números com zeros à esquerda são tratados como octal em alguns contextos ( 010
é às vezes 10, às vezes 8) e cuidado para que a verificação acima permita números maiores que o número inteiro máximo suportado pelo sistema (ou qualquer que seja a aplicação que você usará aquele inteiro; o bash, por exemplo, trata 18446744073709551616 como 0, isto é, 2 64 ).
Exemplos:
$ 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
$ 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
Mais leitura em:
-
link (onde Oliver Kiddle nos chamou a atenção da questão
x[0$(...)]
). - link
-
link para outro desdobramento potencialmente levando a injeção de código em
bash
. - Implicações de segurança de esquecer de citar uma variável em shells bash / POSIX onde isso e deixar uma variável sem aspas podem agravar-se mutuamente.