How Do You Know the File Hasn't Been Changed?
Nilufar was a systems administrator who managed software deployments for a large enterprise. Her job required downloading software packages from vendor websites, then distributing them to thousands of machines. But she'd seen the horror stories: compromised vendor websites serving malware disguised as legitimate software, supply chain attacks where an installer was modified to include a backdoor.
Her first tool for fighting this was hashing.
A cryptographic hash function took any input β a document, a software package, a string of text β and produced a fixed-length output called a digest or hash. The properties that made it useful:
First, the function was deterministic: the same input always produced the same output. Second, it was one-way: given a hash, you couldn't work backwards to find the input (without trying every possible input). Third, it had the avalanche effect: changing even a single bit of input produced a completely different hash β about half of the output bits would flip. And fourth, it was collision-resistant: finding two different inputs that produced the same hash was computationally infeasible.
The vendor published the SHA-256 hash of their software package. After downloading, Nilufar computed the SHA-256 hash of her downloaded file. If the hashes matched, the file was identical to what the vendor published β not modified, not corrupted. If they differed, something had changed.
2. One-way: Cannot reverse a hash to find the original input
3. Avalanche effect: Tiny input change = completely different hash
4. Collision-resistant: Computationally infeasible to find two inputs with the same hash
These four properties make hashing useful for integrity verification, password storage, and digital signatures.
Not All Hashes Are Equal: MD5's Downfall
Nilufar's company still had some legacy systems that used MD5 for file verification. She flagged this as a risk.
MD5 (Message Digest 5) had been a standard hashing algorithm for years β fast, widely supported, producing a 128-bit hash. But in 1996, researchers found theoretical weaknesses. By 2004, they had demonstrated practical collision attacks: two different inputs that produced the same MD5 hash.
The implications were severe. An attacker could potentially create a malicious file that had the same MD5 hash as a legitimate file. The verification would pass β the hashes matched β but the file was different. MD5 was broken for security purposes.
The lesson: collision resistance was the critical property for security use cases. SHA-256 (part of the SHA-2 family, producing 256-bit hashes) and SHA-3 were the modern standards. SHA-256 produced hashes like: a3b4c5d6... β 64 hexadecimal characters representing 256 bits. An SHA-256 collision had never been found and would require astronomical computational effort.
Hashing Passwords: The Right Way Requires a Salt
Nilufar's next concern was the authentication database. Passwords should never be stored in plaintext β a database breach would immediately expose all user credentials. Instead, when a user created their password, the system should store the hash of that password. At login, the system hashed the entered password and compared to the stored hash.
But simple password hashing had a vulnerability: rainbow tables. An attacker who obtained a database of password hashes could precompute hashes of millions of common passwords and look up matches instantly. The hash of "password123" was always the same β so a table of pre-computed hashes made cracking trivially fast.
The defense was salting. Before hashing, the system added a random unique value β the salt β to each password. "password123" + random salt "x7q2" = "password123x7q2", then hashed. The salt was stored alongside the hash (it didn't need to be secret). Now, even if two users had the same password, their stored hashes were different. Rainbow tables became useless β an attacker would need to recompute the entire table for each unique salt.
Modern password hashing algorithms like bcrypt and Argon2 built in salting automatically and added another defense: they were intentionally slow. The computational cost of hashing one password was tuned to take hundreds of milliseconds. Legitimate logins barely noticed. Brute-force attacks that tried millions of passwords per second were throttled to thousands β or fewer.
2. Hash the password before storing (SHA-256 minimum, bcrypt/Argon2 preferred)
3. Add a unique random salt per user to defeat rainbow tables
4. Use intentionally slow hashing to throttle brute-force attempts
The salt is stored with the hash β it's not secret, but it makes precomputed attacks useless.
Digital Signatures: Proving Identity and Preventing Denial
Hashing alone solved integrity β but it didn't prove who created the data. Anyone could compute a SHA-256 hash. For that, Nilufar needed digital signatures.
A digital signature used asymmetric cryptography β but in the opposite direction from encryption. The process:
First, the sender computed a hash of the document. Then, the sender encrypted that hash with their own private key. The result was the digital signature β attached to the document. The recipient received the document and the signature. To verify, the recipient decrypted the signature using the sender's public key (obtaining the hash), computed their own hash of the received document, and compared the two. If they matched: the document hadn't been modified (integrity), and only the holder of that private key could have created this signature (authentication). If the hashes matched, the sender could not later deny having signed it (non-repudiation).
"Digital signatures give us three things," Nilufar told her team. "Integrity β the data hasn't been changed. Authentication β we know who signed it. And non-repudiation β they can't deny it, because only their private key could have produced this signature."
Code signing certificates used digital signatures so that when a user installed software, their operating system could verify the software came from the published developer and hadn't been modified. Email signing (S/MIME, PGP) let recipients verify that an email truly came from the claimed sender. Document signing in PDF workflows created legally binding records of who approved what and when.
Authentication: The signature proves the identity of the signer β only the holder of the private key could have produced it.
Non-repudiation: The signer cannot deny having signed β the private key is theirs alone, and the signature is mathematical proof.
Process: Sign = Hash the document, encrypt hash with YOUR private key. Verify = Decrypt signature with signer's PUBLIC key, compute hash of document, compare.