This document provides detailed information about the security model, considerations, and implications of using git-conceal.
The encryption key is stored in plaintext in .git/git-conceal.key.
Like other files in .git/ subfolders, this file is not part of your working copy files so not at risk of being pushed to the remote repository.
The file is automatically created with secure permissions to prevent other users on your computer from accessing it:
- Unix systems: Mode 0600 (read/write for owner only)
- Windows: ACL restricted to the current user only
On Unix systems, you can verify permissions with:
ls -l .git/git-conceal.keyIf permissions are incorrect, fix them with:
chmod 600 .git/git-conceal.keyShare keys securely with collaborators (e.g., via encrypted channels, password managers, etc.). Never commit the key file to the repository.
Always backup your encryption keys. If you lose the key, encrypted files cannot be recovered.
Make sure your .gitattributes patterns are correct before adding sensitive files, and that you didn't make any typo in the name of your filter=git-conceal attribute, or those files won't be encrypted!
The filters are applied at git add time, so files must be listed in .gitattributes before being staged.
You can check if a file that you added via git add will be encrypted once pushed to the remote by using:
git-conceal status <filename>If you accidentally git add-ed a secret file before having the right filter for it in .gitattributes, you can git restore --staged <file> and git add <file> it again afterwards, or use git add --renormalize <file>.
git-conceal uses AES-256-CTR (Advanced Encryption Standard with 256-bit keys in Counter Mode) as the algorithm to encrypt the data.
The AES algorithm requires an Initialization Vector (IV) to provide initial state. We are using the first 16 bytes of the SHA-256 of the plaintext as the IV. This allows us to have deterministic encryption required for Git to work properly with those files (see Deterministic encryption: Security implications)
To validate the integrity of the encrypted data, git-conceal uses HMAC (Hash-based Message Authentication Code) over the entire ciphertext (header + version + IV + encrypted data) as a signature.
This allows the tool to validate that the data hasn't been tampered with, as well as detecting when a user might try to decrypt the encrypted data with the wrong key (resulting in authentication failure with proper error message, instead of silently generating garbage output as "decrypted" data due to incorrect key).
The cryptographic key used for the HMAC signature is derived from the encryption key, using HKDF-SHA256 (HMAC-based Key Derivation Function with SHA-256). This ensures that the encryption key and HMAC key are cryptographically separated (good security practice).
The final binary data generated by git-conceal during encryption (i.e. what is stored in the blob of the Git object database) has the following structure:
[Magic Number][Version][IV][Encrypted data][HMAC]
- Magic Number (9 bytes): encrypted blobs always start with
\0a8ccrypt(null byte followed by ASCII stringa8ccrypt) as its magic number. This allows to detect if a blob is encrypted data, as opposed to arbitrary plaintext. - Version (1 byte): a single byte indicating the version of the encrypted data format. Currently it will always be 1 (hex value
0x01), but this byte makes the tool ready to support potential future changes in the format if needed one day. - IV (16 bytes): The Initialization Vector that was used as a seed for the AES algorithm during encryption—and that we'll need to seed the decryption.
- Encrypted data: the actual encrypted data (i.e. the result of encrypting the plaintext data with AES)
- HMAC (32 bytes): the HMAC signature of the whole { Magic Number + Version + IV + Encrypted data } bytes. Used to validate integrity.
git-conceal uses deterministic encryption (same plaintext → same ciphertext), meaning the same file content will always produce the same encrypted output. This is necessary for Git's content-addressable storage to work efficiently.
This design choice has security implications worth being aware of, even though that shouldn't cause any concerns in the context in which git-conceal is used in practice.
-
Content equality detection: An attacker who can observe the encrypted files in the repository can determine if two files have identical content, or if a file's content hasn't changed between commits, even without the encryption key.
-
Pattern analysis: An attacker can identify files that are frequently updated vs. files that remain static, which may leak information about which secrets are actively used.
-
File relationships: By comparing encrypted file contents across commits, an attacker can detect when files are copied, moved, or when their content is synchronized.
-
Actual file contents: The actual file contents remain protected as long as the encryption key is kept secret. Without the key, attackers cannot decrypt the files.
-
Integrity: The HMAC ensures integrity and verifies that the correct key is used for decryption. Any tampering with encrypted files will be detected.
-
Key verification: The HMAC also serves as a key verification mechanism - if you use the wrong key, decryption will fail with an authentication error rather than producing garbage output.
For secrets stored in Git repositories, the primary threat is typically:
- Unauthorized access to the repository (e.g., compromised Git hosting service)
- Leaked repository backups
- Accidental public repository exposure
In these scenarios, deterministic encryption still protects the actual secret values. The ability to detect unchanged files is essential for Git's efficiency and is a necessary trade-off for transparent encryption in version control systems.
Consider alternative approaches if you need:
-
Hide file equality: If you need to hide the fact that two files contain the same secret, consider using different keys or additional obfuscation techniques.
-
Hide update patterns: If you need to hide update patterns (e.g., to prevent attackers from knowing which secrets are actively maintained), consider using a different encryption scheme. Note that this would break Git's content deduplication and significantly impact repository size.
-
Protection against active attackers: If you're protecting against attackers who can observe your repository in real-time and correlate changes with external events, deterministic encryption may leak timing information.