Preciso encapsular variáveis awk entre aspas para higienizá-las?

7

De acordo com uma resposta no stackoverflow , é meu entendimento que encapsular variáveis bash entre aspas duplas é uma forma bastante segura de higienizar a entrada do usuário.

E as variáveis do awk? Por exemplo, se eu tiver algo como:

awk -v SOURCEIP="$SOURCEIP" -v REVERSEDNS="$REVERSEDNS" '{
   gsub(/^_TMPSOURCEIP_/, SOURCEIP);
   gsub(/^_TMPREVERSEDNS_/, REVERSEDNS);
   print
}' /home/foo/footemplate

Devo colocar aspas ao redor da variável nas linhas do gsub? Então ficaria assim:

awk -v SOURCEIP="$SOURCEIP" -v REVERSEDNS="$REVERSEDNS" '{
   gsub(/^_TMPSOURCEIP_/, "SOURCEIP");
   gsub(/^_TMPREVERSEDNS_/, "REVERSEDNS");
   print
}' /home/foo/footemplate

Ou isso não faz diferença?

    
por Mike B 06.02.2014 / 07:49

3 respostas

4

Estes dois exemplos demonstram a diferença:

$ echo _TMP_ | awk -v VAR='some "text"' '{ gsub(/_TMP_/, VAR) ; print }'
some "text"
$ echo _TMP_ | awk -v VAR='some "text"' '{ gsub(/_TMP_/, "VAR") ; print }'
VAR

Quando VAR está sem aspas, awk trata-o como uma variável com o valor some "text" . Quando VAR está dentro de aspas, awk o trata como uma string de três caracteres.

MAIS: bash tem problemas de limpeza. Considere:

$ VAR="rm important_file" ; $VAR

O texto acima irá apagar important_file . Desta forma, bash é como uma linguagem macro: substituirá uma variável e tentará executar o resultado. awk é diferente. Considere:

$ echo _TMP_ | awk -v VAR='var); print $1' '{ gsub(/_TMP_/, VAR) ; print }'
var); print $1

awk trata VAR como mero texto, não como possíveis comandos para executar.

Podem surgir problemas, no entanto, se permitir que bash modifique o script awk . Nos meus exemplos acima, os scripts awk estavam todos entre aspas simples. Isso evita bash de mexer neles.

    
por 06.02.2014 / 08:21
4

(OK, desculpe, eu li sua pergunta muito rapidamente, então algumas das minhas respostas estão um pouco além do ponto, ainda deixando como é útil para você ou alguns)

Há várias coisas a considerar aqui.

citação das variáveis shell

Deixar uma variável sem aspas em shells POSIX (em contextos de lista, como em argumentos para um comando), não awk , é o operador split + glob.

Se você fizer isso:

cmd foo=$var

Em que $var é * * .

Não solicite ao shell que divida o conteúdo de $var com base no valor da variável de shell $IFS especial, por padrão em espaços em branco. Portanto, acima disso, isso nos dá foo=* e * e executa globbing em cada um desses, que expande foo=* para todos os nomes de arquivos no diretório atual que começam com foo= e * para todos os nomes não nomes de arquivos ocultos.

Então, na verdade, você deve quase sempre citar suas variáveis shell , sejam elas argumentos para awk ou não. Isso também se aplica à substituição do comando do shell ( '...' e $(...) ) e à expansão aritmética do shell ( $((...)) ).

passando dados como estão para awk

O outro problema é que awk (não o shell) expande sequências de escape de barras invertidas nas atribuições de variáveis como -v var=value .

Por exemplo, -v var='\n/\n/' define o conteúdo da variável awk var como <newline>/<newline>/ , não \n/\n/ . Isso também se aplica às variáveis awk definidas como:

awk '...' var=value

Para passar dados para awk sem passar por essa expansão, você pode usar as matrizes ENVIRON ou ARGV awk:

var=$value awk 'BEGIN {var=ENVIRON["var"]} ...'

(acima, é uma atribuição de variável shell (para uma variável não-array), então não pode ser split + glob, que é um dos raros casos em que você pode omitir as aspas ao redor das variáveis)

ou:

awk 'BEGIN {var=ARGV[1]; delete ARGV[1]} ...' "$value"

cotação e awk variáveis

