Appendix Episode 51 10-12 min

Git Security for Contributors

Secrets, .gitignore, environment variables, push protection, and safe contributor habits.

Listen

Transcript

Alex: Welcome back. Today we're talking about Git security for contributors, especially the everyday habit of keeping secrets out of a repository.

Jamie: And by secrets, we mean more than passwords, right? Because I hear that word and immediately wonder what actually counts.

Alex: Right. A secret is anything that lets a person or a program access something they should not be able to access. That includes API keys, GitHub personal access tokens, database passwords, private keys, cloud credentials, payment provider keys, and sometimes configuration files that contain those values.

Jamie: So if one of those lands in Git, the problem is not just that it looks embarrassing in a commit.

Alex: Exactly. If a secret reaches a public GitHub repository, even briefly, you should treat it as compromised. Bots scan public repositories constantly, Git history keeps old versions, forks can preserve the data, and search engines may index it. The consequences can be very real: cloud bills, private repo access, impersonation, or fraudulent use of a service. GitHub can automatically revoke some GitHub-issued tokens when it detects them, but third-party keys, like AWS or Stripe, usually need you to rotate them yourself, fast.

Jamie: How should someone use this appendix if they're listening first and reading later?

Alex: Use it as a practical reference. It starts with why leaks matter, then moves into prevention, then recovery, then daily habits. If you're using a screen reader, the code blocks contain exact patterns and commands, so switch into Focus Mode before copying. If you're low vision, the examples are meant to be easy to scan, and the checklist near the end works well as a repeatable pre-push routine.

Jamie: I like that it is not asking people to become security specialists before they make a contribution.

Alex: No, the goal is steady contributor hygiene. The appendix points back to GitHub's code security documentation and related security features as the authoritative reference, so when GitHub changes an interface or expands protection, that is where you confirm the current details.

Jamie: Let's start with the file I see everywhere but sometimes ignore: .gitignore.

Alex: A .gitignore file tells Git which untracked files to leave alone. If a file matches a pattern in .gitignore, it should not show up in git status, it should not be staged by git add, and it should not be committed. That makes it your first prevention layer for local files that do not belong in the repository.

Jamie: What goes in there for security purposes?

Alex: Environment files are the big one: .env, .env.local, .env.development, .env.production, and patterns like star dot env. Key files belong there too, such as .pem, .key, .p12, .pfx, id_rsa, and id_ed25519. Credential files also belong there, including credentials.json, secrets.json, config/secrets.yml, and .aws/credentials.

Jamie: And there are non-secret files that still create noise.

Alex: Yes. On macOS, that might be .DS_Store or .AppleDouble. On Windows, Thumbs.db and desktop.ini. Some teams ignore VS Code settings, though other teams intentionally commit shared settings, so check the project norm. Then there are build outputs and dependencies: node_modules, dist, build, Python __pycache__, .pyc files, virtual environments like .venv and venv, plus logs, temp files, and cache files.

Jamie: Here is the part that used to trip me up. If I add .env to .gitignore after Git already knows about .env, am I safe?

Alex: Not yet. .gitignore only prevents new untracked files from being added. To check a specific file, run git ls-files .env. If Git prints .env back to you, it is already tracked, so run git rm --cached .env to stop tracking it while keeping your local file. Then add .env to .gitignore and commit that change.

Jamie: What about the files I never want to commit anywhere, no matter which project I'm in?

Alex: That's a good use for a global gitignore. You can create one with touch ~/.gitignore_global, then tell Git to use it with git config --global core.excludesfile ~/.gitignore_global. Put operating system and editor clutter there. And when you create a new repository, GitHub offers language templates for .gitignore; for an existing Node project, the appendix shows downloading the Node template from github.com/github/gitignore and appending it to your .gitignore.

Jamie: So .gitignore keeps local secret files out. But where should the secret actually live when the code needs it?

Alex: The usual pattern is environment variables. Do not hardcode something like API_KEY equals sk-abc123 in your source file. Instead, the code reads from the environment, such as os.environ.get('API_KEY') in Python or process.env.API_KEY in JavaScript. The secret exists outside the repository, and the code only knows the variable name.

