SSD Nodes Learn Hosting plans →
Gidsen Matt ConnorDoor Matt Connor

Wachtwoorden in settings.local.json van Claude Code

Kies je 'Yes, and don't ask again', dan kan een wachtwoord in .claude/settings.local.json belanden. Zo vind je het in je git-geschiedenis, haal je het uit git en vervang je het.

Kort antwoord

Kies je in Claude Code "Yes, and don't ask again" bij een commando met een wachtwoord erin, dan kan dat wachtwoord als allow-regel in .claude/settings.local.json terechtkomen. Claude Code probeert dat bestand buiten git te houden. Dat vangnet werkt alleen op je eigen machine, en alleen voor een bestand dat git nog niet volgt. Staat het bestand al in een commit op GitHub of GitLab, beschouw het wachtwoord dan als gelekt. Vervang het eerst. Ruim daarna je repository op.

Wat schrijft "Yes, and don't ask again" in settings.local.json?

Claude Code vraagt toestemming voordat het een shellcommando uitvoert. Alleen een vaste set alleen-lezen-commando's draait zonder vraag. Kies je "Yes, and don't ask again", dan bewaart Claude Code een allow-regel in .claude/settings.local.json in de hoofdmap van je git-repository. Volgens de documentatie is zo'n goedkeuring voor Bash-commando's permanent, per repository en per commando. Een goedkeuring om bestanden te bewerken geldt alleen tot het einde van de sessie. Die komt niet in het bestand.

Een bestand met één goedgekeurd commando ziet er zo uit:

{
  "permissions": {
    "allow": [
      "Bash(npm run test *)"
    ]
  }
}

Dit is een patroon. De * aan het eind staat voor alle tekst die volgt. Het voorbeeld in de documentatie is npm test: de vraag biedt dan "Yes, and don't ask again for: npm test *" aan. Bij een samengesteld commando zoals git status && npm test bewaart Claude Code een aparte regel voor elk deelcommando dat toestemming nodig had. Dat zijn maximaal vijf regels per commando.

Hoe komt een wachtwoord in een allow-regel terecht?

Een Bash-regel vergelijkt de hele tekst van het commando. Volgens de documentatie kijkt een allow-regel niet voorbij een omgevingsvariabele die aan het begin van het commando een waarde krijgt. De enige uitzondering is een korte lijst variabelen die als veilig bekendstaan, zoals NODE_ENV. De regel Bash(psql *) dekt dus niet het commando PGPASSWORD=geheim psql -h db.example.com -U app app_prod, omdat dat commando niet met psql begint.

De documentatie zegt ook dat een regel die je vanuit een vraag bewaart, precies dekt wat die optie noemde. Wat er blijft, lees je dus in de vraag zelf, achter "don't ask again for:". Staat daar een PGPASSWORD= met waarde, een sshpass -p met wachtwoord, een mysql -p met het wachtwoord er direct achter of een curl met een Authorization-header, dan staat dat geheim straks in settings.local.json.

Dat dit echt gebeurt, liet het beveiligingsbedrijf Apiiro zien in een onderzoek van 5 november 2026. Op dat moment stonden er op openbaar GitHub 104.185 kopieën van .claude/settings.local.json, verdeeld over 103.617 repositories. In 1.122 van die bestanden (1,1 procent) stond een echt geheim. Telt Apiiro ook de git-geschiedenis en andere bestanden mee, dan gaat het om 2.539 echte geheimen in 1.994 openbare repositories en 63 organisaties. In de allowlists zaten 379 databasewachtwoorden.

Waar staat het bestand, en wie houdt het uit git?

Sinds versie 2.1.211 schrijft Claude Code het bestand naar de hoofdmap van de git-repository. Dat gebeurt ook als je Claude Code in een submap start. In een worktree gebruikt het de hoofdmap van de gewone checkout. Vóór 2.1.211 kwam het bestand in de map waar je Claude Code startte. Oudere kopieën kunnen dus in submappen staan. Daarom zoeken de controles hieronder op elk pad. Buiten een git-repository, op Windows en wanneer de hoofdmap van de repository je homemap is, gebruikt Claude Code de hoofdmap van de repository niet. Het bestand staat dan naast .claude/settings.json.

