0Pricing
DevOps Bootcamp · Aula

Prevenção de injeção de comandos e argumentos

Use aspas, valide e passe entradas não confiáveis por matrizes para eliminar divisão de palavras e injeções baseadas em eval.

Prevenção de injeção de comandos e argumentos é uma aula grátis de DevOps Bootcamp no CoddyKit. Esta é a aula 1 de 4. Você pode ler a aula completa abaixo gratuitamente — depois pratica ao vivo no navegador com um editor de código integrado e um tutor de IA 24/7. Faz parte do caminho de aprendizado de DevOps Bootcamp, e seu progresso é sincronizado entre a web e o app CoddyKit. O curso de DevOps Bootcamp inclui 4 aulas no total.

Por que ocorrem ataques de injeção no Bash

O Bash é uma poderosa linguagem de integração — ele passa texto diretamente ao núcleo do sistema, a outros programas e a subshells. Esse poder se torna uma vulnerabilidade no momento em que uma entrada não confiável chega a um comando sem validação ou sem aspas.

Duas causas fundamentais estão por trás de praticamente todas as injeções no Bash:

  • Divisão de palavras: variáveis sem aspas são divididas nos espaços em branco (IFS), transformando um único valor lógico em vários tokens do shell.
  • Expansão de curingas: caracteres como *, ? e [ são expandidos pelo shell antes mesmo de o comando ser executado.

Um invasor que controle um nome de arquivo, nome de usuário, parâmetro de URL ou variável de ambiente pode explorar ambos os mecanismos para executar comandos arbitrários, ler arquivos ou elevar privilégios.

Esta lição mostra exatamente como essas vulnerabilidades surgem e — mais importante — como eliminá-las com o uso correto de aspas, validação de entrada e passagem de argumentos baseada em matrizes.

Divisão de palavras: a ameaça silenciosa

Quando o Bash encontra uma variável sem aspas, ele divide o valor em qualquer caractere listado em $IFS (padrão: espaço, tabulação, quebra de linha). O que parece ser um único argumento torna-se vários.

Execute o script abaixo e observe como um nome de arquivo com um espaço se transforma em dois argumentos separados para rm.

#!/usr/bin/env bash
# Dangerous: unquoted variable
FILE='important file.txt'

# Create the file so the demo is self-contained
touch "$FILE"

echo "Files before:"
ls

# BUG: rm sees TWO arguments: 'important' and 'file.txt'
# If 'important' does not exist, rm prints an error but continues.
rm $FILE   # <-- unquoted, word-split happens here

echo "Files after (unquoted rm):"
ls

Use sempre aspas: a primeira regra do Bash defensivo

A defesa mais simples e eficaz contra a divisão de palavras é sempre colocar entre aspas duplas as expansões de variáveis.

  • "$var" — expande para exatamente um token, preservando espaços, tabulações e quebras de linha.
  • 'literal' — aspas simples: nenhuma expansão é realizada, sendo úteis para strings fixas.
  • Nunca use $var sem aspas, a menos que você precise explicitamente da divisão de palavras e da expansão de curingas.

O script abaixo mostra a versão segura do exemplo anterior.

#!/usr/bin/env bash
set -euo pipefail

FILE='important file.txt'
touch "$FILE"

echo 'Files before:'
ls

# SAFE: double-quotes keep the filename as one token
rm "$FILE"

echo 'Files after (quoted rm):'
ls

Injeção de curingas: quando * se torna uma arma

Variáveis sem aspas também estão sujeitas à expansão de caminhos (curingas). Se a entrada controlada pelo usuário contiver * ou ?, o Bash a expande de acordo com o sistema de arquivos antes de o comando ser executado.

Um vetor de ataque clássico: um formulário web define PATTERN=* e o script executa cp $PATTERN /tmp/leak/ — copiando todos os arquivos do diretório atual.

A correção é idêntica: coloque a variável entre aspas duplas. Uma "$PATTERN" entre aspas é passada literalmente; o shell nunca realiza expansão de curingas sobre ela.

#!/usr/bin/env bash
set -euo pipefail

# Simulate attacker-supplied input
PATTERN='*'

mkdir -p /tmp/safe_demo_src /tmp/safe_demo_dst
touch /tmp/safe_demo_src/secret1.txt /tmp/safe_demo_src/secret2.txt

cd /tmp/safe_demo_src

# UNSAFE: glob expands, copies every file
# cp $PATTERN /tmp/safe_demo_dst/

# SAFE: pattern is treated as a literal filename
cp "$PATTERN" /tmp/safe_demo_dst/ 2>&1 || echo 'No file named literally "*" — attack neutralised'

rm -rf /tmp/safe_demo_src /tmp/safe_demo_dst

Injeção de argumentos por meio de parâmetros posicionais sem aspas

Scripts que aceitam argumentos do chamador são alvos prioritários de injeção. Cada parâmetro posicional ($1, $2, ...) deve estar entre aspas em todos os locais onde for usado.

Um padrão particularmente perigoso é passar $@ ou $* sem aspas para outro comando:

  • "$@" — expande cada parâmetro posicional como uma palavra separada e individualmente entre aspas. Use sempre esta forma.
  • $@ ou $* sem aspas — ficam sujeitos à divisão de palavras e à expansão de curingas.
  • "$*" — reúne todos os parâmetros em uma única palavra, o que raramente é o desejado.
#!/usr/bin/env bash
set -euo pipefail

# Safe wrapper: forward all arguments quoted
grep_wrapper() {
    local pattern="$1"
    shift
    # "$@" preserves each file argument as one token
    grep -rn "$pattern" "$@"
}

# Usage: ./script 'error msg' /var/log/syslog '/path with spaces/app.log'
echo 'Searching current script for "safe":'
grep_wrapper 'safe' "$0"

Injeção de comandos por meio de eval e entradas não validadas

eval analisa novamente seu argumento como código do shell. Qualquer dado não confiável que chegue a eval pode executar comandos arbitrários.

Padrões perigosos comuns:

  • eval "$user_input"
  • eval echo \$$var (consulta indireta de variável)
  • Passar dados do usuário por bash -c "$input"

Regra: nunca passe entradas não confiáveis para eval ou bash -c. Use alternativas seguras do Bash:

  • Expansão indireta: ${!varname} em vez de eval echo \$$varname
  • Matrizes associativas para consultas dinâmicas de chave-valor
  • Funções em vez de strings de comandos geradas
#!/usr/bin/env bash
set -euo pipefail

# Simulated attacker-supplied variable name
VARNAME='PATH; echo INJECTED'

# UNSAFE: eval lets attacker run 'echo INJECTED'
# eval "echo \$$VARNAME"

# SAFE: indirect expansion only resolves valid variable names
# First validate that VARNAME is a legal identifier
if [[ "$VARNAME" =~ ^[A-Za-z_][A-Za-z0-9_]*$ ]]; then
    echo "Value: ${!VARNAME}"
else
    echo "ERROR: invalid variable name: '$VARNAME'" >&2
    exit 1
fi

Validação de entradas: listas de permissões em vez de listas de bloqueio

Rejeitar caracteres conhecidos como perigosos (uma lista de bloqueio) é frágil — os invasores encontram codificações ou caracteres que você esqueceu. Em vez disso, use uma lista de permissões: aceite apenas os caracteres que você sabe serem seguros.

Estratégias de lista de permissões no Bash:

  • Correspondência com expressão regular: [[ "$input" =~ ^[A-Za-z0-9_-]+$ ]]
  • Correspondência com padrão: case "$input" in [A-Za-z0-9]*) ... ;; esac
  • Verificação de enumeração: compare com um conjunto fixo de valores válidos