Jamie: A lot of projects use .env files for that locally.

Alex: They do, and .env is convenient for local development. It might contain GITHUB_TOKEN, DATABASE_URL, or STRIPE_SECRET_KEY for your own machine. The code can load it with a library like dotenv for JavaScript or python-dotenv for Python. But the .env file itself must stay in .gitignore.

Jamie: What if a team needs to share the real value?

Alex: Do not put it in Slack, email, a GitHub comment, or an issue. For GitHub Actions, store it in GitHub Actions Secrets under repository settings, then Secrets and variables, then Actions. In a workflow, you can pass it as an environment variable with something like API_KEY from secrets.API_KEY, so the workflow can use it without the value being written into code. For human sharing, use a shared password manager, and for production systems, use a real secrets manager such as AWS Secrets Manager or HashiCorp Vault.

Jamie: So the code can be public, the variable name can be public, but the value needs a protected home.

Alex: The most reliable daily habit is reviewing what you are about to commit before you commit it.

Jamie: This is where git add dot makes me nervous. It feels convenient, but I never know what else came along for the ride.

Alex: That is the risk. git add . stages everything in the working directory that Git can add. A safer habit is to stage specific files, like git add src/auth.js docs/README.md, when you know those are the intended changes. Or use git add -p, patch mode, which walks through changes chunk by chunk and asks whether to stage each one.

Jamie: After staging, how do I inspect the exact thing Git is going to record?

Alex: Run git status to hear which files are staged and which are not. Then run git diff --staged to review the full staged diff, or git diff --staged docs/config.md for one file. Look for hardcoded passwords, tokens, API keys, .env files, credential files, and TODO comments that mention private details. If your organization allows it, Copilot Chat can help review a staged diff for accidental secrets, but use the tools and data-sharing rules your project expects.

Jamie: That makes the commit feel less like a leap of faith and more like a final inspection.

Alex: Manual review is important, and automation can catch mistakes you miss.

Jamie: This is where pre-commit hooks come in, right? I know the term, but I want the plain version.

Alex: A pre-commit hook is a script that runs automatically when you try to make a commit. If it finds a likely secret or another problem, it blocks the commit and tells you what it found. You can then remove the problem, stage the cleaned change, and try again.

Jamie: The appendix mentions detect-secrets first. What is the workflow there?

Alex: detect-secrets is a Python-based tool and the recommended option in the appendix. You install it, scan the existing repository to create a baseline, review that baseline so known false positives are marked safe, install the pre-commit hook, and test it manually. The baseline matters because it keeps the tool focused on new suspicious changes instead of shouting about old harmless examples every time.

Jamie: And if someone prefers a different tool?

Alex: gitleaks is another option. It is Go-based, has simple installs on macOS and Windows, can scan the full repository history, and can scan staged changes, which means it checks what you are about to commit. The appendix also shows how to wire it into .git/hooks/pre-commit manually.

Jamie: Then there is the pre-commit framework, which sounds broader than one secret scanner.

Alex: Exactly. The pre-commit framework manages multiple hooks from a .pre-commit-config.yaml file in the repo root. You install the framework, add the config, install the hooks, and you can run them manually against all files. That is useful when a project wants secret scanning, formatting, linting, and other checks to run in one repeatable way.

Jamie: Okay, uncomfortable scenario. I committed a secret. What do I do first?

Alex: Rotate the secret immediately. That means create a new key, token, or password, update the service that uses it, and revoke the old one. Do this before you spend time cleaning Git history, because the exposed value may already have been copied.

Jamie: What if I committed it locally but did not push?

Alex: That is easier. If it was the last commit, git reset --soft HEAD~1 undoes the commit while keeping your changes staged. Then edit the file, remove the secret, make sure the secret file or pattern is in .gitignore, stage the cleaned version, and commit again.

Jamie: And if it was pushed to a public repository?

Alex: Still rotate first. Then you need to remove the secret from Git history, not just from the latest file. The appendix recommends git-filter-repo as the modern tool after installing it; it can remove a specific file from all history or replace a specific secret string throughout history. BFG Repo-Cleaner is the fast Java-based alternative; you download it from its project site, remove files from history, or provide a passwords.txt file with secret strings to replace.

