Signal9Signal9 — home
online
all lessons

File Integrity & Hashing: Fingerprints, Checksums, and Tamper Detection

Beginner20 min6 steps
What you'll learn
  • Explain what a cryptographic hash is and why one changed bit flips the whole digest
  • Compute a file's digest with sha256sum and read its output
  • Choose SHA-256 over the broken MD5/SHA-1 for anything security-relevant
  • Verify files against a published checksum and a SHA256SUMS manifest with sha256sum -c
  1. A hash is a file's fingerprint

    Build the mental model of a hash as a deterministic, one-way, fixed-size fingerprint.

    You download an installer, copy a backup across the network, or hand a file to a teammate — and later you need to answer one question: is this the exact same file, byte for byte, that I expected? Comparing whole files is slow and usually impossible (you don't have the original handy). The practical answer is a hash.

    A cryptographic hash function takes any input — one byte or ten gigabytes — and produces a fixed-size fingerprint called a digest (or checksum). Three properties make it useful:

    • Deterministic — the same input always produces the same digest.
    • Avalanche effect — change a single bit and the digest changes completely. There is no 'close'; it either matches or it doesn't.
    • One-way — you can't work backwards from a digest to the original data.

    So instead of shipping a whole file to prove it's unchanged, you ship its tiny digest. If two files share the same SHA-256 digest, they are — for all practical purposes — identical.

    Check yourself

    You change one byte in a 2 GB file and recompute its SHA-256. Roughly how much of the digest changes — a little, or a lot?

  2. Computing a digest with sha256sum

    Produce a file's digest and read the tool's output format.

    The standard tools are named after their algorithms — md5sum, sha1sum, sha256sum, sha512sum. Each prints the digest, two spaces, then the filename.

    Example — not runnable on your lab box

    sha256sum report.pdf
    9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08  report.pdf
    report.pdf
    The file to fingerprint. Pass several files to get one line each.
    The 64-hex-character digest is the fingerprint of report.pdf. Run it again on the same file and you get the exact same string — that repeatability is what lets you compare against a value someone published earlier.

    Example — not runnable on your lab box

    sha256sum *.iso
    44e2...  debian-12.iso
    7b1a...  ubuntu-24.iso
    Hand it a glob and it prints one 'digest␠␠filename' line per file. That two-column shape is exactly the format of a checksum manifest — which is where this goes next.

    To grab just the digest (for scripting or to store in a variable), keep the first field: sha256sum report.pdf | awk '{print $1}'.

  3. Pick the right algorithm: SHA-256, not MD5

    Choose a secure hash and understand why MD5/SHA-1 no longer prove authenticity.

    The algorithms differ in digest size and, crucially, in security:

    • MD5 and SHA-1 are broken. Attackers can craft two different files with the same MD5 (or SHA-1) digest — a 'collision'. A matching MD5 therefore no longer proves a file is authentic. You'll still see MD5 used as a cheap non-adversarial change detector, but never trust it against a motivated attacker.
    • SHA-256 (and SHA-512) are the current standard for integrity and authenticity. When a project publishes checksums for its releases, they're almost always SHA-256.

    Rule of thumb: reach for `sha256sum` unless something explicitly requires otherwise.

    Check yourself

    A release page offers both an MD5 and a SHA-256 for the same file. Which do you use to be confident the file wasn't tampered with?

  4. Verify against a published checksum and a manifest

    Confirm a single download, then verify many files at once with sha256sum -c.

    The most common real task: you downloaded a file and the project lists its SHA-256. Recompute locally and compare — if even one character differs, the file is not what the project released (corrupted, truncated, or tampered), so don't run it.

    Example — not runnable on your lab box

    sha256sum ubuntu-24.iso
    9b1c...e4  ubuntu-24.iso
    Compare this digest against the one the project published. Equal means the file is intact (it matches that digest); any difference means stop and re-download. Whether it's also authentic depends on trusting where the digest came from — the next step covers that distinction.

    Doing that by eye across dozens of files is error-prone. Instead, save the digests to a manifest (conventionally SHA256SUMS) and let the tool do the comparison.

    Example — not runnable on your lab box

    sha256sum *.conf > SHA256SUMS
    Writes one 'digest␠␠filename' line per file into SHA256SUMS — a baseline snapshot you can re-verify later. (Manifest specific files, e.g. *.conf, so the manifest doesn't try to include itself.)

    Example — not runnable on your lab box

    sha256sum -c SHA256SUMS
    app.conf: OK
    db.conf: OK
    web.conf: FAILED
    sha256sum: WARNING: 1 computed checksum did NOT match
    -c
    Check mode: read the manifest and verify each file's current digest against it.
    -c (check) recomputes each listed file and reports OK or FAILED, naming exactly which file drifted. That FAILED line is your tamper/corruption alarm; a missing file is reported too, so a manifest also catches deletions.

    Check yourself

    You run sha256sum -c SHA256SUMS and one line ends in FAILED. What does that tell you about that file?

  5. Integrity is not authenticity: checksums vs signatures

    See the limit of a bare checksum and why real authenticity needs a signature.

    Here's the subtlety that trips people up, and it's worth getting right. A checksum answers *"does this file match this digest?" — that's integrity. It reliably catches accidental corruption, and it catches tampering if you trust where the digest came from*.

    But think about the attacker. If someone can replace the file on a download server, they can usually replace the checksum on the same page too — and make the two match. Your sha256sum -c would happily print OK, because the file really does match the (attacker-supplied) digest. So a checksum sitting next to the download proves integrity, not authenticity — it proves the bytes match a digest, not that the digest is genuinely the project's.

    Closing that gap needs a cryptographic signature. Projects sign their SHA256SUMS file with their private key; you verify it with their public key using gpg --verify. That gives a chain of trust:

    • you trust the project's public key (obtained once, out-of-band), →
    • the signature proves the SHA256SUMS manifest is genuinely theirs and unaltered, →
    • the manifest (via sha256sum -c) proves each file matches.

    A valid signature is what turns "these bytes match a number on a web page" into "these bytes are really what the project published."

    The transcript below is the verification half. The GnuPG Essentials lesson (gpg-essentials) picks up from here: you make a key, sign a SHA256SUMS of your own, verify it, and learn to read the four different verdicts gpg can give.

    Example — not runnable on your lab box

    gpg --verify SHA256SUMS.gpg SHA256SUMS
    gpg: Signature made Tue 22 Jul 2026 10:14:02 UTC
    gpg:                using RSA key 843938DF228D22F7B3742BC0D94AA3F0EFE21092
    gpg: Good signature from "Ubuntu CD Image Automatic Signing Key"
    Verify that the SHA256SUMS manifest was signed by the project's key. 'Good signature' means the manifest is authentic and unmodified — so the digests inside it can be trusted, and only THEN does sha256sum -c against it prove the files are genuine. Without this step, you're trusting whoever served the checksum.

    Check yourself

    A download page lists a SHA-256 right next to the file, with no signature. Matching it proves the file is — intact, or authentic?

  6. Try it in your live terminal

    Run these safe commands on the real sandbox to see hashing and verification end to end.

    You have a real shell. These commands create a throwaway file, fingerprint it, baseline a manifest, and prove verification catches a change — nothing here touches the rest of the system.

    echo hello > /tmp/f.txt && sha256sum /tmp/f.txt
    Creates a file and prints its SHA-256. Note the digest, then run the same command again — it's identical, because the input is identical.

    Example — not runnable on your lab box

    cd /tmp && sha256sum f.txt > SUMS && sha256sum -c SUMS
    Baselines f.txt into a manifest, then verifies it — you should see 'f.txt: OK'.

    Example — not runnable on your lab box

    echo tampered >> /tmp/f.txt && sha256sum -c /tmp/SUMS
    Appends a line, then re-checks against the old manifest. Now it reports 'f.txt: FAILED' — you've just watched a tamper detector fire.
Recap
  • A hash is a deterministic, one-way, fixed-size fingerprint; any change flips the whole digest (the avalanche effect).
  • Compute with sha256sum <file>; prefer SHA-256 over the broken MD5/SHA-1 for anything security-relevant.
  • Compare a recomputed digest to a published one to prove a download is intact (integrity); it's only authentic if you trust where the digest came from.
  • Integrity ≠ authenticity: a checksum next to a download proves the bytes match a number; a GPG signature (gpg --verify) proves the number is really the project's. Security-relevant downloads want both.
  • Build a SHA256SUMS manifest with sha256sum, and verify a whole set later with sha256sum -c — it names the exact files that FAILED or went missing.
Command → what it does / what to watch for
CommandDoesWatch for
sha256sum <file>Print a file's SHA-256 digestThe current, secure default
md5sum / sha1sumOlder/faster digestsBroken for security — corruption check only
sha256sum * > SHA256SUMSBaseline files into a manifestManifest *.ext so it doesn't hash itself
sha256sum -c SHA256SUMSVerify files against a manifestFAILED = changed; also flags missing files
gpg --verify SHA256SUMS.gpg SHA256SUMSProve the manifest is the project'sIntegrity vs authenticity — this adds authenticity
... | awk '{print $1}'Keep just the digestStrips the two-space + filename