Giải thích cho người mới · 10/2026

Bí mật nằm
trong code

Xóa khóa API khỏi tệp chưa phải là xong, vì git vẫn nhớ. Thứ tự đúng khi lộ khóa và cách chặn từ đầu.

Ví dụ đời thường Chạy thật, khóa giả Xoay khóa 15 phút Danh sách kiểm
AKIAIOSFODNN7EXAMPLE Đã xóa. Xong chưa?
Câu hỏi của bài này

Xóa khóa khỏi tệp, git vẫn nhớ

1Khóa đã commit vẫn nằm trong lịch sử. Ai sao chép kho trước đó vẫn giữ bản cũ.
2Khi lộ khóa: xoay trước, dọn lịch sử sau. Dọn lịch sử không thu hồi được bản sao.
3Ba lớp chặn, mỗi lớp có chỗ sót. Mọi khóa trong bài đều là khóa giả.
Hình dung trước

Cất chìa không xóa được bản sao

Xóa dòng ở commit sau

Cất chìa vào ngăn kéo

Hàng xóm đã đúc bản sao trong ngày chìa còn dưới thảm. Bản sao vẫn ở ngoài.

Xoay khóa

Đổi ổ khóa

Chìa cũ, kể cả bản sao, không còn mở được gì. Đây là cách chắc chắn duy nhất.

Khóa đã ra khỏi chỗ cất: coi như đã lộ, và đổi ổ khóa.
Từ khó, nói dễ

Sáu từ sẽ gặp trong bài

Bí mật

tên kỹ thuật: secret

Thứ ai cầm được là dùng được: khóa API, mật khẩu, mã truy cập. Như chìa khóa nhà.

Lịch sử

tên kỹ thuật: history

Mọi commit từ trước đến nay. Xóa dòng ở commit mới không xóa ở commit cũ.

Xoay khóa

tên kỹ thuật: rotate

Tạo khóa mới, chuyển nơi dùng sang, rồi vô hiệu khóa cũ. Như đổi ổ khóa.

Thu hồi

tên kỹ thuật: revoke

Vô hiệu hóa khóa ngay để nó không còn mở được gì.

Biến môi trường

tên kỹ thuật: environment variable

Giá trị đặt ngoài code, chương trình đọc lúc chạy. Như chìa khóa để ở hộp riêng.

Pre-commit hook

tên kỹ thuật: pre-commit hook

Đoạn lệnh git tự chạy ngay trước khi lưu commit, có thể từ chối lần lưu đó.

Lộ ở đâu

Bí mật rò ra qua nhiều cửa

1Commit thẳng vào gitRồi xóa ở commit sau, lịch sử vẫn giữ
2Tệp .env bị commitQuên ghi vào .gitignore, hoặc commit từ trước
3Nhật ký và ảnh chụp màn hìnhKhóa lọt vào rồi gửi cho đồng nghiệp
4Dán vào cuộc trò chuyện với AINội dung có thể nằm lại trong lịch sử trò chuyện
5Mã chạy trong trình duyệtMọi thứ gửi xuống trình duyệt đều đọc được
Thí nghiệm thật · khóa giả

Commit khóa giả, rồi xóa ở commit sau

# commit 1: config.py có khóa giả
AWS_ACCESS_KEY_ID = "AKIAIOSFODNN7EXAMPLE"
# commit 2: đọc từ biến môi trường
AWS_ACCESS_KEY_ID = os.environ["AWS_ACCESS_KEY_ID"]

$ git log --oneline
2bfea15 remove secret from config
a0d01a2 add config

Tệp hiện tại sạch: grep và git grep không in ra gì.

Cặp khóa ví dụ có sẵn trong tài liệu AWS. Kho thử, Git 2.56.0, chạy thật.

Thí nghiệm thật · bước hai

Nhưng lịch sử vẫn còn

$ git log --oneline -S AKIAIOSFODNN7EXAMPLE
2bfea15 remove secret from config
a0d01a2 add config

$ git show a0d01a2:config.py
AWS_ACCESS_KEY_ID = "AKIAIOSFODNN7EXAMPLE"
AWS_SECRET_ACCESS_KEY = "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"

Ba lệnh, dưới một giây: ai có kho này cũng đọc lại được cả hai khóa.

Tệp sạch không có nghĩa kho sạch. Phải tìm trong lịch sử.

Thứ tự đúng

Xoay trước, dọn sau

1
Xoay hoặc thu hồiNgay lập tức, trên dịch vụ
2
Đọc nhật kýAi dùng khóa lúc nó lộ
3
Dọn lịch sửNếu cần, và báo đồng đội
4
Ghi lại và chặnĐặt rào để lần sau không lặp lại

Tài liệu secret scanning của GitHub cũng khuyên xoay ngay, và nói xóa khỏi lịch sử là chậm, thường không cần khi khóa đã thu hồi.

Quy trình xoay khóa AWS

Tạo khóa thứ hai trước khi bỏ khóa cũ

1

Tạo khóa thứ hai

Khóa cũ còn hoạt động, lúc này có hai khóa.

2

Cập nhật nơi dùng

Mọi ứng dụng và công cụ sang khóa mới.

3