Het vangnet werkt zo. De eerste keer dat Claude Code het bestand schrijft in een repository die het nog niet negeert, zet het de regel **/.claude/settings.local.json in je globale excludes-bestand van git. Dat is het bestand uit core.excludesFile als je globale git-configuratie daar een absoluut pad of een pad met ~ invult. Anders is het $XDG_CONFIG_HOME/git/ignore, of ~/.config/git/ignore als XDG_CONFIG_HOME niet is ingesteld. Git zelf valt op dezelfde twee locaties terug als core.excludesFile niet is ingesteld.

Waar laat het vangnet een wachtwoord door?

Het vangnet is één regel in een bestand in jouw homemap. Die regel komt nooit in de .gitignore van de repository zelf. Er zijn vier gevallen waarin het vangnet niets doet:

  • Je maakte het bestand zelf aan. Dat noemt de documentatie van Claude Code zelf: heeft Claude Code het bestand nog niet geschreven, voeg het dan zelf toe aan .gitignore.
  • De map werd pas later een repository. Apiiro noemt dit geval uitdrukkelijk.
  • Een kloon op een andere machine. De regel staat in jouw globale bestand, niet in de repository. Een collega of je CI-server heeft hem dus niet.
  • Het bestand is al gecommit. Volgens de documentatie van git gelden ignore-regels niet voor bestanden die git al volgt.

Nog één detail. Volgt git het bestand, dan behandelt Claude Code het als een bestand van de repository. Het houdt de regels dan tegen tot je de map vertrouwt. Dat beschermt jou tegen regels die iemand anders in een repository heeft gezet. Je wachtwoord blijft wel gewoon in de commit staan. De documentatie beschrijft geen melding die je waarschuwt dat het bestand gevolgd wordt, dus reken daar niet op.

Oefen de controle eerst op een wegwerprepository

Wil je de controles eerst zien werken? Bouw dan een kleine repository met een nepwachtwoord. Op Ubuntu ziet dat er zo uit:

sudo apt update && sudo apt install -y git
mkdir -p ~/sl-demo && cd ~/sl-demo
git init
git config user.name "Demo"
git config user.email "demo@example.com"
mkdir -p .claude
printf '%s\n' '{"permissions":{"allow":["Bash(PGPASSWORD=nep-wachtwoord-123 psql -h db.example.com -U app app_prod)"]}}' > .claude/settings.local.json
git add .claude/settings.local.json
git commit -m "Claude-instellingen"

git config zonder --global zet de naam en het e-mailadres alleen voor deze repository. Je globale instellingen blijven zo zoals ze waren. Meldt git add dat het pad genegeerd wordt, dan staat de regel van Claude Code al in je globale excludes-bestand. Dat is goed nieuws. Gebruik voor deze oefening dan git add -f. Draai daarna de controles hieronder in deze map. Op je echte project doe je precies hetzelfde.

Controleer of je repository een wachtwoord lekt

Volgt git het bestand?

git ls-files -- '*settings.local.json'

Het patroon met * vindt ook kopieën in submappen. Komt er geen uitvoer, dan volgt git op dit moment geen bestand met die naam. Komt er een pad terug, dan staat het bestand in je repository en gaat het mee bij elke push.

Staat er een geheim in de geschiedenis?

Een bestand dat git nu niet meer volgt, kan nog in een oudere commit staan. Doorzoek daarom de geschiedenis van alle branches:

git fetch --all
git log --all -p -- '*settings.local.json' | grep -nEi 'PGPASSWORD=|sshpass -p|-p[^ ]+|--password|authorization:|bearer '

