A história geral: namespaces separados
Geralmente shells distinguem entre variáveis e funções porque são usadas em diferentes contextos. Em suma, um nome é um nome de variável se aparecer depois de um $
, ou como um argumento para builtins como export
(sem -f
) e unset
(sem -f
). Um nome é um nome de função se aparecer como um comando (após a expansão de alias) ou como um argumento para export -f
, unset -f
, etc.
Variáveis podem ser exportadas para o ambiente. O nome da variável de ambiente é o mesmo da variável shell (e os valores são os mesmos também).
Com o bash antigo: confusão devido à exportação de função
O Bash, ao contrário da maioria dos outros shells, também pode exportar funções para o ambiente. Como não há indicação de tipo no ambiente, não há como reconhecer se uma entrada no ambiente é uma função ou não, além de analisar o nome ou o valor da variável de ambiente.
Versões mais antigas do bash armazenam uma função no ambiente usando o nome da função como nome e algo que se parece com a definição da função como o valor da função. Por exemplo:
bash-4.1$ foobar () { echo foobar; }
bash-4.1$ export -f foobar
bash-4.1$ env |grep -A1 foobar
foobar=() { echo foobar
}
bash-4.1$
Observe que não há como distinguir uma função cujo código seja { echo foobar; }
de uma variável cujo valor seja () { echo foobar}
(onde 
é um caractere de nova linha). Isso acabou sendo uma má decisão de design.
Às vezes, os shell scripts são chamados com variáveis de ambiente cujo valor está sob controle de uma entidade potencialmente hostil. Scripts CGI, por exemplo. O recurso de exportação / importação de funções do Bash permitia injetar funções dessa maneira. Por exemplo, executando o script
#!/bin/bash
ls
de uma solicitação remota é seguro, desde que o ambiente não contenha variáveis com um determinado nome (como PATH
). Mas se a solicitação puder definir a variável de ambiente ls
to () { cat /etc/passwd; }
, então o bash executará com alegria cat /etc/passwd
, já que esse é o corpo da função ls
.
Com o mais recente bash: confusão principalmente aliviada
Esta vulnerabilidade de segurança foi descoberta por Stéphane Chazelas como um dos aspectos do Bug do shellshock . Nas versões pós-Shellshock do bash, as funções exportadas são identificadas pelo nome e não pelo seu conteúdo.
bash-4.3$ foobar () { echo foobar; }
bash-4.3$ export -f foobar
bash-4.3$ env |grep -A1 foobar
BASH_FUNC_foobar%%=() { echo foobar
}
Não há nenhum problema de segurança agora porque nomes como BASH_FUNC_foobar%%
não são comumente usados como nomes de comando e podem ser filtrados por interfaces que permitem a passagem de variáveis de ambiente. É tecnicamente possível ter um caractere %
no nome de uma variável de ambiente (isso é o que faz as funções exportadas do bash moderno funcionar), mas normalmente as pessoas não fazem isso porque os shells não aceitam %
no nome de um variável.
A frase no manual bash refere-se ao comportamento antigo (pré-Shellshock). Deve ser atualizado ou removido. Com as versões modernas do bash, não há ambiguidade no ambiente se você assumir que as variáveis de ambiente não terão um nome que termine em %%
.