Valide na fronteira — assim que a entrada entrar no script — antes que ela toque qualquer comando.

#!/usr/bin/env bash
set -euo pipefail

validate_username() {
    local name="$1"
    # Allowlist: only lowercase letters, digits, underscore, hyphen; 1-32 chars
    if [[ ! "$name" =~ ^[a-z0-9_-]{1,32}$ ]]; then
        echo "ERROR: invalid username '${name}'" >&2
        return 1
    fi
    echo "Username accepted: $name"
}

validate_username 'alice'          # OK
validate_username 'bob_smith-2'    # OK
validate_username 'root; rm -rf /' # REJECTED
validate_username '../etc/passwd'   # REJECTED

Usando matrizes para passar argumentos com segurança

Quando precisar criar um comando dinamicamente — adicionando sinalizadores condicionalmente ou percorrendo entradas em um loop — use uma matriz do Bash em vez de concatenar strings.

A concatenação de strings elimina toda a estrutura; uma matriz do Bash preserva cada argumento como um elemento distinto, sem uma nova análise pelo shell.

  • Declare: args=()
  • Adicione: args+=(--flag "$value")
  • Execute: command "${args[@]}"

"${args[@]}" expande cada elemento como uma palavra separada e individualmente entre aspas — exatamente como "$@".