git log --all -p toont elke wijziging aan het bestand, in elke branch. grep houdt alleen de regels over die op een bekend wachtwoordpatroon lijken. Het patroon -p[^ ]+ vindt mysql -pGeheim, maar ook onschuldige opties zoals --port. Lees dus elke gevonden regel zelf. Geeft grep geen uitvoer, dan staat geen van deze patronen in de geschiedenis van het bestand. Andere geheimen, zoals een API-sleutel in een URL, vindt dit patroon niet. Kijk bij twijfel de volledige uitvoer van git log --all -p door.

Waar staat jouw globale excludes-bestand?

git config --global core.excludesFile
echo "${XDG_CONFIG_HOME:-$HOME/.config}/git/ignore"

Het eerste commando toont het pad dat je zelf in core.excludesFile hebt gezet. Blijft de uitvoer leeg, dan heb je niets ingesteld. Git en Claude Code gebruiken dan het pad dat het tweede commando toont. Kijk of de regel van Claude Code in dat bestand staat:

grep -n 'settings.local.json' "${XDG_CONFIG_HOME:-$HOME/.config}/git/ignore"

Toont het eerste commando wel een pad, zoek dan in dat bestand. Zie je **/.claude/settings.local.json, dan werkt het vangnet op deze machine. Op elke andere machine waar je de repository kloont, controleer je dit opnieuw.

Eerst roteren, dan opruimen

Vind je een echt wachtwoord, vervang het dan voordat je iets aan git verandert. Ook Apiiro zet het roteren van elk geheim dat het bestand ooit bevatte bovenaan. De reden is eenvoudig. Het verwijderen van het bestand verandert de oude commits niet, dus het wachtwoord staat nog in de geschiedenis. Het staat ook in elke fork en in elke kloon die iemand al heeft gemaakt. Die kopieën haal je niet terug, ook niet als je de geschiedenis herschrijft. Herschrijven is opruimen. Het is geen bescherming.

Voor PostgreSQL wijzig je het wachtwoord van een rol als een gebruiker met genoeg rechten. Maak verbinding:

psql -h db.example.com -U postgres

Typ daarna in psql:

\password app

\password vraagt interactief om het nieuwe wachtwoord, zodat het niet in je shellgeschiedenis komt. Plan dit wel. Een applicatie die het oude wachtwoord gebruikt, kan pas weer verbinden als je het nieuwe wachtwoord ook daar invult.

Voor elk ander geheim dat je vindt, geldt hetzelfde. Een SSH-wachtwoord, een MySQL-gebruiker of een API-token uit een Authorization-header vervang je bij de dienst zelf, niet alleen in je repository. Kijk daarna in de logboeken van je database of dienst of iemand het oude geheim heeft gebruikt. Dat antwoord heb je straks ook nodig bij de vraag over het datalek.

Haal settings.local.json uit git

git rm --cached -- .claude/settings.local.json
printf '%s\n' '**/.claude/settings.local.json' >> .gitignore
git add .gitignore
git commit -m "Stop met het volgen van .claude/settings.local.json"
git check-ignore -v .claude/settings.local.json

git rm --cached haalt het bestand alleen uit de index, de lijst bestanden die git volgt. Het bestand zelf blijft op je schijf staan, met al je goedgekeurde regels. De regel in .gitignore gaat mee in de commit. Hij geldt daardoor voor iedere kloon, op elke machine. Zo dicht je het gat dat het globale vangnet openlaat.

git check-ignore -v toont welke regel een pad negeert, in de vorm bron:regelnummer:patroon, gevolgd door het pad. Noemt de bron .gitignore, dan staat de regel in de repository. Een absoluut pad als bron betekent dat alleen je globale bestand het pad negeert. Gebruik dit commando niet als controle zolang git het bestand nog volgt. Volgens de documentatie van git toont git check-ignore gevolgde bestanden helemaal niet, omdat ignore-regels daar niet voor gelden. Geen uitvoer betekent in dat geval dus niet dat alles goed is.

