Fatskills
Practice. Master. Repeat.
Study Guide: CompTIA Security+ Hashing Algorithms and Salting - Zero-Fluff, Hands-On Guide
Source: https://www.fatskills.com/comptia-security-/chapter/tech-comptia-security-hashing-algorithms-salting-zero-fluff-hands-on-guide

CompTIA Security+ Hashing Algorithms and Salting - Zero-Fluff, Hands-On Guide

By Fatskills Exam Guides Team — the exam nerds behind 28,500+ quizzes and 2.1M practice questions across 500+ global exams.

⏱️ ~8 min read

CompTIA Security+ Hashing Algorithms & Salting: Zero-Fluff, Hands-On Guide

(SHA-256, MD5 (deprecated), HMAC, and Salting)


1. What This Is & Why It Matters

Hashing is not encryption—it’s a one-way function that turns data into a fixed-size "fingerprint" (hash). In security, you’ll use it for: - Password storage (never store plaintext passwords—hash them!) - File integrity checks (e.g., verifying a downloaded ISO hasn’t been tampered with) - Digital signatures (proving a message hasn’t been altered) - API authentication (HMAC for request signing)

Real-world scenario: You’re a sysadmin inheriting a legacy web app. The database stores passwords in plaintext (? disaster waiting to happen). Your first task: migrate to hashed passwords with salts before the next breach. If you don’t, attackers who dump the database get every user’s password instantly.

Why this matters in production: - MD5 is broken (collisions found in seconds—never use it for security). - SHA-256 is the current standard for most use cases (but SHA-3 is emerging). - Salting prevents rainbow table attacks (precomputed hashes for common passwords). - HMAC adds a secret key to hashing, making it useful for API authentication.


2. Core Concepts & Components

? Hashing Algorithm

  • Definition: A function that converts input data into a fixed-size string (hash). Same input-same hash. Different input? (ideally) wildly different hash.
  • Production insight: If two files have the same hash, they’re almost certainly identical. If they differ by even 1 bit, the hash should be completely different (avalanche effect).

? SHA-256 (Secure Hash Algorithm 256-bit)

  • Definition: A cryptographic hash function from the SHA-2 family. Outputs a 64-character hex string (256 bits).
  • Production insight: Used in Bitcoin, TLS certificates, password storage, and file verification. Resistant to collisions (so far).

? MD5 (Message Digest Algorithm 5) – DEPRECATED

  • Definition: A 128-bit hash function (32-character hex string). Fast but cryptographically broken (collisions found in 2004).
  • Production insight: Still used for non-security purposes (e.g., checksums for file downloads), but never for passwords or integrity checks. If you see MD5 in a security context, flag it as a vulnerability.

? HMAC (Hash-Based Message Authentication Code)

  • Definition: A hashing method that combines a secret key with the input data. Used to verify both integrity and authenticity (e.g., API requests).
  • Production insight: HMAC-SHA256 is common in AWS API signing, JWT tokens, and webhook verification. Without the key, attackers can’t forge valid hashes.

? Salt

  • Definition: A random, unique value added to a password before hashing to prevent rainbow table attacks.
  • Production insight: Every password must have a unique salt. If two users have the same password, their hashes should still differ because of different salts.

? Rainbow Table

  • Definition: A precomputed table of hashes for common passwords (e.g., "password123"-482c811da5d5b4bc6d497ffa98491e38). Used to crack unsalted hashes.
  • Production insight: Salting makes rainbow tables useless—attackers would need to generate a new table for every salt.

? Collision Attack

  • Definition: Finding two different inputs that produce the same hash (e.g., two different files with the same MD5 hash).
  • Production insight: SHA-256 is resistant to collisions (for now). MD5 is trivially collidable (bad for security).

3. Step-by-Step Hands-On: Hashing & Salting in Practice

Prerequisites

  • A Linux/macOS terminal (or WSL on Windows).
  • Python 3.x installed (python3 --version).
  • OpenSSL (openssl version).

Task 1: Generate SHA-256 Hashes (File & Password)

Step 1: Hash a file (verify integrity)

# Download a test file (e.g., Ubuntu ISO)
wget https://releases.ubuntu.com/22.04/ubuntu-22.04.3-desktop-amd64.iso

