In my previous blog, we explored how to verify file integrity during transfers using SHA-256 checksums.
That approach is effective in ensuring the file is intact β but it has a limitation: anyone who can get hold of the file can also generate the same SHA-256 hash.
So how do we make it tamper-proof and also be able to verify authenticity without jumping to heavy public key cryptography?
The answer: HMAC-SHA256.
π§ What is HMAC-SHA256?
HMAC stands for Hash-based Message Authentication Code.
Itβs a cryptographic technique where:
- You take your file (or message),
- Combine it with a secret key that is known only to sender and receiver
- Run it through a hashing algorithm like SHA-256. The result is a hash that can only be reproduced if you have both the exact file and the shared secret key.
Why this matters:
If an attacker changes the file, or tries to generate a fake hash without the secret key, the verification will fail.
βοΈ High-Level Workflow
Sender side:
- Generate HMAC-SHA256 hash of the file using the shared secret key.
- Send both the file and the generated HMAC hash.
Receiver side:
- Recompute HMAC-SHA256 hash of the received file using the same key.
- Compare it with the sent hash.
- Match β β file is intact and authenticated. Mismatch β β file is tampered or key mismatch.
π How to Generate the Secret Key
The security of HMAC entirely depends on the strength and secrecy of the key. A good secret key should:
- Be long enough (at least 32 bytes for SHA-256).
- Be generated using a cryptographically secure random generator.
- Never be hardcoded in source code.
- Be stored securely (e.g., in environment variables, secrets manager, or secure vault).
π Example β Generate a Secret Key in Python
def generate_secret_key(key_outfile):
# Generate a 32 byte(256-bit) random key
key = secrets.token_bytes(32)
encoded_key = base64.urlsafe_b64encode(key).decode()
# Store the Key in a txt file for sharing it across
with open(key_outfile, "w") as fout:
fout.write(encoded_key)
print("Secret file generated")
# key_outfile is the file path to where the secret key is stored.
The above code generates a random base64-encoded key and stores it in a file, which can then be shared securely between systems.
π‘ Pro Tip:
Use a Secrets Manager (like AWS Secrets Manager, HashiCorp Vault, Azure Key Vault) to store and retrieve the key securely.
Rotate keys periodically to minimize risk.
π Example β Generating HMAC-SHA256 Hash in Python
# Function to generate the HMAC SHA-256 Hash
def generate_hash(file_path, secret_key):
with open(file_path, 'rb') as fin:
file_data = fin.read()
hash = hmac.new(secret_key.encode(), file_data, hashlib.sha256).hexdigest()
return hash
# file_path : Path to the file for which hash is to be generated
# secret_key : Secret Key to be used
π Example β Verifying HMAC-SHA256 Hash in Python
# Function to verify the hash of the given file
def verify_hash(file_path, secret_key, file_hash):
# Compute the Hash of the File
calculated_hash = generate_hash(file_path=file_path, secret_key=secret_key)
# Match the hash with the received hash
is_hash_valid = hmac.compare_digest(calculated_hash, file_hash)
return is_hash_valid
# generate_hash Function as provided earlier
β Pros
Adds authentication: Ensures that only systems with the shared secret can generate the correct hash.
Simple to implement: Works with most programming languages and libraries.
Lightweight: No heavy cryptographic key pairs required.
β Cons
Key management: Both parties must securely store and protect the shared key.
Not for untrusted distribution: If the key is leaked, security is compromised.
Still no confidentiality: File content is not encrypted.
π Ideal Use Cases
- Internal file transfers between trusted systems that share a secure key.
- Batch jobs or ETL processes where both ends are controlled environments.
- Verifying sensitive configuration files or scripts within DevOps pipelines.
- Ensuring authenticity of firmware updates within closed networks.
π§ Final Thoughts
HMAC-SHA256 is a powerful upgrade from plain SHA-256 checksums, giving you both integrity and authenticity without overcomplicating the setup.
Itβs a great fit for controlled environments where a shared secret can be securely managed.
Given that the secret needs to be securely shared and managed, in my next post, I will explore a way where the secret no longer needs to be shared.
I have posted the code in Github.
Top comments (0)