Lỡ commit file .env lên GitHub, xóa đi vẫn chưa xong
Xóa file .env, xóa commit hay viết lại lịch sử đều không thu hồi được key đã lộ. Việc đầu tiên là đổi khóa, sau đó mới dọn lịch sử, quét bằng gitleaks và chặn trong CI để không lặp lại.
Xem nhanh trong 1 phút
Bạn đẩy code lên GitHub, năm phút sau mới thấy file .env nằm trong commit. Phản xạ đầu tiên của hầu hết mọi người là git rm .env, commit tiếp, rồi thở phào. Có người cẩn thận hơn thì git reset, push --force cho commit đó biến mất.
Cả hai cách đều để key còn sống. Bot quét GitHub công khai liên tục, và một key nằm trên repo public vài phút là đủ để bị chép đi. Theo báo cáo năm 2025 của GitGuardian, năm 2024 có 23,8 triệu bí mật mới lộ trên GitHub công khai, và 70% bí mật lộ từ năm 2022 tới lúc khảo sát vẫn dùng được. Nghĩa là phần lớn người làm lộ đã không đổi key.
Vì sao xóa không đủ
Git giữ lại mọi thứ. Commit mới xóa .env chỉ làm file biến mất khỏi phiên bản hiện tại, còn ai mở lịch sử ra vẫn thấy. Viết lại lịch sử rồi force push thì sạch hơn, nhưng tài liệu của GitHub nói rõ những chỗ vẫn còn bản sao:
Máy của đồng nghiệp đã clone, các fork của repo. Bạn không xóa hộ được.
Commit cũ vẫn có thể mở được qua mã SHA trong bộ nhớ đệm của GitHub.
Pull request giữ tham chiếu tới commit cũ, phải nhờ GitHub Support gỡ.
Và quan trọng nhất: bản đã bị bot chép đi thì không ai thu hồi được.
Vì vậy GitHub khuyên làm theo thứ tự ngược với phản xạ: thu hồi hoặc đổi khóa trước. Key đã vô hiệu thì lộ cũng không dùng được, nhiều khi không cần viết lại lịch sử nữa.
Làm gì trong 30 phút đầu
Liệt kê mọi thứ trong file đó. Mở .env bản đã commit, ghi ra từng dòng: key OpenAI, Anthropic, Stripe, chuỗi kết nối database, JWT_SECRET, mật khẩu SMTP, token bot Telegram.
Đổi từng cái, bắt đầu từ thứ tốn tiền hoặc mở được dữ liệu. Key AI và key thanh toán trước, mật khẩu database ngay sau. Với JWT_SECRET hay SESSION_SECRET, đổi xong mọi người bị đăng xuất, chấp nhận điều đó.
Xem nhật ký sử dụng của từng dịch vụ quanh thời điểm commit: trang usage của nhà cung cấp AI, request log của Stripe, danh sách kết nối vào database. Thấy dòng lạ thì xử lý như sự cố lộ dữ liệu.
Sau đó mới dọn lịch sử nếu repo public hoặc file có dữ liệu không đổi được (ví dụ thông tin khách hàng).
Đổi khóa mà app không chết
Lý do nhiều người ngại đổi key là sợ app sập giữa chừng. Cách làm chung là cho key cũ và key mới cùng chạy một lúc:
Tạo key mới, đừng xóa key cũ ngay.
Cập nhật biến môi trường trên nền tảng deploy (Vercel, Railway, Render, VPS), deploy lại.
Kiểm tra app chạy được với key mới, xem log lỗi.
Thu hồi key cũ.
Một số dịch vụ làm sẵn việc này. Stripe cho chọn thời gian hết hạn khi xoay key, key cũ và mới cùng dùng được tối đa 7 ngày. Nhưng với key đã lộ, đừng để khoảng chồng chéo dài: vài phút tới vài giờ là đủ, đúng bằng thời gian deploy.
Mật khẩu database thì làm tương tự: tạo user mới hoặc đặt mật khẩu mới, đổi DATABASE_URL, deploy, rồi xóa user cũ hoặc đổi tiếp mật khẩu cũ.
Dọn lịch sử bằng git-filter-repo
GitHub khuyên dùng git-filter-repo (bản 2.47 trở lên). Làm trên một bản clone mới:
Lệnh push sẽ báo lỗi ở các refs/pull/*, đó là bình thường vì GitHub không cho sửa. Sau đó báo mọi người đang có bản clone xóa đi clone lại, và gửi yêu cầu cho GitHub Support nếu có pull request chứa commit cũ. Một lần nữa: bước này chỉ để dọn, không thay được việc đổi khóa.
Quét repo bằng gitleaks
Gitleaks là công cụ mã nguồn mở tìm key, token, mật khẩu trong code và lịch sử Git. Chạy một lần để biết repo đang có gì:
# cài: brew install gitleaksgitleaks git -v . # quét toàn bộ lịch sử commitgitleaks dir -v . # quét thư mục hiện tại, kể cả file chưa commit
Repo cũ chạy lần đầu thường ra vài kết quả. Mỗi kết quả trỏ tới đúng file, dòng, commit. Cái nào là key thật thì đổi, cái nào là key mẫu trong test thì thêm vào file .gitleaksignore.
Chặn trước khi lọt
Ngay trên máy, cài hook chạy trước mỗi commit. Thêm file .pre-commit-config.yaml:
Repo của tổ chức (organization) cần thêm GITLEAKS_LICENSE, lấy miễn phí ở gitleaks.io. Tài khoản cá nhân thì không cần.
GitHub cũng có push protection bật sẵn cho tài khoản cá nhân, chặn việc đẩy một số loại key đã biết lên repo public. Nó hữu ích nhưng chỉ nhận ra các mẫu key phổ biến, không thay được gitleaks.
Cất .env ở đâu
.env luôn nằm trong .gitignore từ commit đầu tiên. Nhờ AI tạo project thì kiểm lại dòng này, đừng tin nó đã làm.
Commit một file .env.example chỉ có tên biến, không có giá trị.
Giá trị thật để trong phần Environment Variables của nền tảng deploy, hoặc trình quản lý bí mật như Doppler, Infisical, 1Password. Bản sao lưu .env cũng cất ở đó, không để trong thư mục project rồi zip gửi qua chat.
Mỗi môi trường một bộ key: key dùng lúc dev trên máy khác key production. Lộ key dev thì thiệt hại nhỏ hơn nhiều.
Kiểm tra app của bạn trong vài phút
Trong thư mục project, chạy git log --all --oneline -- .env .env.local .env.production. Có dòng nào hiện ra là file đó từng được commit.
Chạy gitleaks git -v . và đọc từng kết quả.
Mở .gitignore, chắc chắn có .env* và có ngoại lệ !.env.example nếu bạn commit file mẫu.
Mở trang usage của nhà cung cấp AI, xem biểu đồ 30 ngày có ngày nào tăng vọt bất thường không.
Nếu bước 1 hoặc 2 ra key thật: đổi key đó hôm nay, trước khi làm bất cứ việc gì khác.
Bạn đã từng lỡ đẩy key lên GitHub chưa, và phát hiện ra bằng cách nào? Vào cộng đồng Vibe Code & Bảo Mật kể lại cho mọi người cùng rút kinh nghiệm.
API keyGitHubbí mật/anh/web/lo-commit-file-env-len-github-480.webpAPI key · GitHub · bí mật
Lo commit file .env len GitHub, xoa di van chua xong Xoa file .env, xoa commit hay viet lai lich su deu khong thu hoi duoc key da lo. Viec dau tien la doi khoa, sau do moi don lich su, quet bang gitleaks va chan trong CI de khong lap lai. API key GitHub bi mat Ban day code len GitHub, nam phut sau moi thay file `.env` nam trong commit. Phan xa dau tien cua hau het moi nguoi la `git rm .env`, commit tiep, roi tho phao. Co nguoi can than hon thi `git reset`, `push --force` cho commit do bien mat.
Ca hai cach deu de key con song. Bot quet GitHub cong khai lien tuc, va mot key nam tren repo public vai phut la du de bi chep di. Theo bao cao nam 2025 cua GitGuardian, nam 2024 co 23,8 trieu bi mat moi lo tren GitHub cong khai, va 70% bi mat lo tu nam 2022 toi luc khao sat van dung duoc. Nghia la phan lon nguoi lam lo da khong doi key.

## Vi sao xoa khong du
Git giu lai moi thu. Commit moi xoa `.env` chi lam file bien mat khoi phien ban hien tai, con ai mo lich su ra van thay. Viet lai lich su roi force push thi sach hon, nhung tai lieu cua GitHub noi ro nhung cho van con ban sao:
- May cua dong nghiep da clone, cac fork cua repo. Ban khong xoa ho duoc.
- Commit cu van co the mo duoc qua ma SHA trong bo nho dem cua GitHub.
- Pull request giu tham chieu toi commit cu, phai nho GitHub Support go.
- Va quan trong nhat: ban da bi bot chep di thi khong ai thu hoi duoc.
Vi vay GitHub khuyen lam theo thu tu nguoc voi phan xa: **thu hoi hoac doi khoa truoc**. Key da vo hieu thi lo cung khong dung duoc, nhieu khi khong can viet lai lich su nua.
## Lam gi trong 30 phut dau
1. **Liet ke moi thu trong file do.** Mo `.env` ban da commit, ghi ra tung dong: key OpenAI, Anthropic, Stripe, chuoi ket noi database, `JWT_SECRET`, mat khau SMTP, token bot Telegram.
2. **Doi tung cai, bat dau tu thu ton tien hoac mo duoc du lieu.** Key AI va key thanh toan truoc, mat khau database ngay sau. Voi `JWT_SECRET` hay `SESSION_SECRET`, doi xong moi nguoi bi dang xuat, chap nhan dieu do.
3. **Xem nhat ky su dung cua tung dich vu** quanh thoi diem commit: trang usage cua nha cung cap AI, request log cua Stripe, danh sach ket noi vao database. Thay dong la thi xu ly nhu su co lo du lieu.
4. **Sau do moi don lich su** neu repo public hoac file co du lieu khong doi duoc (vi du thong tin khach hang).
## Doi khoa ma app khong chet
Ly do nhieu nguoi ngai doi key la so app sap giua chung. Cach lam chung la cho key cu va key moi cung chay mot luc:
- Tao key moi, dung xoa key cu ngay.
- Cap nhat bien moi truong tren nen tang deploy (Vercel, Railway, Render, VPS), deploy lai.
- Kiem tra app chay duoc voi key moi, xem log loi.
- Thu hoi key cu.
Mot so dich vu lam san viec nay. Stripe cho chon thoi gian het han khi xoay key, key cu va moi cung dung duoc toi da 7 ngay. Nhung voi key **da lo**, dung de khoang chong cheo dai: vai phut toi vai gio la du, dung bang thoi gian deploy.
Mat khau database thi lam tuong tu: tao user moi hoac dat mat khau moi, doi `DATABASE_URL`, deploy, roi xoa user cu hoac doi tiep mat khau cu.
## Don lich su bang git-filter-repo
GitHub khuyen dung `git-filter-repo` (ban 2.47 tro len). Lam tren mot ban clone moi:
```bash
git clone https://github.com/ban/app.git app-sach
cd app-sach
git filter-repo --sensitive-data-removal --invert-paths --path .env
git push --force --mirror origin
```
Lenh push se bao loi o cac `refs/pull/*`, do la binh thuong vi GitHub khong cho sua. Sau do bao moi nguoi dang co ban clone xoa di clone lai, va gui yeu cau cho GitHub Support neu co pull request chua commit cu. Mot lan nua: buoc nay chi de don, khong thay duoc viec doi khoa.
## Quet repo bang gitleaks
Gitleaks la cong cu ma nguon mo tim key, token, mat khau trong code va lich su Git. Chay mot lan de biet repo dang co gi:
```bash
# cai: brew install gitleaks
gitleaks git -v . # quet toan bo lich su commit
gitleaks dir -v . # quet thu muc hien tai, ke ca file chua commit
```
Repo cu chay lan dau thuong ra vai ket qua. Moi ket qua tro toi dung file, dong, commit. Cai nao la key that thi doi, cai nao la key mau trong test thi them vao file `.gitleaksignore`.
## Chan truoc khi lot
**Ngay tren may**, cai hook chay truoc moi commit. Them file `.pre-commit-config.yaml`:
```yaml
repos:
- repo: https://github.com/gitleaks/gitleaks
rev: v8.24.2
hooks:
- id: gitleaks
```
Roi chay `pre-commit install`. Tu do commit co key se bi chan.
**Trong CI**, them workflow GitHub Actions de bat ca nhung nguoi chua cai hook:
```yaml
# .github/workflows/gitleaks.yml
name: gitleaks
on: [push, pull_request]
jobs:
scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0
- uses: gitleaks/gitleaks-action@v3
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
```
Repo cua to chuc (organization) can them `GITLEAKS_LICENSE`, lay mien phi o gitleaks.io. Tai khoan ca nhan thi khong can.
GitHub cung co push protection bat san cho tai khoan ca nhan, chan viec day mot so loai key da biet len repo public. No huu ich nhung chi nhan ra cac mau key pho bien, khong thay duoc gitleaks.
## Cat .env o dau
- `.env` luon nam trong `.gitignore` tu commit dau tien. Nho AI tao project thi kiem lai dong nay, dung tin no da lam.
- Commit mot file `.env.example` chi co ten bien, khong co gia tri.
- Gia tri that de trong phan Environment Variables cua nen tang deploy, hoac trinh quan ly bi mat nhu Doppler, Infisical, 1Password. Ban sao luu `.env` cung cat o do, khong de trong thu muc project roi zip gui qua chat.
- Moi moi truong mot bo key: key dung luc dev tren may khac key production. Lo key dev thi thiet hai nho hon nhieu.
## Kiem tra app cua ban trong vai phut
1. Trong thu muc project, chay `git log --all --oneline -- .env .env.local .env.production`. Co dong nao hien ra la file do tung duoc commit.
2. Chay `gitleaks git -v .` va doc tung ket qua.
3. Mo `.gitignore`, chac chan co `.env*` va co ngoai le `!.env.example` neu ban commit file mau.
4. Mo trang usage cua nha cung cap AI, xem bieu do 30 ngay co ngay nao tang vot bat thuong khong.
5. Neu buoc 1 hoac 2 ra key that: doi key do hom nay, truoc khi lam bat cu viec gi khac.
---
Ban da tung lo day key len GitHub chua, va phat hien ra bang cach nao? Vao [cong dong Vibe Code & Bao Mat](https://www.facebook.com/groups/1867387324580254) ke lai cho moi nguoi cung rut kinh nghiem.
## Nguon
- [Removing sensitive data from a repository](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/removing-sensitive-data-from-a-repository), GitHub Docs
- [About push protection](https://docs.github.com/en/code-security/secret-scanning/introduction/about-push-protection), GitHub Docs
- [gitleaks](https://github.com/gitleaks/gitleaks) va [gitleaks-action](https://github.com/gitleaks/gitleaks-action), GitHub
- [The State of Secrets Sprawl 2025](https://blog.gitguardian.com/the-state-of-secrets-sprawl-2025/), GitGuardian
- [API keys: rotate an API key](https://docs.stripe.com/keys), Stripe Docs