Sổ tay kỹ thuật

Lỡ commit secret vào Git – rotate, git filter-repo, gitleaks

troubleshooting#git#secrets#gitleaks· cập nhật 2026-11-06

Làm gì ngay khi phát hiện token, mật khẩu hay private key đã bị commit (dù đã push hay chưa), và cách chặn để không lặp lại.

Sửa trang này
Mục lục

Triệu chứng chính: GitHub gửi alert secret scanning, push bị chặn bởi push protection, gitleaks báo trong CI, hoặc bạn tự thấy .env, keys.txt, id_ed25519, token API trong git log -p.

Rotate trước, dọn lịch sử sau. Secret đã lên remote thì coi như đã lộ: có thể đã bị clone, fork, cache, hoặc bot quét repo public bắt được trong vài phút. Viết lại lịch sử Git không thu hồi được secret; chỉ thu hồi/đổi secret mới có tác dụng.

Lỗi thường gặp

Tình huống Hậu quả Cách xử lý
Commit nhưng chưa push Secret chỉ ở máy local Sửa commit: git reset --soft HEAD~1 hoặc git commit --amend, xoá secret, thêm vào .gitignore. Vẫn nên rotate nếu đã gửi file đi đâu khác
Đã push lên nhánh feature, chưa merge Ai có quyền đọc repo đều có thể đã thấy Rotate, rồi rewrite nhánh, git push --force-with-lease
Đã vào main / repo public Lộ hoàn toàn Rotate ngay, kiểm tra log sử dụng secret, rồi git filter-repo toàn repo, báo cả team re-clone
Push bị push protection chặn Secret chưa lên GitHub Gỡ secret khỏi mọi commit chưa push (amend/rebase), push lại. Không bấm "allow secret" trừ khi chắc là false positive
Đã xoá file ở commit sau Secret vẫn nằm trong lịch sử Commit xoá không đủ; cần rotate + filter-repo
Secret nằm trong PR đã đóng / fork refs/pull/* và fork không bị rewrite theo repo gốc Rotate là biện pháp duy nhất chắc chắn; liên hệ GitHub Support để purge cache/PR ref nếu cần

Các bước khoanh vùng

  1. Xác định secret là gì, cấp bởi ai: token GitHub, Cloudflare, API cloud, mật khẩu thiết bị, age key, TLS key.
  2. Rotate / revoke tại nơi cấp. Với private key TLS: cấp lại cert với key mới và thu hồi cert cũ. Với age key SOPS: sinh key mới, sops updatekeys + sops -r mọi file, và đổi luôn các secret bên trong (người có key cũ đã đọc được chúng).
  3. Kiểm tra đã bị dùng chưa: audit log của nhà cung cấp, log đăng nhập TACACS+/SSH, CloudTrail...
  4. Tìm mọi chỗ secret xuất hiện: gitleaks trên toàn bộ lịch sử, mọi nhánh.
  5. Rewrite lịch sử bằng git filter-repo (sau khi đã rotate).
  6. Force push, báo team xoá clone cũ và clone lại; đóng/rebase các PR đang mở.
  7. Chặn tái diễn: .gitignore, gitleaks pre-commit + CI, bật push protection, chuyển secret sang SOPS/ansible-vault.

Cấu hình đúng

Quét bằng gitleaks

# gitleaks v8.19+ (lệnh mới)
gitleaks git -v --redact                       # quét toàn bộ lịch sử repo hiện tại
gitleaks dir -v --redact .                     # quét file trong thư mục (không cần git)
gitleaks git --pre-commit --staged -v --redact # chỉ phần đã stage
 
# gitleaks bản cũ hơn: detect / protect
gitleaks detect --source . -v --redact
gitleaks protect --staged -v --redact
 
# Xuất báo cáo cho CI
gitleaks git --redact --report-format json --report-path gitleaks.json

False positive: thêm fingerprint (cột Fingerprint trong output) vào file .gitleaksignore ở gốc repo, hoặc tuỳ biến rule trong .gitleaks.toml. Tích hợp pre-commit xem pre-commit hooks.

Xoá secret khỏi lịch sử bằng git filter-repo

pip install git-filter-repo        # hoặc apt install git-filter-repo
 
# Làm trên clone MỚI (filter-repo từ chối chạy trên clone đang dùng nếu không có --force)
git clone --mirror git@github.com:example/netops.git netops-clean
cd netops-clean

Cách 1: thay chuỗi secret trong mọi file, mọi commit:

../replacements.txt
<SECRET_LITERAL_1>
<SECRET_LITERAL_2>==>***REMOVED***
regex:(?i)(ansible_password:\s*).+==>\1***
git filter-repo --replace-text ../replacements.txt

Dòng không có ==> sẽ được thay bằng ***REMOVED***. Đặt replacements.txt ngoài repo và xoá nó sau khi xong.

Cách 2: xoá hẳn file khỏi lịch sử:

git filter-repo --invert-paths --path .env --path inventories/prod/group_vars/all/plain-secrets.yml

Đẩy lên lại:

git remote add origin git@github.com:example/netops.git   # filter-repo gỡ remote origin để tránh push nhầm
git push --force --mirror origin

Force push viết lại toàn bộ hash commit. Tạm tắt branch protection nếu cần, báo mọi người clone lại (đừng pull, bản cũ sẽ merge secret trở lại), và tag/release cũng đổi hash.

Chặn từ đầu

.gitignore
.env
.env.*
!.env.example
*.pem
*.key
*.p12
keys.txt
*.vault-pass

GitHub: Settings → Code security (Advanced Security) bật Secret scanning và Push protection. Repo public được bật miễn phí; repo private trong organization cần GitHub Secret Protection / Advanced Security.

Kiểm tra nhanh

# Secret còn trong lịch sử? (mọi nhánh, mọi tag)
git log --all -p -S '<SECRET_LITERAL>' --oneline | head
gitleaks git -v --redact --log-opts="--all"
 
# File nhạy cảm từng được commit?
git log --all --oneline -- .env '*.pem' '*.key'
 
# File hiện tại có bị ignore chưa
git check-ignore -v .env