Let op bij een gedeelde repository. Haalt een collega deze commit binnen, dan verwijdert git het bestand uit de werkmap van die collega, want de commit verwijdert het bestand. Laat collega's vooraf een kopie maken als ze hun goedgekeurde regels willen houden. Wil je de geschiedenis daarna herschrijven, doe dat dan pas na het roteren. Dat vraagt een force-push en afspraken met iedereen die de repository al heeft gekloond.

Hoe voorkom je dat het opnieuw gebeurt?

Lees de vraag voordat je "Yes, and don't ask again" kiest. De tekst achter "don't ask again for:" is de regel die blijft. Staat daar een geheim, keur het commando dan één keer goed met "Yes". Of schrijf zelf een regel zonder geheim via /permissions.

Haal het wachtwoord uit de commandoregel. Voor PostgreSQL leest libpq, de clientbibliotheek achter psql, wachtwoorden uit ~/.pgpass. Elke regel heeft de vorm hostname:port:database:username:password. Volgens de PostgreSQL-documentatie negeert libpq het bestand als de groep of andere gebruikers er toegang toe hebben. Zet de rechten dus op 600:

nano ~/.pgpass
chmod 0600 ~/.pgpass

Het commando dat Claude Code dan draait, psql -h db.example.com -U app app_prod, bevat geen wachtwoord meer. Een regel als Bash(psql -h db.example.com *) bevat dan ook niets geheims. Bedenk wel dat zo'n regel Claude elke SQL-opdracht op die database laat uitvoeren. Hoe de verschillende toestemmingsmodi daarmee omgaan, staat in toestemmingen en auto mode in Claude Code.