Jamie: History cleanup sounds like something that can affect other contributors.

Alex: It can. Rewriting history usually means a force push of the cleaned history, and collaborators may need to re-clone or carefully reset their local copies. After the cleanup, ask GitHub to rescan if alerts remain. The short decision path is: rotate, decide whether it was only local or already pushed, remove the current exposure, clean history when needed, push the cleaned state, and verify alerts are resolved.

Jamie: GitHub also has built-in protection before a bad push lands, right?

Alex: Yes. GitHub secret scanning looks for known secret patterns, and push protection can block a push when GitHub detects a supported secret type. It is especially helpful because it interrupts the mistake before the secret becomes part of the public repository.

Jamie: Does that mean I can rely on it to catch every secret?

Alex: No. It covers many supported token formats and partner patterns, but it will not catch every custom password, private note, or unusual credential. If push protection blocks you, do not bypass it unless you are certain it is a false positive and your project policy allows the bypass. Remove the secret, rotate it if it was real, recommit the clean change, and push again.

Jamie: Where does someone check whether it is enabled?

Alex: In the repository settings, look for the code security area, then secret scanning and push protection if they are available for that repository and organization. GitHub interface names can shift, so the important thing is to check the repository's security settings and confirm the current status there.

Jamie: Let's talk about credentials on my own computer. Where should Git and command-line tools store them?

Alex: Not in a plain text file, not hardcoded in a script, and not pasted into git config. On macOS, use Keychain. On Windows, Git for Windows normally uses Credential Manager. On Linux, a libsecret-backed credential store is the safer option when it is installed and configured.

Jamie: How do I check what Git is using right now?

Alex: Run git config --global credential.helper to see the global helper. You can also check the local repository config if needed. For credentials that humans need to retrieve and share, use a password manager with secure sharing rather than a document, chat thread, or note file.

Jamie: If someone wants a simple routine before contributing to open source, what should they actually do?

Alex: Before committing, confirm .gitignore covers environment and credential files, stage only the files you intend to include, run git status, review git diff --staged, and let your hooks run. Before pushing, watch for secret scanning or push protection messages, and do not push emergency debug files or local configuration. For one-time repository setup, add a language-appropriate .gitignore, configure a global gitignore, set up hooks, store workflow values in GitHub Actions Secrets, and enable the available code security features.

Jamie: And maintainers have a few extra responsibilities.

Alex: They do. Maintainers should enable secret scanning and push protection where available, use branch protection where it fits the project, document how to report a leaked secret, and keep dependency alerts such as Dependabot visible. That way contributors are not relying only on memory; the repository itself helps protect the work.

Jamie: I appreciate how practical this is. Security here is not about being perfect. It is about building enough checks into your normal Git flow that one tired afternoon does not become a public incident.

Workshop Content

Full chapter content from the Git Going with GitHub workshop guide.

Companion Podcast and Transcript

Use audio and transcript companions to review concepts in a conversational format.

Git Security for Contributors

Companion audio: this episode reinforces key ideas and may not be a word-for-word reading of this page.

Transcript preview

Alex: Welcome back. Today we're talking about Git security for contributors, especially the everyday habit of keeping secrets out of a repository.

Jamie: And by secrets, we mean more than passwords, right? Because I hear that word and immediately wonder what actually counts.

Alex: Right. A secret is anything that lets a person or a program access something they should not be able to access. That includes API keys, GitHub personal access tokens, database passwords, private keys, cloud credentials, payment provider keys, and sometimes configuration files that contain those values.

Jamie: So if one of those lands in Git, the problem is not just that it looks embarrassing in a commit.

Appendix F: Git Security for Contributors

Episode coming soon: Git Security for Contributors - a conversational audio overview of this appendix. Listen before reading to preview the concepts, or after to reinforce what you learned.

Reference companion to: Chapter 08: Open Source Culture | Also relevant: Chapter 14

Authoritative source: GitHub Docs: Code security

Keeping Secrets Out of Your Repository