# Generate SHA-256 hash
sha256sum ubuntu-22.04.3-desktop-amd64.iso

Expected output:

a1b2c3d4e5f6...ubuntu-22.04.3-desktop-amd64.iso

Why this matters: If the hash matches the one on Ubuntu’s website, the file hasn’t been tampered with.

Step 2: Hash a password (with salt)

# Generate a random salt (16 bytes = 32 hex chars)
openssl rand -hex 16

Example output:

3a7b9c1d5e8f0a2b4c6d8e0f1a3b5c7d

Now, hash a password with the salt (using Python):

import hashlib

password = "SuperSecret123"
salt = "3a7b9c1d5e8f0a2b4c6d8e0f1a3b5c7d"  # From openssl command above

# Combine password + salt and hash with SHA-256
hashed_password = hashlib.sha256((password + salt).encode()).hexdigest()

print(f"Salt: {salt}")
print(f"Hashed password: {hashed_password}")

Expected output:

Salt: 3a7b9c1d5e8f0a2b4c6d8e0f1a3b5c7d
Hashed password: 5e884898da28047151d0e56f8dc6292773603d0d6aabbdd62a11ef721d1542d8

Why this matters: Even if two users have the same password, their hashes will differ because of the unique salt.


Task 2: Verify a Password (Simulate Login)

def verify_password(input_password, stored_hash, salt):
    # Hash the input password with the stored salt
    new_hash = hashlib.sha256((input_password + salt).encode()).hexdigest()
    return new_hash == stored_hash

# Test with correct password
print(verify_password("SuperSecret123", "5e884898da28047151d0e56f8dc6292773603d0d6aabbdd62a11ef721d1542d8", "3a7b9c1d5e8f0a2b4c6d8e0f1a3b5c7d"))
# Output: True

# Test with wrong password
print(verify_password("WrongPassword", "5e884898da28047151d0e56f8dc6292773603d0d6aabbdd62a11ef721d1542d8", "3a7b9c1d5e8f0a2b4c6d8e0f1a3b5c7d"))
# Output: False

Why this matters: This is exactly how secure password storage works in production (e.g., Django, Flask, ASP.NET).


Task 3: HMAC for API Authentication

import hmac
import hashlib

# Secret key (should be stored securely, e.g., AWS Secrets Manager)
secret_key = b"SuperSecretAPIKey123"

# Message to sign (e.g., API request)
message = b"user_id=123&action=delete"

# Generate HMAC-SHA256
signature = hmac.new(secret_key, message, hashlib.sha256).hexdigest()

print(f"HMAC-SHA256: {signature}")

Expected output:

HMAC-SHA256: 7f83b1657ff1fc53b92dc18148a1d65dfc2d4b1fa3d677284addd200126d9069

Why this matters: APIs like AWS S3, Stripe, and GitHub use HMAC to verify requests. Without the secret key, attackers can’t forge valid signatures.


4.-Production-Ready Best Practices

Security

  • Never use MD5 or SHA-1 for security (only for checksums).
  • Always salt passwords (and store the salt alongside the hash).
  • Use slow hashing algorithms for passwords (e.g., bcrypt, Argon2, PBKDF2)—SHA-256 is too fast for brute-force resistance.
  • Rotate HMAC keys periodically (e.g., every 90 days).
  • Never log hashes or salts (they’re sensitive data).

Performance

  • SHA-256 is fast (good for file integrity, bad for passwords).
  • Bcrypt/Argon2 are slow by design (good for passwords, bad for large files).

Storage

  • Store salts in plaintext (they’re not secret, just unique).
  • Use a database column for salts (e.g., users.salt).
  • Never hardcode HMAC keys (use environment variables or secrets managers).

Verification

  • Compare hashes in constant time (to prevent timing attacks): ```python #-Bad (vulnerable to timing attacks) if hash1 == hash2: return True

#-Good (constant-time comparison) if hmac.compare_digest(hash1, hash2): return True ```


5. Common Mistakes & Traps

