Có một hiểu nhầm khá phổ biến: “Đã dùng Git thì chắc cũng kiểm soát được chuyện lộ secret trong source code”. Thực tế thì không.
Git chỉ làm đúng việc của nó: lưu lịch sử thay đổi mã nguồn. Nếu developer lỡ commit AWS Access Key, password database, token nội bộ hoặc private key vào repository, Git vẫn ghi nhận bình thường. Nó không biết đâu là code hợp lệ, đâu là secret. Một khi secret đã vào Git history, việc xử lý không còn đơn giản là xóa dòng đó đi rồi commit lại, việc xử lý về sau rất là cồng kềnh.
Đây là lý do các công cụ như git-secrets ra đời. git-secrets không thay thế Git, mà bổ sung một lớp kiểm tra trước khi commit. Nó dùng Git hooks để scan nội dung commit, commit message và một số tình huống merge nhằm phát hiện chuỗi khớp với pattern bị cấm. Với AWS, công cụ này có sẵn rule set để phát hiện các dạng AWS credential phổ biến thông qua lệnh git secrets –register-aws.
Mọi người có thể tìm git-secrets của AWS Lab tại: https://github.com/awslabs/git-secrets
Bài viết này không đi theo hướng giới thiệu lý thuyết dài dòng. Thay vào đó, tôi thử đặt Git thường và git-secrets vào cùng một bộ tình huống kiểm thử để xem khác biệt nằm ở đâu.
I. Mục tiêu so sánh
Mục tiêu không phải chứng minh git-secrets là công cụ hoàn hảo. Mục tiêu là trả lời ba câu hỏi thực tế hơn:
- Git thường có tự ngăn developer commit secret không?
- git-secrets chặn được những loại secret nào?
- Những tình huống nào git-secrets không xử lý tốt nếu chỉ dùng cấu hình mặc định?
Nói ngắn gọn: Git là hệ thống quản lý phiên bản. git-secrets là một lớp kiểm soát trước commit.
II. Cách hoạt động ngắn gọn của git-secrets
git-secrets là một công cụ shell script do AWS Labs phát triển, mục tiêu là ngăn user vô tình commit các thông tin nhạy cảm (AWS access keys, mật khẩu, API tokens, v.v.) vào repository Git. Nó hoạt động dựa trên ba trụ cột chính: Git hooks, regex patterns lưu trong git config, và secret providers.
Tận dụng Git hooks để chặn từ gốc
Khi ta chạy git secrets –install trong một repo, công cụ này cài 3 hook vào thư mục .git/hooks/:
- pre-commit: Trước khi commit được tạo, hook quét nội dung diff của các file sắp commit. Nếu khớp pattern bị cấm → commit bị từ chối, trả về exit code khác 0.
- commit-msg: Quét nội dung message của commit. Đôi khi dev hay paste token vào message để ghi chú — hook này chặn việc đó.
- prepare-commit-msg: Chỉ kích hoạt khi merge non-fast-forward (
--no-ff). Nó kiểm tra toàn bộ lịch sử commit sắp được merge vào, để nếu nhánh nguồn chứa secret thì merge cũng bị chặn.
Vì Git mỗi hook chỉ chạy được một script, nếu repo của tôi dùng kiểu Debian (pre-commit.d/, commit-msg.d/) thì git-secrets sẽ cài vào đó để cùng tồn tại với các hook khác.
Cơ chế quét bằng regex (egrep-compatible)
Đây là phần “trí tuệ” của công cụ. Các pattern được lưu vào git config dưới hai khóa:
- secrets.patterns — các pattern bị cấm (prohibited)
- secrets.allowed — các pattern được phép (whitelist, dùng để loại false positives)
Khi quét, git-secrets dùng egrep để tìm các dòng khớp với pattern cấm. Mỗi dòng khớp được xuất ra theo định dạng:
<đường-dẫn-file>:<số-dòng>:<nội-dung-dòng>
Sau đó công cụ áp dụng logic loại trừ hai bước:
- Lấy tất cả dòng khớp với pattern cấm.
- Với mỗi dòng đó, kiểm tra xem có khớp với ít nhất một allowed pattern không.
- Nếu mọi dòng “đáng ngờ” đều bị một allowed pattern loại trừ → an toàn, cho qua. Nếu còn bất kỳ dòng nào không bị loại trừ → fail, chặn commit.
Một chi tiết tinh tế: vì subject text chứa cả tên file và số dòng (ví dụ /tmp/example:3:...), ta có thể viết allowed pattern dạng /tmp/example:.* để whitelist cả file, hoặc /tmp/example:3:.* để whitelist một dòng cụ thể. A/e cũng có thể đặt các allowed pattern vào file .gitallowed ở root của repo.
Các chế độ quét thủ công
Ngoài chạy tự động qua hook, ta có thể quét chủ động:
git secrets --scan [files...]— quét file hoặc working tree.git secrets --scan -r <dir>— quét đệ quy.git secrets --scan --cached— quét blob trong index.git secrets --scan-history— quét toàn bộ lịch sử mọi revision, dùng trước khi public hóa một repo private (vì secret có thể nằm ở commit cũ dù a/e đã xóa ở HEAD).
Một điểm cần lưu ý
Hook chỉ chạy ở máy của developer. Nếu ai đó chưa cài hook, hoặc dùng git commit –no-verify, công cụ này không thể chặn được. Vì vậy git-secrets nên được coi là lớp phòng thủ thứ nhất ở client-side, kết hợp với các giải pháp server-side như GitHub Push Protection, Gitleaks chạy trong CI/CD, hoặc secret scanning của hosting provider.
Tóm lại, kiến trúc của git-secrets khá đơn giản nhưng hiệu quả: Git hooks làm điểm chặn, regex patterns trong git config làm “định nghĩa secret”, allowed patterns xử lý false positives, và secret providers cho phép mở rộng động — toàn bộ logic gói gọn trong một shell script ~700 dòng.
III. Môi trường kiểm thử giả định
Để so sánh công bằng, tôi giả định có hai repository giống nhau:
repo-normal-gitrepo-with-git-secrets
Trong đó:
| Repository | Cấu hình |
|---|---|
repo-normal-git | Git mặc định, không có hook kiểm tra secret |
repo-with-git-secrets | Đã chạy git secrets --install và git secrets --register-aws |
Với từng test case, thao tác kiểm thử là:
- Tạo file hoặc sửa file.
git add.git commit.- Quan sát kết quả.
4. Bộ 20 test case thực tế
Test case 01: Commit AWS Access Key ID trong file .env
Tình huống
Developer tạo file .env để test local, trong đó có AWS Access Key ID.
Kỳ vọng với Git thường
Git commit bình thường. Git không có cơ chế hiểu đây là access key.
Kỳ vọng với git-secrets
Commit bị chặn nếu chuỗi khớp với AWS pattern đã đăng ký.
Nhận xét
Đây là tình huống cơ bản nhất và cũng là lý do nhiều team bắt đầu dùng git-secrets: chặn credential ngay từ máy developer.
Test case 02: Commit AWS Secret Access Key trong file cấu hình
Tình huống
Một file config chứa AWS Secret Access Key dùng cho môi trường dev.
Git thường
Commit thành công.
Git + git-secrets
Có khả năng bị chặn nếu secret đi kèm ngữ cảnh hoặc pattern mà rule set nhận diện được.
Nhận xét
Access Key ID thường dễ nhận diện hơn vì có prefix đặc trưng. Secret Access Key khó hơn vì bản chất nó giống một chuỗi random dài. Đây cũng là nơi false positive hoặc false negative có thể xuất hiện.
Test case 03: Commit cả Access Key ID và Secret Access Key trong cùng file
Tình huống
File .env chứa đầy đủ cặp credential.
Git thường
Commit thành công.
Git + git-secrets
Commit bị chặn.
Nhận xét
Nếu cả hai giá trị xuất hiện cùng nhau, rủi ro nghiêm trọng hơn nhiều vì attacker có đủ thông tin để thử sử dụng credential. git-secrets phù hợp để chặn sớm tình huống này.
Test case 04: Commit AWS credential trong source code
Tình huống
Developer hard-code credential trong Python, Java, Node.js hoặc shell script.
Ví dụ logic:
client = create_aws_client(access_key, secret_key)
Git thường
Commit thành công.
Git + git-secrets
Commit bị chặn nếu credential match pattern.
Nhận xét
Git không phân biệt secret nằm trong .env, source code, YAML hay README. Với git-secrets, miễn là nội dung staged diff match pattern thì có thể bị chặn.
Test case 05: Commit secret trong comment
Tình huống
Developer comment lại credential cũ để “test tạm”.
Git thường
Commit thành công.
Git + git-secrets
Commit bị chặn nếu chuỗi trong comment match pattern.
Nhận xét
Đây là điểm hay: git-secrets không quan tâm secret nằm trong code chạy thật hay comment. Cứ match là chặn.
Test case 06: Commit secret trong README hoặc tài liệu hướng dẫn
Tình huống
Tài liệu nội bộ hướng dẫn cấu hình AWS, vô tình copy nhầm credential thật.
Git thường
Commit thành công.
Git + git-secrets
Commit bị chặn.
Nhận xét
Nhiều secret leak không nằm trong code production mà nằm trong README, wiki export, script hướng dẫn hoặc file note. Test case này rất thực tế.
Test case 07: Commit key giả nhưng đúng format
Tình huống
Developer viết tài liệu training và tạo một chuỗi giả nhưng có format giống AWS key.
Git thường
Commit thành công.
Git + git-secrets
Có thể bị chặn.
Nhận xét
Đây là false positive có chủ đích. Với git-secrets, nếu chuỗi giống secret, công cụ ưu tiên chặn. Trường hợp này cần dùng allowed pattern hoặc thay bằng placeholder an toàn hơn.
Ví dụ nên viết:
AWS_ACCESS_KEY_ID=<REPLACE_WITH_YOUR_ACCESS_KEY>AWS_SECRET_ACCESS_KEY=<REPLACE_WITH_YOUR_SECRET_KEY>
Không nên viết ví dụ có hình dạng giống credential thật.
Test case 08: Commit file .env.example
Tình huống
Team muốn commit file .env.example để hướng dẫn biến môi trường.
Git thường
Commit thành công.
Git + git-secrets
Nếu chỉ có placeholder thì commit thành công. Nếu placeholder giống secret thật thì có thể bị chặn.
Nhận xét
Best practice là dùng placeholder rõ ràng, không dùng chuỗi random “cho giống thật”.
Test case 09: Commit secret trong file YAML Kubernetes
Tình huống
Một manifest Kubernetes chứa secret trực tiếp trong YAML.
Git thường
Commit thành công.
Git + git-secrets
Bị chặn nếu giá trị match pattern đã cấu hình.
Nhận xét
git-secrets không thay thế Kubernetes Secret management. Nó chỉ giúp phát hiện một phần rủi ro trước khi nội dung đi vào Git history.
Test case 10: Commit private key PEM
Tình huống
Developer commit nhầm private key dạng PEM.
Git thường
Commit thành công.
Git + git-secrets
Mặc định AWS rule set có thể không bao phủ đầy đủ mọi loại private key nếu chưa cấu hình custom pattern.
Nhận xét
Đây là test case quan trọng. Nếu team chỉ chạy --register-aws, không nên kỳ vọng công cụ sẽ bắt toàn bộ secret ngoài AWS. Muốn chặn private key, token nội bộ, GitHub token, Slack token, database password… cần bổ sung pattern riêng hoặc dùng thêm secret scanning tool khác.
Test case 11: Commit database password trong file config
Tình huống
File application.yml có password database.
Git thường
Commit thành công.
Git + git-secrets
Chỉ bị chặn nếu password khớp với prohibited pattern đã cấu hình.
Nhận xét
Đây là giới hạn rất thực tế. Một password như Welcome@123 hoặc P@ssw0rd2026 có thể không bị phát hiện nếu không có rule phù hợp. git-secrets không hiểu ngữ nghĩa “đây là password database”, trừ khi pattern đủ tốt.
Test case 12: Commit API key nội bộ của công ty
Tình huống
Công ty có API key dạng riêng, ví dụ prefix theo tên hệ thống nội bộ.
Git thường
Commit thành công.
Git + git-secrets
Mặc định không chặn. Sau khi thêm custom pattern thì chặn được.
Nhận xét
Đây là nơi git-secrets hữu ích trong môi trường doanh nghiệp. Team có thể thêm pattern riêng bằng git secrets --add. README của git-secrets hỗ trợ cơ chế prohibited pattern và allowed pattern để tùy biến rule.
Test case 13: Commit secret trong commit message
Tình huống
Developer không đưa secret vào file, nhưng ghi trong commit message:
fix: rotate leaked key xxxxx
Git thường
Commit thành công.
Git + git-secrets
Có thể bị chặn thông qua commit-msg hook nếu nội dung message match prohibited pattern.
Nhận xét
Nhiều người chỉ nghĩ đến file content, nhưng commit message cũng là một phần Git history. git-secrets có hook riêng để kiểm tra commit message.
Test case 14: Commit secret sau khi đổi tên file
Tình huống
File chứa secret được rename từ config.old sang config.yml.
Git thường
Commit thành công.
Git + git-secrets
Nếu nội dung nằm trong staged change và match pattern, commit bị chặn.
Nhận xét
Đổi tên file không làm secret an toàn hơn. Vấn đề là nội dung có đi vào commit hay không.
Test case 15: Commit secret đã base64 encode
Tình huống
Developer base64 encode secret rồi đưa vào file.
Git thường
Commit thành công.
Git + git-secrets
Có thể không chặn nếu chuỗi encode không match prohibited pattern.
Nhận xét
Đây là một giới hạn lớn của pattern-based scanning. Base64 không phải mã hóa bảo mật. Nhưng với scanner đơn giản, nó có thể làm mất dấu hiệu nhận diện ban đầu.
Test case 16: Commit secret bị tách thành nhiều phần
Tình huống
Developer tách secret thành nhiều chuỗi nhỏ rồi nối lại trong code.
Git thường
Commit thành công.
Git + git-secrets
Có thể không chặn nếu từng phần riêng lẻ không match pattern.
Nhận xét
Nếu attacker hoặc developer cố tình né rule, git-secrets không phải sandbox hay DLP engine. Nó là lớp phòng vệ sớm, không phải giải pháp chống bypass tuyệt đối.
Test case 17: Commit secret nằm trong file binary
Tình huống
Secret nằm trong file .zip, .jar, .png, .sqlite hoặc binary artifact.
Git thường
Commit thành công.
Git + git-secrets
Có thể không phát hiện tốt nếu nội dung không được scan dưới dạng text có thể đọc được.
Nhận xét
Không nên commit binary artifact không cần thiết vào Git. Với secret trong binary, cần thêm kiểm soát ở CI/CD hoặc artifact scanning.
Test case 18: Secret đã tồn tại trong Git history trước khi cài git-secrets
Tình huống
Repository đã từng commit secret từ vài tháng trước. Sau đó team mới cài git-secrets.
Git thường
Không cảnh báo gì.
Git + git-secrets
Commit mới có thể được chặn, nhưng secret cũ vẫn nằm trong history nếu không scan history và xử lý.
Nhận xét
git-secrets có lệnh scan history, nhưng việc phát hiện secret cũ chỉ là bước đầu. Nếu secret thật đã leak vào history, cần rotate credential và làm sạch history nếu cần. README có nhắc tới khả năng scan history thông qua --scan-history.
Test case 19: Developer clone repository nhưng chưa cài hook
Tình huống
Một developer mới clone repository nhưng chưa chạy git secrets --install.
Git thường
Commit thành công.
Git + git-secrets
Thực tế là không có bảo vệ local nếu hook chưa được cài.
Nhận xét
Đây là điểm yếu vận hành. Git hooks trong .git/hooks không tự động đi theo repository khi clone. Vì vậy không nên chỉ dựa vào máy developer. Nên kết hợp thêm CI scan hoặc pre-receive hook ở phía server nếu nền tảng hỗ trợ.
Test case 20: CI/CD chạy secret scan trước khi merge
Tình huống
Pipeline chạy git secrets --scan trước khi cho merge vào main branch.
Git thường
Không có bước scan native.
Git + git-secrets
Có thể phát hiện secret trong pipeline nếu cấu hình job đúng.
Nhận xét
Đây là cách triển khai thực tế hơn: dùng local hook để chặn sớm, dùng CI để kiểm soát lại. Local hook giúp developer phát hiện nhanh. CI giúp giảm phụ thuộc vào việc từng người có cài hook đúng hay không.
5. Bảng tổng hợp kết quả
| # | Tình huống | Git thường | Git + git-secrets |
|---|---|---|---|
| 1 | AWS Access Key ID trong .env | Không chặn | Chặn |
| 2 | AWS Secret Access Key | Không chặn | Có thể chặn |
| 3 | Cặp AWS credential đầy đủ | Không chặn | Chặn |
| 4 | Secret hard-code trong source code | Không chặn | Chặn nếu match |
| 5 | Secret trong comment | Không chặn | Chặn nếu match |
| 6 | Secret trong README | Không chặn | Chặn nếu match |
| 7 | Key giả đúng format | Không chặn | Có thể chặn nhầm |
| 8 | .env.example dùng placeholder | Không chặn | Không chặn nếu placeholder an toàn |
| 9 | Secret trong Kubernetes YAML | Không chặn | Chặn nếu match |
| 10 | Private key PEM | Không chặn | Cần custom rule |
| 11 | Database password | Không chặn | Cần custom rule |
| 12 | API key nội bộ | Không chặn | Cần custom rule |
| 13 | Secret trong commit message | Không chặn | Có thể chặn |
| 14 | Secret trong file rename | Không chặn | Chặn nếu nằm trong staged diff |
| 15 | Secret base64 | Không chặn | Có thể không chặn |
| 16 | Secret bị split nhiều phần | Không chặn | Có thể không chặn |
| 17 | Secret trong binary | Không chặn | Hạn chế |
| 18 | Secret cũ trong history | Không cảnh báo | Cần scan history |
| 19 | Dev chưa cài hook | Không chặn | Không chặn |
| 20 | CI secret scan | Không có native | Có thể tích hợp |
6. Kết luận
Git không được thiết kế để phân loại dữ liệu nhạy cảm. Với Git, một AWS key, một password database hay một dòng log đều chỉ là text. Nếu đã git add và git commit, Git sẽ lưu lại.
Vì vậy, nói “Git để lộ secret” cũng không hoàn toàn đúng. Chính xác hơn là: Git không có cơ chế mặc định để ngăn secret đi vào lịch sử mã nguồn.
-> git-secrets tốt nhất ở vai trò chặn sớm
Giá trị lớn nhất của git-secrets là chặn lỗi trước khi commit được tạo ra. Đây là điểm khác biệt lớn so với việc phát hiện secret sau khi đã push lên remote.
Khi secret đã lên remote repository, ta thường phải làm nhiều việc hơn:
- rotate credential;
- kiểm tra log truy cập;
- đánh giá khả năng bị sử dụng trái phép;
- xóa hoặc rewrite Git history nếu cần;
- thông báo incident nếu dữ liệu nhạy cảm bị ảnh hưởng.
Chặn trước commit rẻ hơn rất nhiều so với xử lý sau leak.
->Nhưng git-secrets không phải “secret detection platform”
git-secrets mạnh với pattern rõ ràng, đặc biệt là AWS credential. Nhưng nó không thể thay thế toàn bộ các lớp kiểm soát khác.
Một số giới hạn dễ thấy:
- Không tự hiểu mọi loại secret.
- Có thể false positive với key giả.
- Có thể false negative với secret bị encode, split hoặc format lạ.
- Phụ thuộc vào việc hook có được cài trên máy developer hay không.
- Không tự xử lý secret đã nằm trong Git history.
- Không thay thế secret manager như AWS Secrets Manager, HashiCorp Vault, GCP Secret Manager.
Nói cách khác, git-secrets là một guardrail tốt, nhưng không phải toàn bộ chiến lược quản lý secret.
VII. Khuyến nghị triển khai thực tế
Nếu áp dụng trong team, tôi sẽ không dừng ở việc bảo developer tự cài git-secrets. Cách triển khai nên có ít nhất bốn lớp.
Lớp 1: Local hook cho developer
Cài git-secrets ở local để chặn lỗi sớm:
git secrets --installgit secrets --register-aws
Với repository mới, nên đưa bước này vào onboarding document hoặc script bootstrap.
Lớp 2: Custom pattern cho secret nội bộ
AWS rule set chỉ giải quyết một phần bài toán. Mỗi công ty thường có format token riêng, ví dụ:
- internal API key;
- service token;
- webhook secret;
- database connection string;
- signing key;
- third-party integration token.
Các pattern này nên được chuẩn hóa và đưa vào rule chung.
Lớp 3: CI/CD secret scanning
Không thể tin rằng tất cả developer đều cài hook đúng. Vì vậy pipeline cần có bước scan trước khi merge.
Ví dụ flow hợp lý:
Developer commit→ Local git-secrets hook→ Push branch→ CI secret scan→ Pull request review→ Merge
Local hook giúp phản hồi nhanh. CI giúp kiểm soát tập trung.
Lớp 4: Quy trình xử lý khi secret đã leak
Nếu phát hiện secret trong Git history, đừng chỉ xóa file rồi commit lại. Cần xử lý theo hướng incident response:
- Xác định secret là thật hay giả.
- Rotate hoặc revoke secret.
- Kiểm tra log sử dụng.
- Đánh giá phạm vi ảnh hưởng.
- Làm sạch Git history nếu cần.
- Bổ sung rule để tránh lặp lại.
Điểm quan trọng nhất là: secret đã commit thì phải giả định là đã lộ, đặc biệt nếu repository đã từng được push ra remote hoặc có nhiều người có quyền đọc.
8. Kết luận
Sau 20 test case, kết luận khá rõ:
Git thường không bảo vệ secret. Git chỉ lưu lịch sử thay đổi.
AWS git-secrets giúp chặn một nhóm rủi ro rất cụ thể: developer vô tình commit secret có pattern nhận diện được, đặc biệt là AWS credential.
Nhưng nếu dùng git-secrets như một giải pháp duy nhất thì chưa đủ. Nó nên được xem là một lớp trong chiến lược DevSecOps:
Local hook+ CI secret scanning+ custom pattern+ secret manager+ credential rotation+ incident response
Nếu team đang dùng AWS, git-secrets là một lựa chọn nhẹ, dễ triển khai và đáng dùng. Nhưng cách dùng đúng không phải là “cài cho có”. Cách dùng đúng là biến nó thành một guardrail trong toàn bộ vòng đời phát triển phần mềm.