Who this is for: Anyone committing code or documentation to a repository. You don't need to be a security expert — this appendix covers the practical habits that protect you and the projects you contribute to. Most security incidents in open source aren't caused by attacks; they're caused by accidents. A token committed by mistake, a password left in a config file, a .env file that slipped through.

The good news: a few simple habits prevent almost all of them.


Table of Contents

  1. Why This Matters — What Happens When Secrets Leak
  2. The .gitignore File — Your First Line of Defense
  3. Environment Variables — The Right Way to Store Secrets
  4. Review Before You Commit
  5. Pre-Commit Hooks — Automated Secret Detection
  6. I Accidentally Committed a Secret — What Now?
  7. GitHub's Built-In Push Protection
  8. Secure Credential Storage
  9. Security Checklist for Contributors

Learning Cards: Using This Security Reference

Screen reader users
  • Sections are ordered from understanding (section 1) to prevention (2-5) to recovery (6-7) to daily habits (8-9)
  • Code blocks contain exact gitignore patterns and terminal commands -- switch to Focus Mode before copying
  • The Security Checklist (section 9) is a task list you can use before every push
Low vision users
  • Code examples for .gitignore patterns and terminal commands are in high-contrast code blocks
  • Warning callouts use bold text -- scan for bold to find the most critical safety notes
  • The Security Checklist at the bottom uses checkbox formatting for easy visual tracking
Sighted users
  • Read section 1 for motivation, then jump to the section matching your current need
  • The .gitignore templates in section 2 are copy-paste ready for most project types
  • Skip to the Security Checklist (section 9) for a pre-push routine you can follow every time

1. Why This Matters — What Happens When Secrets Leak

When a secret (API key, token, password, private key) is committed to a public GitHub repository — even for a few seconds before you delete it — it's effectively compromised.

Why "I'll just delete it right away" isn't enough:

  • Bots scan GitHub continuously and harvest secrets within seconds of a push
  • The secret lives in your git history even after you delete the file
  • GitHub forks capture history — once forked, you can't fully erase it
  • Search engines may index the content before you remove it

Real-world consequences:

  • An AWS key leaked to a public repo can result in thousands of dollars of compute charges within hours
  • A GitHub PAT can be used to access private repositories, delete code, or impersonate you
  • A Stripe API key can be used to make fraudulent charges against your account

The good news: GitHub automatically revokes its own tokens (PATs, GitHub App tokens) when it detects them in a commit. But third-party services (AWS, Stripe, Twilio, etc.) require you to rotate the secret manually — and fast.


2. The .gitignore File — Your First Line of Defense

A .gitignore file tells Git which files to never track. Files listed in .gitignore won't show up in git status, won't be staged by git add, and won't be committed.

What belongs in .gitignore

Secrets and credentials

# Environment files (contain API keys, database passwords, etc.)
.env
.env.local
.env.*.local
.env.development
.env.production
*.env

# Key files
*.pem
*.key
*.p12
*.pfx
id_rsa
id_ed25519

# Credential files
credentials.json
secrets.json
config/secrets.yml
.aws/credentials

Editor and OS clutter

# macOS
.DS_Store
.AppleDouble

# Windows
Thumbs.db
desktop.ini

# VS Code (optional — some teams commit these)
.vscode/settings.json

# JetBrains IDEs
.idea/

Build output and dependencies

# Node
node_modules/
dist/
build/

# Python
__pycache__/
*.pyc
.venv/
venv/

# General
*.log
*.tmp
*.cache

Checking if a file is already tracked

.gitignore only prevents untracked files from being added. If Git is already tracking a file, .gitignore won't stop it from being committed in the future.

# Check if a specific file is tracked
git ls-files .env

# If it returns the filename, it's being tracked — you need to untrack it
git rm --cached .env
# Then add it to .gitignore and commit

Global .gitignore — apply to every repo on your machine

You can create a global .gitignore that applies to all repositories on your computer — useful for OS-specific and editor-specific files you never want to commit anywhere.

# Create a global gitignore file
touch ~/.gitignore_global

# Tell Git to use it
git config --global core.excludesfile ~/.gitignore_global

Add your editor and OS files to ~/.gitignore_global so you never have to add them to individual repos.