Xem last used

Biết khóa cũ còn ai dùng không.

4

Vô hiệu, chưa xóa

Đổi khóa cũ sang Inactive trước.

5

Kiểm bằng khóa mới

Nơi nào còn dùng khóa cũ sẽ hỏng.

6

Đợi rồi mới xóa

Sau một thời gian mới xóa khóa cũ.

Mục tiêu đề xuất

15 phút đủ để đóng cửa

PhútViệcTrong 15 phút?
0 đến 2Vô hiệu hóa khóa bị lộĐược
2 đến 5Tạo khóa mới, lưu vào nơi cấtĐược
5 đến 10Cập nhật các nơi dùngNếu ít nơi
10 đến 15Kiểm ứng dụng, báo người liên quanĐược
Sau đóĐọc nhật ký, dọn lịch sử, xoay khóa liên quanKhông

Mốc mục tiêu do tác giả đề xuất, không phải số đo. Thời gian thật tùy quyền truy cập và việc bạn biết khóa dùng ở đâu.

.gitignore

Chặn tệp mới, không chặn tệp đã commit

$ git status --short
?? dev.env

$ echo dev.env > .gitignore
$ git status --short
(dev.env biến mất)

$ git ls-files old.env
old.env # đã commit từ trước

Ghi vào .gitignore trước khi tạo tệp. Tệp đã được theo dõi thì git vẫn theo dõi.

Muốn ngừng theo dõi: git rm --cached. Khóa trong lịch sử vẫn còn.

Lớp hai · chặn lúc commit

Hook chặn được, nhưng có chỗ sót

Blocked: this commit adds
something that looks like a secret.
deploy.py: key or password
assigned to a string
exit code: 1

Bộ quét 30 dòng Python, cả lần thử mất khoảng 0,14 giây.

Bỏ sót

Khóa tách làm hai chuỗi rồi nối lại: commit vẫn qua (mã 0).

Báo nhầm

Dòng password = "change-me-please" chỉ là chữ giữ chỗ nhưng bị chặn.

Dọn lịch sử

Viết lại lịch sử: kho sạch, bản sao còn

Sạch

Nhánh main sau khi viết lại bằng filter-branch (khoảng 0,8 giây, 5 commit).

Còn khóa

Nhánh sao lưu refs/original và reflog: phải xóa rồi chạy gc mới sạch.

Vẫn còn khóa

Bản sao chụp trước khi viết lại: git log -S vẫn thấy cả hai commit.

Dọn lịch sử không thay được việc xoay khóa.
Với AI agent

Sáu thói quen khi làm việc với agent

1Không dán khóa thật vào cuộc trò chuyện
2Giữ tệp .env ngoài tầm với của agent
3Dùng khóa quyền hẹp, hạn ngắn
4Đọc diff trước khi commit
5Không cho agent tự đẩy lên nhánh chính
6Đưa quy tắc vào chính sách nhóm
Minh họa, không có thật

Minh họa: nhờ agent nối cơ sở dữ liệu

Không có thói quen
10:00 · DevDán chuỗi kết nối thật vào cuộc trò chuyện.
10:10 · AgentGhi thẳng chuỗi vào config.py rồi commit.
10:45 · Nền tảngCảnh báo bí mật. Dev xóa dòng, nghĩ là xong.
Có thói quen
10:00 · DevĐặt DATABASE_URL trong biến môi trường, dặn agent đọc từ đó.
10:10 · AgentViết config.py đọc biến môi trường. Hook không thấy gì lạ.
10:20 · DevĐọc diff, chỉ thấy tên biến, rồi đẩy lên.
Ở phiên bản đầu, lúc 10:45 xoay mật khẩu ngay, rồi mới tính chuyện dọn.
Trung thực về bằng chứng

Đã chạy thật và chưa kiểm chứng

Đã chạy thật
  • Xóa khóa rồi vẫn đọc lại được
  • Bộ quét 30 dòng bỏ sót và báo nhầm
  • Bản sao trước khi viết lại còn khóa
Chưa chạy hoặc chưa kiểm
  • git-filter-repo, gitleaks, trufflehog: chưa chạy ở đây
  • Số 41% của VibeWrench: không xác nhận được, không dùng
  • Trang GitHub và AWS đọc qua công cụ tóm tắt
Danh sách kiểm

Tám câu hỏi trước khi đẩy code

1Có bí mật thật nào trong thay đổi này không?
2Bí mật ở biến môi trường, không ở trong code?
3Đã ghi .env vào .gitignore trước khi tạo?
4Tôi đã đọc diff, tìm chuỗi dài lạ chưa?
5Có hook cục bộ và quét phía nền tảng chưa?
6Khóa tôi dùng quyền hẹp và hạn ngắn không?
7Tôi từng dán khóa thật cho AI chưa? Nếu có, xoay.
8Nếu lộ lúc này, tôi biết xoay ở đâu và báo ai?
Kết luận

Khóa đã vào git thì đổi ổ khóa, đừng chỉ cất chìa.

Xoay khóa trước, đọc nhật ký, rồi mới dọn lịch sử. Giữ bí mật ngoài code, chặn lúc commit, và luôn đọc diff.