← retour aux snippets

charger un fichier .env de façon sûre

Charger un .env sans eval ni source arbitraire: parsing clé=valeur, quotes optionnelles, CRLF, whitelist.


objectif

Charger des variables depuis un fichier .env sans exécuter de code, en acceptant KEY=VALUE (quotes optionnelles), en gérant CRLF et une liste de clés autorisées.

code minimal

# charge .env de manière sûre (KEY=VALUE), sans eval/source
# usage: safe_env_load .env "APP_PORT APP_NAME DB_URL"
safe_env_load() {
  local file="${1:-.env}"; shift || true
  local whitelist=" $* "
  [ -r "$file" ] || { echo "absent: $file" >&2; return 0; }

  local line key val
  while IFS= read -r line || [ -n "$line" ]; do
    # ignorer vides/commentaires
    [[ "$line" =~ ^[[:space:]]*$ ]] && continue
    [[ "$line" =~ ^[[:space:]]*\# ]] && continue

    # parse: KEY = VALUE (quotes optionnelles)
    if [[ "$line" =~ ^[[:space:]]*([A-Za-z_][A-Za-z0-9_]*)[[:space:]]*=[[:space:]]*(.*)[[:space:]]*$ ]]; then
      key="${BASH_REMATCH[1]}"
      val="${BASH_REMATCH[2]}"

      # enlever CR final (fichiers CRLF)
      val="${val%$'\r'}"

      # retirer quotes englobantes si presentes
      if [[ "$val" =~ ^\"(.*)\"$ ]]; then val="${BASH_REMATCH[1]}"; fi
      if [[ "$val" =~ ^\'(.*)\'$ ]]; then val="${BASH_REMATCH[1]}"; fi

      # refuser expansions/substitutions dangereuses
      if [[ "$val" =~ [\`\$]|\$\(|\)\>|\<\(|\|\| ]]; then
        echo "ligne suspecte ignoree pour $key" >&2
        continue
      fi

      # si whitelist fournie, ne charger que ces cles
      if [ -n "${whitelist// }" ] && [[ " $whitelist " != *" $key "* ]]; then
        continue
      fi

      # assigner sans eval, exporter
      printf -v "$key" '%s' "$val"
      export "$key"
    fi
  done < "$file"
}

utilisation

# exemple de .env
cat > .env <<'EOF'
# commentaire
APP_NAME="demo app"
APP_PORT=8080
DB_URL=postgres://user:pass@localhost:5432/app
EOF

# charger uniquement les cles necessaires
safe_env_load .env "APP_NAME APP_PORT DB_URL"

# verifier
printf 'APP_NAME=%q\nAPP_PORT=%q\nDB_URL=%q\n' "$APP_NAME" "$APP_PORT" "$DB_URL"

# utiliser ensuite avec envsubst, app, etc.
envsubst '${APP_NAME} ${APP_PORT}' < config.tmpl > config.conf

variante(s) utile(s)

# charger toutes les cles sans whitelist (attention a ce que contient .env)
safe_env_load .env

# charger puis lancer une commande avec env propre (sans variables parasites)
( env -i PATH="$PATH" HOME="$HOME" bash -lc 'safe_env_load .env "APP_NAME APP_PORT"; ./start.sh' )

# ecrire un .env nettoye (quotes evaporees, CRLF supprime, uniquement whitelist)
safe_env_load .env "APP_NAME APP_PORT DB_URL"
{
  printf 'APP_NAME=%s\n' "$APP_NAME"
  printf 'APP_PORT=%s\n' "$APP_PORT"
  printf 'DB_URL=%s\n' "$DB_URL"
} > .env.cleaned

# fallback simple (moins strict) si Bash n'est pas disponible: set -a/. (risques)
# set -a; . ./.env; set +a

notes

  • ne faites pas source .env: cela execute du code arbitraire. Le parsing ci-dessus n’evalue rien.
  • seuls les formats KEY=VALUE (quotes optionnelles) sont pris en charge; pas d’export, pas de substitutions shell.
  • utilisez la whitelist pour limiter ce qui entre dans votre environnement (principe du moindre privilege).
  • pour des besoins complexes (multilignes, escapes), utilisez un parseur dedie (ex: Python dotenv), ou formalisez en JSON/YAML.