GitHub's .gitignore templates

When creating a new repository on GitHub, you can choose a .gitignore template for your language — GitHub pre-fills it with the most common patterns for that ecosystem. Find all templates at github.com/github/gitignore.

For an existing project:

# Download a template (e.g., for Node.js)
curl https://raw.githubusercontent.com/github/gitignore/main/Node.gitignore >> .gitignore

3. Environment Variables — The Right Way to Store Secrets

Instead of hardcoding secrets in your files, store them in environment variables that live outside of your repository.

The pattern

# Never do this (hardcoded secret in code)
API_KEY = "sk-abc123yoursecretkeyhere"

# Do this instead (read from environment)
API_KEY = os.environ.get("API_KEY")      # Python
const apiKey = process.env.API_KEY;      // JavaScript

Using a .env file locally

A .env file stores your local environment variables. It's convenient and universally supported — and it must be in your .gitignore.

# .env (NEVER commit this file)
GITHUB_TOKEN=ghp_yourtokenhere
DATABASE_URL=postgres://user:password@localhost/mydb
STRIPE_SECRET_KEY=sk_test_yourkeyhere

Load it in your code with a library like dotenv (JavaScript) or python-dotenv (Python). The .env file stays on your machine; the code that reads it goes into the repository.

Sharing secrets with your team safely

Never send secrets in Slack, email, or GitHub comments. Use:

  • GitHub Actions Secrets — for CI/CD pipelines: Settings → Secrets and variables → Actions
  • A password manager with sharing (1Password Teams, Bitwarden) — for team credentials
  • A secrets manager (AWS Secrets Manager, HashiCorp Vault) — for production systems

Example: Using GitHub Actions Secrets

# .github/workflows/deploy.yml
jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - name: Deploy
        env:
          API_KEY: ${{ secrets.API_KEY }}   # Pulled from GitHub Secrets, never in code
        run: ./deploy.sh

4. Review Before You Commit

The most effective habit is simply reviewing what you're about to commit before you commit it.

git diff --staged — see exactly what's going in

# Review all staged changes before committing
git diff --staged

# Review a specific file
git diff --staged docs/config.md

Read through the diff looking for:

  • Any hardcoded passwords, tokens, or API keys
  • .env or credential files that snuck in
  • Any TODO comments that reference sensitive information

Avoid git add . blindly

git add . stages everything in your working directory — including files you didn't mean to add.

# Risky - stages everything without review
git add .

# Better - stage specific files you know are clean
git add src/auth.js docs/README.md

# Or stage interactively - review each file before adding
git add -p

git add -p (patch mode) walks you through each change chunk by chunk and asks whether to stage it. It's slower but gives you full control.

Check what's staged before committing

# See which files are staged (and which aren't)
git status

# See the full diff of staged changes
git diff --staged

GitHub Copilot can help: After staging your changes, open Copilot Chat and ask: "Review my staged changes for any accidentally included secrets, API keys, or credentials." Paste the output of git diff --staged into the chat.


5. Pre-Commit Hooks — Automated Secret Detection

A pre-commit hook is a script that runs automatically every time you try to commit. If the script detects a problem (like a potential secret), it blocks the commit and tells you what it found.

Think of it as a safety net that catches things you might have missed during review.

detect-secrets scans for over 20 types of secrets and integrates well with existing repos.

# Install
pip install detect-secrets

# Create a baseline (scan your existing code — mark known non-secrets as safe)
detect-secrets scan > .secrets.baseline

# Install the pre-commit hook
detect-secrets hook

# Test it manually
detect-secrets scan

After setup, any commit containing a potential secret is blocked with a clear message showing which file and line triggered the alert.

Option B: gitleaks (Go-based, zero dependencies)

# Install on macOS
brew install gitleaks

# Install on Windows
winget install gitleaks

# Scan your entire repo history for secrets
gitleaks detect --source . --verbose

# Scan staged changes only (what you're about to commit)
gitleaks protect --staged

# Add as a pre-commit hook manually
# Add this to .git/hooks/pre-commit:
gitleaks protect --staged -v

Option C: pre-commit framework (manages multiple hooks)

