80 lines
3.5 KiB
Diff
80 lines
3.5 KiB
Diff
# 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)) {
|