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
- 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.
- 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 -rmọi file, và đổi luôn các secret bên trong (người có key cũ đã đọc được chúng). - Kiểm tra đã bị dùng chưa: audit log của nhà cung cấp, log đăng nhập TACACS+/SSH, CloudTrail...
- Tìm mọi chỗ secret xuất hiện: gitleaks trên toàn bộ lịch sử, mọi nhánh.
- Rewrite lịch sử bằng
git filter-repo(sau khi đã rotate). - Force push, báo team xoá clone cũ và clone lại; đóng/rebase các PR đang mở.
- 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.jsonFalse 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-cleanCách 1: thay chuỗi secret trong mọi file, mọi commit:
<SECRET_LITERAL_1>
<SECRET_LITERAL_2>==>***REMOVED***
regex:(?i)(ansible_password:\s*).+==>\1***git filter-repo --replace-text ../replacements.txtDò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 originForce 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
.env
.env.*
!.env.example
*.pem
*.key
*.p12
keys.txt
*.vault-passGitHub: 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