The pre-commit framework lets you install and manage hooks from a YAML config file, making it easy to share hook config across your team.

# Install
pip install pre-commit

# Create .pre-commit-config.yaml in your repo root:
# .pre-commit-config.yaml
repos:
  - repo: https://github.com/Yelp/detect-secrets
    rev: v1.4.0
    hooks:
      - id: detect-secrets

  - repo: https://github.com/gitleaks/gitleaks
    rev: v8.18.0
    hooks:
      - id: gitleaks
# Install the hooks
pre-commit install

# Run manually against all files
pre-commit run --all-files

Note: Pre-commit hooks live in .git/hooks/ and are local to your machine — they're not committed to the repo automatically. To share hook config with your team, commit the .pre-commit-config.yaml file and ask everyone to run pre-commit install.


6. I Accidentally Committed a Secret — What Now?

Stay calm and act quickly. Follow these steps in order.

Step 1: Rotate the secret immediately

Before anything else — go to wherever that secret is managed and revoke or rotate it. It may already be compromised, so neutralizing it is more important than removing it from git history.

Secret type Where to rotate
GitHub PAT github.com/settings/tokens → Delete and regenerate
SSH key github.com/settings/keys → Delete and generate new
AWS key AWS IAM Console → Deactivate and create new
Stripe key Stripe Dashboard → Developers → API Keys → Roll key
Any other API key Check the service's dashboard for key management

GitHub automatically revokes its own tokens when secret scanning detects them. Other services do not.

Step 2: Was it pushed to a public repo?

If it was pushed (remote has the secret):

The secret is potentially already compromised — assume it was harvested. Rotation is critical. Then remove it from history:

If it was only committed locally (not pushed):

You can fix it cleanly before anyone sees it:

# Undo the last commit, keep your changes staged (safest)
git reset --soft HEAD~1

# Now remove the secret from the file, re-add, and re-commit
# (Edit the file to remove the secret)
git add -p   # Review what you stage
git commit -m "Your original commit message without the secret"

Step 3: Remove the secret from git history

This only matters if the commit was pushed. If it was local-only and you used git reset --soft above, you're done.

# Install git-filter-repo
pip install git-filter-repo

# Remove a specific file from all history
git filter-repo --path secrets.json --invert-paths

# Replace a specific string (the secret value) throughout all history
git filter-repo --replace-text <(echo "ghp_actualtoken==>REMOVED")

Method B: BFG Repo-Cleaner (fast, Java-based)

# Download BFG
# From https://rtyley.github.io/bfg-repo-cleaner/

# Remove a file from all history
java -jar bfg.jar --delete-files secrets.json

# Replace secret strings
# Create a file called passwords.txt with the secret on each line
java -jar bfg.jar --replace-text passwords.txt

Step 4: Force push the cleaned history

After rewriting history, you must force push:

git push --force-with-lease origin main

Coordinate with your team first. Anyone who has cloned or pulled the repo will need to re-clone or rebase after a force push. Send a heads-up before doing this on a shared repo.

Step 5: Tell GitHub to rescan

After removing the secret from history, go to Security → Secret scanning in your repository and mark any open alerts as resolved.

Quick decision flowchart

Secret committed
      │
      ├─ Still local only (not pushed)?
      │     └─ git reset --soft HEAD~1 -> remove secret -> recommit
      │
      └─ Already pushed?
            ├─ Rotate the secret FIRST (assume compromised)
            ├─ Remove from history with git filter-repo or BFG
            └─ Force push + notify team

7. GitHub's Built-In Push Protection

GitHub automatically scans pushes for known secret patterns before they reach the remote. If it detects a secret, the push is blocked.

remote: Push cannot contain secrets.
remote:
remote:   Secret detected: GitHub Personal Access Token
remote:   File: config/settings.py, Line: 14
remote:
remote:   To bypass (if this is a false positive):
remote:   https://github.com/owner/repo/security/secret-scanning/unblock-secret/TOKEN

What push protection covers

GitHub knows the patterns for hundreds of secret types including:

  • GitHub tokens (PATs, GitHub App tokens, OAuth tokens)
  • AWS access keys
  • Azure credentials
  • Google Cloud keys
  • Stripe, Twilio, Slack, and dozens more API keys