#!/usr/bin/env bash
set -euo pipefail

# Build a find command safely with an array
BASEDIR='/tmp'
USER_PATTERN='*.log'   # could come from user input (validate first!)
MAX_DAYS=7

cmd=(find "$BASEDIR" -type f -name "$USER_PATTERN" -mtime "+${MAX_DAYS}")

# Optionally add -delete only when requested
DELETE=false
if [[ "$DELETE" == 'true' ]]; then
    cmd+=(-delete)
fi

echo "Running: ${cmd[*]}"
"${cmd[@]}"

O separador --: proteção contra injeção de sinalizadores

Até mesmo um argumento colocado corretamente entre aspas pode ser interpretado incorretamente como um sinalizador de opção se começar com -. Considere rm "$file" quando file='-rf .': as aspas protegem contra a divisão de palavras, mas o rm ainda interpreta -rf como sinalizadores.

A convenção POSIX -- indica o fim das opções para a maioria dos utilitários GNU/BSD. Tudo após -- é tratado como argumento posicional, nunca como sinalizador.

#!/usr/bin/env bash
set -euo pipefail

# Simulate a dangerous filename supplied by the user
FILENAME='-rf /tmp/safe_demo_target'

mkdir -p /tmp/safe_demo_target
touch /tmp/safe_demo_target/keep_me.txt

echo 'Files before:'
ls /tmp/safe_demo_target/

# UNSAFE (flag injection — do NOT uncomment in real systems):
# rm "$FILENAME"

# SAFE: -- ends option processing; filename is treated literally
rm -- "$FILENAME" 2>&1 || echo "No such file (attack neutralised): $FILENAME"

echo 'Files after:'
ls /tmp/safe_demo_target/
rm -rf /tmp/safe_demo_target

Higienização de entradas para SQL e ferramentas externas

Quando scripts Bash invocam interfaces de linha de comando de bancos de dados (psql, mysql), usam curl com URLs fornecidas pelo usuário ou recorrem a ferramentas semelhantes, aplicam-se duas camadas adicionais:

  • Consultas parametrizadas: nunca incorpore dados do usuário em strings SQL. Passe os valores por meio de -v no psql ou de --data-urlencode no curl.
  • Separe dados de código: use printf com uma string de formato literal; nunca permita que a entrada do usuário seja a string de formato.

O exemplo abaixo consulta o PostgreSQL com segurança, mantendo o valor fornecido pelo usuário completamente fora do texto SQL.

#!/usr/bin/env bash
set -euo pipefail

# Validate first: only allow alphanumeric usernames
USERNAME="${1:-alice}"
if [[ ! "$USERNAME" =~ ^[a-z0-9_]{1,32}$ ]]; then
    echo 'ERROR: invalid username' >&2
    exit 1
fi

# UNSAFE:
# psql -c "SELECT * FROM users WHERE name = '$USERNAME';"

# SAFE: pass value as a psql variable, never inside the SQL text
# psql -v username="$USERNAME" -c 'SELECT * FROM users WHERE name = :username;'

# Demonstrate the principle with printf (safe format string usage)
printf 'Query would use username: %s\n' "$USERNAME"

Lista de verificação de proteção: juntando tudo

Um script Bash seguro, pronto para produção, combina todas as técnicas desta lição em uma defesa consistente e em camadas. Veja um modelo mínimo, mas completo, de script protegido:

  • set -euo pipefail — encerra em caso de erro, trata variáveis não definidas como erros e propaga falhas em pipelines.
  • Valide na entrada — use uma lista de permissões para cada entrada externa antes que ela toque qualquer comando.
  • Coloque tudo entre aspas — "$var", "$@", "${array[@]}" — sem exceções, a menos que você precise da divisão.
  • Use matrizes para construir comandos dinâmicos.
  • Prefixe os argumentos com -- ao passar nomes de arquivos ou strings fornecidos pelo usuário.
  • Nunca use eval com dados não confiáveis; prefira ${!var} para consultas indiretas.
  • Restrinja as permissões — execute scripts com os privilégios mínimos necessários; evite sudo em scripts que aceitem entradas do usuário.
#!/usr/bin/env bash
# Hardened template — safe argument injection prevention
set -euo pipefail
IFS=$'\n\t'