Esse split + glob é apenas um recurso de shell (mis-). A linguagem awk é uma linguagem completamente diferente.

Em awk , as variáveis são referenciadas a varname , não $varname , e aspas são usadas para introduzir strings. Portanto, "varname" é a string varname , enquanto varname refere-se à variável.

sanitizando variáveis para evitar injeção de código

Estritamente falando, as variáveis shell não estão sanitizando, não estão citando as variáveis que estão usando o operador split + glob. Enquanto na maioria das linguagens você coloca aspas em torno de strings fixas, em shells, é o contrário: tudo é string e aspas são usadas para evitar algum comportamento especial, e especialmente as variáveis quase sempre devem ser citadas (uma decisão ruim de design). fez sentido na casca Bourne nos anos 70, mas é um obstáculo em conchas modernas, sendo zsh a única casca que parcialmente fixa isso).

O shell ou o awk não avaliarão / interpretarão o código armazenado em sua própria variável, a menos que você diga a eles.

var='foo; rm -f var'
echo $var
# or
echo "$var"

Não fará com que o conteúdo da variável seja avaliado como código de shell (embora o primeiro seja submetido a divisão e globbing, o que pode ter consequências desastrosas (por exemplo, com var='/*/*/*/*/../../../../*/*/*/*/../../../../*/*/*/*' ). Você precisaria de:

eval "echo $var"
# or
sh -c "echo $var"

para ser avaliado / interpretado como código shell.

awk não possui esse recurso eval . perl / python do.

Mas cuidado com a contaminação cruzada. Você pode ter os dados da variável shell pass (em variáveis shell ) como código para executar por awk :

awk '{print "'"$var"': " $0}'

seria perigoso caso a variável $var shell contenha, por exemplo:

var='test"; print "foo" > /etc/passwd; print "blah'

porque o shell seria executado:

["awk", "{print \"test\"; print \"foo\" > /etc/passwd; print \"blah: \" $0}"]

Ou o contrário:

awk '{system("echo foo: " $0)}' < file

em que awk executaria um shell como:

["sh", "-c", "echo foo: content-of-the-line"]

para cada linha de file (e pense no que uma linha como ; rm -rf / faria).

Não é apenas entre awk e sh . Você precisa ser cuidadoso sempre que dados variáveis / não controlados possam ser avaliados como código por outro interpretador. Exemplos são:

sed "s/$regexp/blah/g"
A linguagem de

sed é limitada, mas ainda pode prejudicar, como acontece com regexp='//;w /etc/passwd; s/ '.

Ou:

find . -exec sh -c "echo {}" \;

Agora, para evitar esses problemas, existem duas abordagens gerais:

  1. converta a variável de um intérprete para o outro. Isso funciona para o shell - > awk ou encontrar - > sh caso acima. Como mudança:

    awk '{print "'"$var"': " $0}'
    

    para:

    awk -v awk_var="$var" '{print awk_var ": " $0}'
    

    E:

    find . -exec sh -c "echo {}" \;
    

    para:

    find . -exec sh -c 'echo "$1"' sh {} \;
    

    mas isso não funcionará para o shell - > sed, ou awk - > casos de shell.

  2. quando 1 não é possível, você precisa limpar as variáveis para remover ou escapar dos caracteres que podem ser um problema. Em,

    awk '{system("echo foo: " $0)}'
    

    você precisa converter $0 em algo que seja uma string limpa no que diz respeito ao shell. Uma opção é prefixar cada caractere com uma barra invertida, mas isso não funcionará para a nova linha (não é um problema aqui). Outro é colocar a string entre aspas simples e escapar de cada aspas simples.

    awk 'function escape(s) {
           gsub(/'\''/,"&\\&&",s)
           return "'\''" s "'\''"
         }
         {system("echo foo: " escape($0))}'
    
por 06.02.2014 / 08:20
0

Se você está passando uma variável Awk para system , você precisa fazer um shell para citá-la:

function quote(str,   d, m, x, y, z) {
  d = ""; m = split(str, x, d)
  for (y in x) z = z d x[y] d (y < m ? "\" d : "")
  return z
}

Exemplo:

system(sprintf("ffmpeg -i %s outfile.m4a", quote(ARGV[1])))

Origem

    
por 15.01.2017 / 07:45