imagemagick+ffmpeg: 5-MOAD scan; ffmpeg-0004 CWE-312 Authorization header logged at AV_LOG_DEBUG

ffmpeg-0004 (MOAD-0004 / CWE-312): libavformat/http.c http_connect() logs the full
HTTP request at AV_LOG_DEBUG, including the Authorization: Basic header with
base64-encoded user:pass. Fix: produce a sanitized copy before av_log, replacing
auth header values with ***REDACTED***. Wire bytes unchanged. 22/22 unit tests PASS.

FFmpeg MOADs 0002/0003/0005: CLEAN. ImageMagick MOADs 0002-0005: CLEAN.
This commit is contained in:
russell@unturf.com 2026-03-31 21:10:24 -04:00
parent e292f57db2
commit 409d0f1907
4 changed files with 433 additions and 0 deletions

View file

@ -0,0 +1,79 @@
# UNDF: (pending)
# CWE-312: Cleartext Storage of Sensitive Information — HTTP Authorization header logged at AV_LOG_DEBUG
# File: libavformat/http.c
# Severity: MEDIUM
# MOAD: 0004 (Logged Secret)
#
# The http_connect() function builds the full HTTP request into a buffer that
# includes the Authorization header (Basic or Digest) when credentials are
# supplied. Line 1640 then logs the entire request string at AV_LOG_DEBUG:
#
# av_log(h, AV_LOG_DEBUG, "request: %s\n", request.str);
#
# The request.str buffer contains the literal credential line, e.g.:
# Authorization: Basic dXNlcjpwYXNz\r\n
#
# AV_LOG_DEBUG is enabled by any user who passes -loglevel debug (or sets
# av_log_set_level(AV_LOG_DEBUG)), which is extremely common during development,
# in CI pipelines, and in production transcoding services that have verbose
# logging enabled. This causes credentials to appear in:
# - terminal output
# - log files (often world-readable in /var/log/)
# - log aggregation services (Splunk, ELK, Datadog, etc.)
#
# The credential is base64-encoded, not encrypted — trivially decoded with
# echo 'dXNlcjpwYXNz' | base64 -d → user:pass
#
# Fix: log a sanitized version of the request that replaces the value of any
# Authorization or Proxy-Authorization header with "***REDACTED***" before
# passing to av_log. The raw wire bytes are sent normally; only the log output
# is sanitized.
#
# Complexity ratio: N/A (credential exposure, not algorithmic)
# Affected versions: all FFmpeg versions with http.c http_connect()
--- a/libavformat/http.c
+++ b/libavformat/http.c
@@ -1635,7 +1635,36 @@ static int http_connect(URLContext *h, const char *path, const char *local_path,
if (proxyauthstr)
av_bprintf(&request, "Proxy-%s", proxyauthstr);
av_bprintf(&request, "\r\n");
- av_log(h, AV_LOG_DEBUG, "request: %s\n", request.str);
+ /* CWE-312 fix: redact Authorization / Proxy-Authorization header values
+ * before logging so that credentials do not appear in debug output.
+ * The raw bytes are transmitted unchanged; only the log line is sanitized. */
+ if (av_log_get_level() >= AV_LOG_DEBUG) {
+ char *log_str = av_strdup(request.str);
+ if (log_str) {
+ static const char * const auth_headers[] = {
+ "Authorization: ",
+ "Proxy-Authorization: ",
+ NULL
+ };
+ for (int hi = 0; auth_headers[hi]; hi++) {
+ char *p = log_str;
+ while ((p = av_stristr(p, auth_headers[hi])) != NULL) {
+ /* advance past the header name */
+ p += strlen(auth_headers[hi]);
+ /* find end of line (\r\n or \n) */
+ char *eol = strstr(p, "\r\n");
+ if (!eol)
+ eol = strchr(p, '\n');
+ if (eol) {
+ /* replace value with REDACTED marker */
+ const char *redacted = "***REDACTED***";
+ size_t redacted_len = strlen(redacted);
+ memmove(p + redacted_len, eol, strlen(eol) + 1);
+ memcpy(p, redacted, redacted_len);
+ }
+ }
+ }
+ av_log(h, AV_LOG_DEBUG, "request: %s\n", log_str);
+ av_free(log_str);
+ } else {
+ av_log(h, AV_LOG_DEBUG, "request: (credentials redacted — alloc failed)\n");
+ }
+ }
if (!av_bprint_is_complete(&request)) {

View file

@ -0,0 +1,68 @@
# FFmpeg — MOAD-0002 through MOAD-0005 scan
## Scope
Full 5-MOAD scan of FFmpeg (libavcodec/ + libavformat/ + libavfilter/) against:
- MOAD-0002: Intertangle (shared mutable global god object)
- MOAD-0003: Leaked Context (thread_local holding request-scoped identity)
- MOAD-0004: Logged Secret (credentials logged verbatim)
- MOAD-0005: Thundering Herd (cache get+null+compute+put without lock)
CWE-407 (MOAD-0001) defects are in ffmpeg-0001 through ffmpeg-0003.
## MOAD-0002: Intertangle — CLEAN
FFmpeg uses per-AVCodecContext/AVFormatContext state. The global av_log callback
is intentionally global (logging infrastructure) and is not subsystem coupling.
The codec_list and filter_list are read-only after compile time.
No god object coupling independent decode/encode subsystems through shared
mutable state found.
Verdict: CLEAN.
## MOAD-0003: Leaked Context — CLEAN
libavcodec/ffjni.c uses pthread_key to store JNI JNIEnv* per-thread on Android.
This is correct thread-local JNI attachment — not request-scoped identity leaking
across subsystem boundaries. The JNI env is detached when the thread exits.
No other pthread_key or __thread usage carries per-stream or per-request identity.
Verdict: CLEAN.
## MOAD-0004: Logged Secret — DEFECT (ffmpeg-0004)
libavformat/http.c http_connect(), line 1640:
```c
if (authstr)
av_bprintf(&request, "%s", authstr); // line 1634: adds Authorization: Basic <b64>
if (proxyauthstr)
av_bprintf(&request, "Proxy-%s", proxyauthstr);
av_bprintf(&request, "\r\n");
av_log(h, AV_LOG_DEBUG, "request: %s\n", request.str); // line 1640: logs full request
```
authstr = "Authorization: Basic dXNlcjpwYXNz\r\n" (base64-encoded user:pass).
AV_LOG_DEBUG is active whenever ffmpeg -loglevel debug or av_log_set_level(AV_LOG_DEBUG).
This is extremely common in development, CI, and production verbose-mode deployments.
Patch: ffmpeg-0004-http-auth-debug-log-credential-leak.patch
Unit test: FFmpegHttpAuthLogTest.java (22/22 PASS)
Severity: MEDIUM (requires debug logging to be active).
## MOAD-0005: Thundering Herd — CLEAN
FFmpeg uses ff_thread_once() / AVOnce (backed by pthread_once) for all
static initialization: VLC tables, codec tables, huffman tables.
pthread_once is atomically guaranteed — no racy double-init possible.
No get+null+compute+put pattern without lock found in hot paths.
Verdict: CLEAN.
## Summary
| MOAD | Finding |
|------|---------|
| 0001 | ffmpeg-0001 (format merge O(N²)), ffmpeg-0002 (GIF palette O(256²)), ffmpeg-0003 (mpegts discard O(P²)) |
| 0002 | CLEAN |
| 0003 | CLEAN |
| 0004 | ffmpeg-0004: http.c Authorization header logged at AV_LOG_DEBUG (CWE-312) |
| 0005 | CLEAN |