Mistake Symptom Fix/Prevention
Using MD5 for passwords Attackers crack hashes in seconds using rainbow tables. Migrate to bcrypt/Argon2 immediately.
Reusing salts If two users have the same password, their hashes match. Generate a unique salt per user.
Not salting at all Rainbow table attacks succeed instantly. Always salt passwords.
Using SHA-256 for passwords Brute-force attacks succeed too easily (SHA-256 is fast). Use bcrypt/Argon2 (slow hashing).
Hardcoding HMAC keys If the code leaks, attackers can forge API requests. Store keys in AWS Secrets Manager/Vault.
Storing hashes in logs Logs may be exfiltrated, exposing password hashes. Never log hashes or salts.

6.-Exam/Certification Focus (CompTIA Security+)

Typical Question Patterns

  1. "Which hashing algorithm is deprecated for security use?"
  2. ? MD5 (and SHA-1).
  3. SHA-256, HMAC-SHA256.

  4. "What is the purpose of a salt?"

  5. ? Prevent rainbow table attacks by making each hash unique.
  6. "To encrypt the password" ( hashing-encryption).

  7. "Which is better for password storage: SHA-256 or bcrypt?"

  8. ? bcrypt (slow hashing resists brute force).
  9. SHA-256 (too fast for passwords).

  10. "What does HMAC provide that SHA-256 alone does not?"

  11. ? Authentication (proves the message came from a trusted source).
  12. ? "Encryption" ( HMAC is not encryption).

Key Trap Distinctions

  • Hashing vs. Encryption:
  • Hashing = one-way (can’t reverse).
  • Encryption = two-way (can decrypt with a key).
  • Salt vs. Pepper:
  • Salt = unique per user, stored in DB.
  • Pepper = global secret, stored separately (e.g., in config).
  • Collision Resistance:
  • MD5 = broken (collisions found).
  • SHA-256 = resistant (for now).

7.-Hands-On Challenge (with Solution)

Challenge:

You’re given a database dump with unsalted MD5 password hashes. An attacker has a rainbow table. How do you migrate to SHA-256 with salts without forcing users to reset passwords?

Solution:

  1. Generate a unique salt for each user.
  2. Re-hash the existing MD5 hash (not the plaintext password!) with the salt. ```python import hashlib

# Example: Migrate an MD5 hash to SHA-256 with salt old_md5_hash = "5f4dcc3b5aa765d61d8327deb882cf99" # MD5("password") salt = "3a7b9c1d5e8f0a2b4c6d8e0f1a3b5c7d" # Random salt

# Re-hash the MD5 hash with salt (not the plaintext password!) new_sha256_hash = hashlib.sha256((old_md5_hash + salt).encode()).hexdigest() print(new_sha256_hash) ```
3. Store the new hash and salt in the database.
4. Update the login system to use SHA-256 with salts.

Why this works: - You never store plaintext passwords (even temporarily). - The MD5 hash acts as a "proxy" for the password during migration. - After migration, disable MD5 entirely.


8.-Rapid-Reference Crib Sheet

Command/Concept Example/Usage Notes
SHA-256 (file) sha256sum file.txt Verify file integrity.
SHA-256 (string) echo -n "password" \| sha256sum -n prevents newline.
MD5 (file) md5sum file.txt Never use for security.
Generate salt openssl rand -hex 16 16 bytes = 32 hex chars.
HMAC-SHA256 echo -n "message" \| openssl dgst -sha256 -hmac "key" Used for API signing.
Bcrypt (Python) bcrypt.hashpw(password.encode(), bcrypt.gensalt()) Slow hashing for passwords.
Verify hash bcrypt.checkpw(password.encode(), stored_hash) Constant-time comparison.
Rainbow table Precomputed hashes for common passwords. Salting prevents this.
Collision attack Two different inputs-same hash. MD5 is vulnerable.
Pepper Global secret added to hashes. Stored separately from salts.

9.-Where to Go Next

  1. OWASP Password Storage Cheat Sheet – Best practices for password hashing.
  2. Cryptography I (Stanford, Coursera) – Deep dive into hashing, HMAC, and encryption.
  3. Hashing in Python (Real Python) – Practical guide to hashlib and hmac.
  4. Argon2 vs. Bcrypt vs. PBKDF2 – Which slow hashing algorithm to use.