If push protection blocks you

  1. Confirm it's actually a secret — check the file and line mentioned
  2. If it's a real secret: Remove it from the file, amend your commit, and push again
  3. If it's a false positive: Use the bypass URL GitHub provides to push with an explanation

Checking your repo's push protection status

As a contributor you can see push protection in action when a push is blocked. Maintainers configure it in Settings → Code security → Push protection.

For full detail on GitHub's security scanning features: See Appendix L: GitHub Security Features.


8. Secure Credential Storage

Never store credentials in plaintext

Do not do these:

# Storing a token in a plain text file
echo "ghp_mytoken" > ~/token.txt

# Hardcoding in a script
export GITHUB_TOKEN="ghp_mytoken"  # in a .bashrc or .zshrc that's committed

# In a git config
git config --global url."https://myusername:ghp_mytoken@github.com".insteadOf "https://github.com"

Do this instead - use the OS credential store:

# macOS — use Keychain
git config --global credential.helper osxkeychain

# Windows — use Credential Manager (set automatically by Git for Windows)
git config --global credential.helper wincred

# Linux — use the libsecret store (requires installation)
git config --global credential.helper /usr/share/doc/git/contrib/credential/libsecret/git-credential-libsecret

With a credential helper set, Git asks for your credentials once and stores them securely in the OS keychain — not in any file.

Using a password manager

Store your GitHub PAT, SSH key passphrase, and other credentials in a password manager (1Password, Bitwarden, KeePass). Most support browser extensions, CLI access, and automatic lock after inactivity.

Checking what credential helper is set

git config --global credential.helper

If this returns nothing, your credentials may be stored in plaintext. Set a credential helper as above.

Learning Cards: Security Checklist

Screen reader users
  • The checklist below uses Markdown task list formatting -- each item is announced as "checkbox not checked"
  • Items are grouped into three categories: Before Committing, Before Pushing, and Repository Setup
  • Read through the list once to learn the habits, then use it as a pre-push routine
Low vision users
  • Checkboxes create a clear visual pattern for scanning -- each line starts with a square box
  • Three groups are separated by h3 headings: Before Committing, Before Pushing, Repository Setup
  • Consider copying this checklist into a personal note and checking items off for each project
Sighted users
  • This is a printable pre-push checklist -- bookmark it or copy it into your project's CONTRIBUTING.md
  • Three sections cover the complete workflow: staging, pushing, and one-time repository setup
  • The most critical items are the first two in "Before Committing" -- git diff review and selective git add

9. Security Checklist for Contributors

Use this before every push to a public repository.

Before committing

  • I reviewed git diff --staged and didn't see any tokens, passwords, or keys
  • I used git add <specific files> or git add -p rather than git add .
  • Any .env files or credential files are listed in .gitignore
  • Config files with real values are in .gitignore; only example/template files are committed

Before pushing

  • git log --oneline -5 — all commits look expected
  • No commits with messages like "remove secret" or "oops" that suggest a secret was added and removed (the secret is still in history)

Repository setup (one time)

  • .gitignore includes .env, *.key, *.pem, and relevant patterns for your stack
  • Global .gitignore (~/.gitignore_global) covers editor/OS files
  • Git credential helper is configured to use the OS keychain
  • (Optional) A pre-commit hook is installed to scan for secrets automatically

If you're a maintainer

  • Branch protection is enabled on main with required reviews and status checks
  • Secret scanning is enabled (Settings → Code security → Secret scanning)
  • Push protection is enabled for the repository
  • A SECURITY.md file exists with instructions for reporting vulnerabilities

See also: Appendix L: GitHub Security Features for the GitHub platform security tools (Dependabot, secret scanning alerts, code scanning). Appendix D: Git Authentication for SSH keys, PATs, and commit signing.


Next: Appendix G: VS Code Reference
Back: Appendix E: Advanced Git
Teaching chapter: Chapter 08: Open Source Culture

Authoritative Sources

Use these official references when you need the current source of truth for facts in this chapter.

Section-Level Source Map

Use this map to verify facts for each major section in this file.