# UNDF: UNDF-2026-000001091 # 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)) {