cybersecurity · beginner · ~15 min · safe pentest lab
Practice deny-by-default input validation on attacker-controlled text: enforce an exact length and a strict character alphabet while bounds-checking every index before dereferencing, and reject anything that is not the canonical form.
A SHA-256 digest is 256 bits. When systems store or compare hashes as text, the canonical form is exactly 64 lowercase hexadecimal characters ([0-9a-f]). Attacker-controlled strings routinely flow into hash-comparison code: session tokens, integrity checksums, API signatures, cached lookup keys.
If you accept a malformed "hash" without validating it, downstream code may treat it as trusted, index a table with it, log it unescaped, or compare it in ways that leak information. Uppercase, wrong length, embedded spaces, or non-hex bytes must all be rejected. Deny by default.
Implement int valid_sha256_hex(const char *s) that returns 1 only if s is exactly 64 characters long and every character is a lowercase hex digit. Otherwise return 0. A NULL pointer returns 0.
You operate on a fixed string the caller hands you. Never read past the terminating NUL: check each byte in bounds and stop at the first \0. Do not call strlen and then loop assuming the length — walk the buffer defensively.
NULL input.g.A single C string pointer s (possibly NULL). Only the bytes up to the terminating NUL may be read.
int: 1 if s is a canonical lowercase 64-char hex SHA-256 digest, otherwise 0.
C11, freestanding-friendly: no I/O, no allocation, no network or filesystem access. Must not read beyond the NUL terminator. Must run in O(n) with no undefined behavior. NULL must return 0 without dereferencing.
int valid_sha256_hex(const char *s){
/* TODO: validate that s is a 64-char lowercase hex SHA-256 digest.
This insecure stub trusts the input and always accepts. Replace it. */
(void)s;
return -1;
}
Using strlen() and trusting its result instead of walking the buffer, which reads the whole string even when it is far too long; accepting uppercase hex because you used a case-insensitive check; forgetting to confirm the string ends exactly at index 64 so an over-long string with a valid 64-char prefix is wrongly accepted; dereferencing s before the NULL check; using isxdigit() from <ctype.h> which also accepts uppercase and is locale-dependent.
NULL pointer returns 0 with no dereference; empty string returns 0; a 62- or 63-char string returns 0 (early NUL detected in the loop); a 65+ char string returns 0 (byte at index 64 is not NUL); uppercase A-F returns 0; a trailing or embedded space returns 0; a byte like 'g' or punctuation returns 0; all-zeros and all-f 64-char strings return 1.
Solve this exercise in the browser editor — compile and run against the test harness, no setup required.