Houd het bestand ook buiten bereik van de bestandstools van Claude. De documentatie raadt een Read-weigerregel aan voor geheime bestanden, zoals Read(./.env) of Read(./secrets/**). Voor een bestand in je homemap gebruik je in je gebruikersinstellingen een pad met ~/, bijvoorbeeld Read(~/.pgpass). Zo'n regel stopt de bestandstools, niet elk shellcommando. De documentatie noemt Bash-regels uitdrukkelijk geen veiligheidsgrens rond een programma. Meer over die grens lees je in geheimen buiten bereik van AI-agents houden.

Omgevingsvariabelen. Apiiro raadt aan om geheimen via een omgevingsvariabele aan te spreken in plaats van letterlijk. Zet je PGPASSWORD in je shell voordat je Claude Code start, dan hoeft het commando het wachtwoord niet te bevatten. De documentatie van Claude Code zegt niets over regels met $VARIABELE erin. Kijk dus zelf welke regel de vraag dan aanbiedt. De waarde zelf blijft zichtbaar voor elk commando in die omgeving. Een simpele env toont hem al.

Kies patroonregels in de vorm uit de documentatie. De documentatie schrijft Bash(npm run test *). Apiiro schrijft Bash(npm run:*). Volgens de documentatie matchen die twee dezelfde commando's, maar de vorm met :* werkt alleen aan het eind van een patroon. De spatie voor de * telt mee: Bash(ls *) matcht lsof niet, Bash(ls*) wel. Zet de * na het subcommando. Bash(git log *) staat alleen git log toe, Bash(git *) elk git-commando.

Zet de .gitignore-regel bij de start van elk project. Dan hangt niets meer af van het globale bestand op één machine. Draai je Claude Code op een server, lees dan ook Claude Code veilig draaien op een VPS. Wil je een extra controle voordat Claude Code een commando uitvoert, dan kun je die bouwen met hooks in Claude Code.

Moet je een gelekt databasewachtwoord als datalek melden?

Voor een Nederlandse organisatie hangt dat af van wat er achter het wachtwoord zit. Een gelekt wachtwoord is altijd een beveiligingsincident. Het is niet automatisch een datalek in de zin van de AVG.

De AVG definieert een inbreuk in verband met persoonsgegevens in artikel 4, lid 12. Het gaat om een inbreuk op de beveiliging die leidt tot vernietiging, verlies, wijziging, ongeoorloofde verstrekking van of ongeoorloofde toegang tot persoonsgegevens. Volgens artikel 33, lid 1, meldt de verwerkingsverantwoordelijke zo'n inbreuk zonder onredelijke vertraging aan de toezichthouder. Waar mogelijk gebeurt dat niet later dan 72 uur nadat de organisatie ervan op de hoogte is. Die plicht vervalt als het niet waarschijnlijk is dat de inbreuk een risico oplevert voor de rechten en vrijheden van personen. Artikel 33, lid 5, verplicht je wel om elke inbreuk vast te leggen, ook als je die niet meldt.

De vraag die je moet beantwoorden is dus concreet. Staan er persoonsgegevens in de database? Was die database bereikbaar voor iemand met het wachtwoord, bijvoorbeeld via internet? Tonen de logboeken toegang van buiten in de periode dat het wachtwoord openbaar stond? Hoe Nederland dit toepast, lees je in de uitleg van de Autoriteit Persoonsgegevens zelf, op autoriteitpersoonsgegevens.nl. Heeft je organisatie een functionaris gegevensbescherming, leg de vraag dan meteen daar neer. De 72 uur lopen vanaf het moment dat je van de inbreuk weet, niet vanaf het moment dat je klaar bent met onderzoeken.

FAQ

Zet Claude Code wachtwoorden in settings.local.json?

Alleen als het commando dat je met "Yes, and don't ask again" goedkeurt een wachtwoord bevat, en de optie in de vraag die tekst noemt. De documentatie zegt dat een bewaarde regel precies dekt wat de optie noemde. Een allow-regel kijkt niet voorbij een variabele zoals PGPASSWORD= aan het begin van een commando. Lees daarom de tekst achter "don't ask again for:" voordat je kiest. Apiiro vond in november 2026 honderden databasewachtwoorden in zulke bestanden op GitHub.

Is .claude/settings.local.json standaard uitgesloten van git?

Gedeeltelijk. Schrijft Claude Code het bestand voor het eerst in een repository die het nog niet negeert, dan zet het **/.claude/settings.local.json in je globale git-excludes-bestand. Dat is core.excludesFile of anders ~/.config/git/ignore. Die regel geldt alleen op jouw machine. Hij helpt niet bij een bestand dat je zelf aanmaakte of dat al gecommit is. Zet de regel daarom ook in de .gitignore van de repository.

Ik heb het bestand uit git verwijderd. Is het wachtwoord nu veilig?

Nee. git rm --cached haalt het bestand alleen uit de index. Oudere commits, forks en bestaande klonen bevatten het wachtwoord nog. Vervang het wachtwoord bij de database of dienst zelf. Controleer met git log --all -p -- '*settings.local.json' wat er nog in de geschiedenis staat. Herschrijf je de geschiedenis, dan is dat opruimen achteraf en geen vervanging van het roteren.

Wat is het verschil tussen Bash(npm run:) en Bash(npm run )?

Volgens de documentatie van Claude Code matchen ze dezelfde commando's. De vorm met de spatie is wat de toestemmingsvraag zelf schrijft. De vorm met :* werkt alleen aan het eind van een patroon. In Bash(git:* push) is de dubbele punt een gewoon teken, dus die regel matcht geen git-commando's.

Moet ik een gelekt databasewachtwoord altijd melden bij de Autoriteit Persoonsgegevens?

Niet altijd. Artikel 33 van de AVG vraagt een melding binnen 72 uur bij een inbreuk in verband met persoonsgegevens, tenzij een risico voor personen onwaarschijnlijk is. Een wachtwoord voor een database zonder persoonsgegevens, of voor een database die niemand van buiten kon bereiken, valt daar mogelijk niet onder. Leg elke inbreuk wel intern vast, zoals artikel 33, lid 5, vraagt. Controleer hoe de AP jouw geval ziet op autoriteitpersoonsgegevens.nl.

Was dit artikel nuttig?