Programování

Správa tajných klíčů – .env, gitignore, CI secrets a Vault

Jak správně ukládat API klíče a hesla v projektu. Soubor .env, .gitignore, GitHub Actions secrets, HashiCorp Vault a co dělat, když klíč uteče do gitu.

12. 10. 2026 · 4 min čtení

API klíče, hesla k databázi, tokeny k platebním bránám – každý projekt má tajemství, která nesmí skončit v repozitáři. Přesto je uniklý klíč v gitu jedna z nejčastějších bezpečnostních nehod. Tady je praktický postup od malého projektu po produkční infrastrukturu.

Pravidlo číslo jedna: tajemství nepatří do kódu

Tohle je špatně, i když je repozitář „soukromý“:

STRIPE_KEY = "sk_live_51H..."

Soukromý repozitář se může stát veřejným, může ho naklonovat kolega na nezabezpečený notebook nebo ho můžete nahrát jako přílohu někam, kam nemáte. A git si pamatuje všechno – smazání v dalším commitu klíč z historie neodstraní.

Úroveň 1: soubor .env

Tajemství patří do proměnných prostředí. Pro lokální vývoj je nejpohodlnější soubor .env v kořeni projektu:

# .env
DATABASE_URL=postgres://app:heslo@localhost:5432/app
STRIPE_SECRET_KEY=sk_test_...

Načtení v Pythonu (balíček python-dotenv):

from dotenv import load_dotenv
import os

load_dotenv()
db_url = os.environ["DATABASE_URL"]  # spadne hned, pokud chybí – to je dobře

V Node.js od verze 20.6 není potřeba balíček dotenv, stačí přepínač:

node --env-file=.env server.js

.gitignore a .env.example

Hned při založení projektu:

echo ".env" >> .gitignore
echo ".env.*" >> .gitignore
echo "!.env.example" >> .gitignore

Do repozitáře naopak patří .env.example se seznamem proměnných, ale bez hodnot:

# .env.example
DATABASE_URL=
STRIPE_SECRET_KEY=

Nový kolega si ho zkopíruje (cp .env.example .env) a hned ví, co vyplnit.

Ověření, že .env opravdu ignorujete:

git check-ignore -v .env

Pokud příkaz nic nevypíše, soubor ignorovaný není.

Úroveň 2: tajemství na serveru

Na produkčním serveru máte několik možností:

  • systemd: EnvironmentFile=/etc/myapp/env v unit souboru, soubor s právy chmod 600 a vlastníkem root.
  • Docker Compose: env_file: .env nebo lépe secrets:, které se připojí jako soubor do /run/secrets/<název>.
  • Docker: nikdy nedávejte tajemství do Dockerfile přes ENV nebo ARG – zůstanou v historii vrstev image (docker history). Pro build použijte RUN --mount=type=secret.
# syntax=docker/dockerfile:1
RUN --mount=type=secret,id=npmrc,target=/root/.npmrc npm ci
docker build --secret id=npmrc,src=$HOME/.npmrc .

Úroveň 3: CI/CD

V GitHub Actions uložte tajemství v Settings → Secrets and variables → Actions a v workflow je čtěte přes kontext secrets:

- name: Deploy
  env:
    SSH_KEY: ${{ secrets.DEPLOY_SSH_KEY }}
  run: ./deploy.sh

GitHub hodnoty v logu automaticky maskuje, ale jen pokud se objeví v přesném tvaru. Base64 nebo jinak upravená hodnota maskovaná nebude, takže tajemství nevypisujte.

Úroveň 4: správce tajemství (Vault a spol.)

Jakmile máte více služeb, serverů a lidí, .env soubory přestanou stačit. Pomůže centrální správce:

Nástroj Kdy se hodí
HashiCorp Vault / OpenBao Vlastní infrastruktura, dynamická hesla k DB, audit přístupu
AWS Secrets Manager, GCP Secret Manager, Azure Key Vault Aplikace běží v daném cloudu
SOPS + age Šifrované soubory s tajemstvími přímo v gitu
1Password CLI, Doppler, Infisical Týmy, které chtějí pohodlné sdílení

Příklad s Vaultem (KV engine v2):

vault kv put secret/myapp DATABASE_URL="postgres://..."
vault kv get -field=DATABASE_URL secret/myapp

Výhoda Vaultu je i v tom, že umí generovat dynamická, krátkodobá hesla k databázi. Když unikne heslo platné hodinu, škoda je mnohem menší.

Co dělat, když klíč unikne

  1. Okamžitě klíč zneplatněte (rotujte) u poskytovatele. To je jediný krok, který skutečně pomáhá.
  2. Projděte logy poskytovatele, zda nebyl klíč zneužit.
  3. Teprve potom odstraňte klíč z historie gitu, např. nástrojem git filter-repo:
git filter-repo --replace-text <(echo 'sk_live_51H...==>REMOVED')
git push --force --all

Přepis historie nestačí sám o sobě: klíč už mohl někdo stáhnout, je v forcích i v cache. Proto vždy nejdřív rotace.

Prevence: skenování tajemství

  • GitHub secret scanning a push protection – u veřejných repozitářů zablokuje push se známým formátem klíče.
  • gitleaks jako pre-commit hook:
# gitleaks v8.19+ (starší verze používaly "detect" a "protect")
gitleaks git -v                         # sken celé historie repozitáře
gitleaks git --pre-commit --staged -v   # jen změny připravené ke commitu
  • trufflehog pro hloubkové skenování repozitářů a historie.

Shrnutí

.env mimo git, .env.example v gitu, tajemství v CI přes secrets, na produkci soubory s omezenými právy nebo správce tajemství. A když klíč unikne: nejdřív rotovat, pak uklízet.