#--- 1. Validate inputs at the boundary ---
SEARCH_DIR="${1:-}"
PATTERN="${2:-}"

[[ -z "$SEARCH_DIR" || -z "$PATTERN" ]] && { echo 'Usage: script <dir> <pattern>' >&2; exit 1; }
[[ ! "$SEARCH_DIR" =~ ^[A-Za-z0-9/_.-]+$ ]] && { echo 'ERROR: unsafe directory path' >&2; exit 1; }
[[ ! "$PATTERN" =~ ^[A-Za-z0-9._-]+$ ]]    && { echo 'ERROR: unsafe pattern'        >&2; exit 1; }

#--- 2. Build command with an array ---
cmd=(find -- "$SEARCH_DIR" -type f -name "$PATTERN")

#--- 3. Execute — no string interpolation, no eval ---
echo "Executing: ${cmd[*]}"
"${cmd[@]}"

Verificação de conhecimentos: aspas e prevenção de injeções

Teste sua compreensão dos principais conceitos desta lição.

Recapitulação da lição: prevenção de injeções de comandos e argumentos

Você estudou o conjunto completo de ferramentas defensivas para lidar com entradas do Bash com segurança:

  • Divisão de palavras e expansão de curingas são os mecanismos fundamentais que transformam variáveis inseguras em vetores de injeção.
  • Coloque todas as variáveis entre aspas duplas ("$var", "$@", "${arr[@]}") para neutralizar ambas as ameaças.
  • Use "$@" — nunca $@ ou $* sem aspas — ao encaminhar argumentos.
  • Prefixe os nomes de arquivos fornecidos pelo usuário com -- para evitar injeção de sinalizadores.
  • Use uma lista de permissões para todas as entradas externas, com uma verificação por expressão regular ([[ $v =~ ^pattern$ ]]), antes que elas cheguem a qualquer comando.
  • Crie comandos dinâmicos com matrizes (cmd+=() → "${cmd[@]}"), nunca por concatenação de strings.
  • Elimine eval e bash -c "$input"; use ${!varname} para uma expansão indireta segura.
  • Sempre inicie os scripts com set -euo pipefail e IFS=$'\n\t' para estabelecer uma proteção básica.

Aplicadas de forma consistente desde a primeira linha de cada script, essas práticas reduzem quase a zero a superfície de ataque do Bash para vulnerabilidades da classe de injeção.

Perguntas Frequentes

A aula “Prevenção de injeção de comandos e argumentos” é grátis?

Sim — o texto completo de “Prevenção de injeção de comandos e argumentos” é grátis para ler aqui na web. Para praticá-la interativamente (um editor de código integrado e um tutor de IA 24/7) e desbloquear o restante do curso de DevOps Bootcamp, atualize para CoddyKit PRO. O curso de DevOps Bootcamp inclui 4 aulas no total.

O que vou aprender em “Prevenção de injeção de comandos e argumentos”?

Use aspas, valide e passe entradas não confiáveis por matrizes para eliminar divisão de palavras e injeções baseadas em eval. Você pratica DevOps Bootcamp com código prático que executa diretamente no navegador, e um tutor de IA 24/7 responde suas dúvidas enquanto trabalha na aula.

Preciso ter experiência prévia para começar DevOps Bootcamp?

Nenhuma experiência prévia é necessária. DevOps Bootcamp no CoddyKit é estruturado para alunos iniciantes até avançados, então você pode começar aqui ou desde o início e aprender no seu ritmo. Esta é a aula 1 de 4.

Quanto tempo leva a aula “Prevenção de injeção de comandos e argumentos”?

A maioria das aulas CoddyKit leva cerca de 5–10 minutos. Cada uma é compacta e interativa, então você faz progresso constante e retoma exatamente de onde parou entre web e app.

Posso escrever e executar código nesta aula de DevOps Bootcamp?

Sim. Cada aula de DevOps Bootcamp inclui um editor de código integrado, então você escreve e executa código real direto no navegador e recebe feedback de IA instantaneamente — nenhuma configuração local necessária.

Todas as aulas deste curso

  1. Prevenção de injeção de comandos e argumentos
  2. Tratamento seguro de segredos e higiene do ambiente
  3. Execução com privilégios mínimos e disciplina no sudo
  4. Análise estática e auditoria com ShellCheck
← Voltar para DevOps Bootcamp