Chapter 15 — Evidence and measurement
Lesson map
Lesson 15 published. Map, example, EX043.
flowchart TD
N0["Learning outcomes"]
N1["15.1 Why logs need protection"]
N2["Lab — Hash-chained audit log"]
N3["15.2 Reading the chain"]
N4["15.3 Measuring controls"]
N5["Synthesis"]
N0 --> N1 --> N2 --> N3 --> N4 --> N5
N5 --> EX["Worked example"]
EX --> Q["Exercise EX043"]
Logical drawing
flowchart LR
N0["Learning outcomes"]
N1["15.1 Why logs need protection"]
N2["Lab — Hash-chained audit log"]
N3["15.2 Reading the chain"]
N4["15.3 Measuring controls"]
N5["Synthesis"]
N0 --> N1 --> N2 --> N3 --> N4 --> N5 --> Q["EX043"]
[Learning outcomes]
|
v
[15.1 Why logs need protection]
|
v
[Lab — Hash-chained audit log]
|
v
[15.2 Reading the chain]
|
v
[15.3 Measuring controls]
|
v
[Synthesis]
|
v
[Worked example] --> [Exercise EX043]
Learning outcomes
You will (1) make logs tamper-evident with hash chains; (2) measure security controls with clear definitions; (3) interpret evidence honestly.
15.1 Why logs need protection
When a patron complains that someone accessed their record, Tidewell checks the access log. But an insider who misused data could also edit the log. Priya learns that evidence must be tamper-evident: changes must be detectable. A hash chain does this by including each entry's hash in the next, so editing any entry changes every later hash. Publishing or separately storing the latest hash, called the head, lets an auditor detect edits. Certificate Transparency logs, defined in RFC 6962, use a related structure, a Merkle tree, at internet scale.
Lab — Hash-chained audit log
"""Tamper-evident audit log: each entry's hash includes the previous hash (SHA-256)."""
import hashlib
def chain(entries):
prev, out = "0" * 64, []
for e in entries:
prev = hashlib.sha256((prev + e).encode()).hexdigest(); out.append(prev)
return out
def main():
log = ["2026-05-01 admin login", "2026-05-01 patron export", "2026-05-02 key rotated"]
good = chain(log)
print("head:", good[-1][:16])
log[1] = "2026-05-01 report viewed"
bad = chain(log)
print("head after editing entry 2:", bad[-1][:16])
print("first differing entry:", next(i + 1 for i, (x, y) in enumerate(zip(good, bad)) if x != y))
if __name__ == "__main__":
main()
Run with python3 lab_chain.py. Verified output:
head: 4492b592d8b729f0
head after editing entry 2: c36641715cd10666
first differing entry: 2
15.2 Reading the chain
The three-entry log has a head beginning 4492b592. When the second entry is edited from "patron export" to "report viewed", the head becomes c3664171 and the first differing hash is at entry 2, showing exactly where tampering began. Priya notes the limit: if an insider can rewrite the whole chain and the stored head, nothing is detected. The head must therefore be stored where the insider cannot change it, for instance sent daily to a separate auditor's system or signed by a different team.
15.3 Measuring controls
Priya defines measures for Tidewell's controls: percentage of staff accounts using security keys, number of days certificates are renewed before expiry, and share of access-log entries reviewed. Each measure has a precise definition and data source. She reports baselines honestly: only 40 percent of staff use security keys so far, and log reviews happen monthly rather than weekly. She resists presenting only favourable numbers, because management decisions depend on an accurate picture. She shows each measure as a trend over months, so management can see whether controls are improving or slipping.
Worked example
Priya builds a hash-chained log, shows an edit changes the head from 4492b592 to c3664171 and locates it at entry 2, and reports control baselines including the 40 percent security-key adoption.
Synthetic case. Log entries and baselines are invented; RFC 6962 is real.
Errors and limits
Hash chains detect tampering only if the head is protected separately. Measures without definitions produce inconsistent numbers.
Exercises
- EX043 — Chain property. Why does editing one entry change every later hash? Rubric: 3 = each entry's hash includes the previous hash;; 2 = chaining;; 1 = it doesn't;; 0 = none.
- EX044 — Lab heads. Report the head before and after the edit and where the difference starts. Rubric: 3 = 4492b592 then c3664171, differing from entry 2;; 2 = heads;; 1 = wrong;; 0 = none.
- EX045 — Protecting the head. Where must the head be stored and why? Rubric: 3 = somewhere the insider cannot change, e.g. a separate auditor's system or signed by another team;; 2 = separate;; 1 = same server;; 0 = none.
Synthesis
Tamper-evident logs and honestly reported measures turn security claims into evidence others can check.
Continue studying — chapter 15 of 40
← Chapter 14 (draft) · Course chapter index · Chapter 16 (draft) →