diff options
| author | 2023-01-11 23:27:14 +0100 | |
|---|---|---|
| committer | 2023-01-11 23:27:14 +0100 | |
| commit | 075cf4c800063e3cc65c3d41a9c23222e8ebb554 (patch) | |
| tree | 6fb7d9c66fdbafea83f160c9043d9fd688844c1b | |
| parent | c75baefe40952e6ae80aa8570c0acfc9baf7d997 (diff) | |
API avoid logging passwords (#5001)
* API avoid logging passwords
* Strip passwords and tokens from API logs
* Only log failed requests information when in debug mode
* Remove debug SHA
* Clean also Apache logs
* Better comments
* Redact also token parameters
* shfmt
* Simplify whitespace
* redacted
| -rw-r--r-- | Docker/FreshRSS.Apache.conf | 2 | ||||
| -rwxr-xr-x | cli/sensitive-log.sh | 9 | ||||
| -rw-r--r-- | lib/lib_rss.php | 25 | ||||
| -rw-r--r-- | p/api/fever.php | 11 | ||||
| -rw-r--r-- | p/api/greader.php | 26 |
5 files changed, 56 insertions, 17 deletions
diff --git a/Docker/FreshRSS.Apache.conf b/Docker/FreshRSS.Apache.conf index 2cfb9cbf9..6281e59e5 100644 --- a/Docker/FreshRSS.Apache.conf +++ b/Docker/FreshRSS.Apache.conf @@ -4,7 +4,7 @@ DocumentRoot /var/www/FreshRSS/p/ RemoteIPHeader X-Forwarded-For RemoteIPTrustedProxy 10.0.0.1/8 172.16.0.1/12 192.168.0.1/16 LogFormat "%a %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined_proxy -CustomLog /dev/stdout combined_proxy +CustomLog "|/var/www/FreshRSS/cli/sensitive-log.sh" combined_proxy ErrorLog /dev/stderr AllowEncodedSlashes On ServerTokens OS diff --git a/cli/sensitive-log.sh b/cli/sensitive-log.sh new file mode 100755 index 000000000..40309b0db --- /dev/null +++ b/cli/sensitive-log.sh @@ -0,0 +1,9 @@ +#!/bin/sh +# Strips sensitive passwords from (Apache) logs + +# For e.g. GNU systems such as Debian +# N.B.: `sed -u` is not available in BusyBox and without it there are buffering delays (even with stdbuf) +sed -Eu 's/([?&])(Passwd|token)=[^& \t]+/\1\2=redacted/ig' 2>/dev/null || + + # For systems with gawk (not available by default in Docker of Debian or Alpine) or with BuzyBox such as Alpine + $(which gawk || which awk) -v IGNORECASE=1 '{ print gensub(/([?&])(Passwd|token)=[^& \t]+/, "\\1\\2=redacted", "g") }' diff --git a/lib/lib_rss.php b/lib/lib_rss.php index d0e819d98..cbdfff773 100644 --- a/lib/lib_rss.php +++ b/lib/lib_rss.php @@ -224,6 +224,31 @@ function html_only_entity_decode($text): string { } /** + * Remove passwords in FreshRSS logs. + * See also ../cli/sensitive-log.sh for Web server logs. + * @param array<string,mixed>|string $log + * @return array<string,mixed>|string + */ +function sensitive_log($log) { + if (is_array($log)) { + foreach ($log as $k => $v) { + if (in_array($k, ['api_key', 'Passwd', 'T'])) { + $log[$k] = '██'; + } else { + $log[$k] = sensitive_log($v); + } + } + } elseif (is_string($log)) { + $log = preg_replace([ + '/\b(auth=.*?\/)[^&]+/i', + '/\b(Passwd=)[^&]+/i', + '/\b(Authorization)[^&]+/i', + ], '$1█', $log); + } + return $log; +} + +/** * @param array<string,mixed> $attributes */ function customSimplePie($attributes = array()): SimplePie { diff --git a/p/api/fever.php b/p/api/fever.php index b7f9b9167..13907f16d 100644 --- a/p/api/fever.php +++ b/p/api/fever.php @@ -18,7 +18,8 @@ FreshRSS_Context::initSystem(); // check if API is enabled globally if (!FreshRSS_Context::$system_conf->api_enabled) { - Minz_Log::warning('Fever API: serviceUnavailable() ' . debugInfo(), API_LOG); + Minz_Log::warning('Fever API: service unavailable!'); + Minz_Log::debug('Fever API: serviceUnavailable() ' . debugInfo(), API_LOG); header('HTTP/1.1 503 Service Unavailable'); header('Content-Type: text/plain; charset=UTF-8'); die('Service Unavailable!'); @@ -45,16 +46,16 @@ function debugInfo() { } } global $ORIGINAL_INPUT; - return print_r( - array( + $log = sensitive_log([ 'date' => date('c'), 'headers' => $ALL_HEADERS, '_SERVER' => $_SERVER, '_GET' => $_GET, '_POST' => $_POST, '_COOKIE' => $_COOKIE, - 'INPUT' => $ORIGINAL_INPUT - ), true); + 'INPUT' => $ORIGINAL_INPUT, + ]); + return print_r($log, true); } //Minz_Log::debug('----------------------------------------------------------------', API_LOG); diff --git a/p/api/greader.php b/p/api/greader.php index afca1afaf..a3dad880e 100644 --- a/p/api/greader.php +++ b/p/api/greader.php @@ -97,27 +97,29 @@ function debugInfo() { } } global $ORIGINAL_INPUT; - return print_r( - array( + $log = sensitive_log([ 'date' => date('c'), 'headers' => $ALL_HEADERS, '_SERVER' => $_SERVER, '_GET' => $_GET, '_POST' => $_POST, '_COOKIE' => $_COOKIE, - 'INPUT' => $ORIGINAL_INPUT - ), true); + 'INPUT' => $ORIGINAL_INPUT, + ]); + return print_r($log, true); } function badRequest() { - Minz_Log::warning('badRequest() ' . debugInfo(), API_LOG); + Minz_Log::warning('GReader API: ' . __METHOD__, API_LOG); + Minz_Log::debug('badRequest() ' . debugInfo(), API_LOG); header('HTTP/1.1 400 Bad Request'); header('Content-Type: text/plain; charset=UTF-8'); die('Bad Request!'); } function unauthorized() { - Minz_Log::warning('unauthorized() ' . debugInfo(), API_LOG); + Minz_Log::warning('GReader API: ' . __METHOD__, API_LOG); + Minz_Log::debug('unauthorized() ' . debugInfo(), API_LOG); header('HTTP/1.1 401 Unauthorized'); header('Content-Type: text/plain; charset=UTF-8'); header('Google-Bad-Token: true'); @@ -125,21 +127,24 @@ function unauthorized() { } function notImplemented() { - Minz_Log::warning('notImplemented() ' . debugInfo(), API_LOG); + Minz_Log::warning('GReader API: ' . __METHOD__, API_LOG); + Minz_Log::debug('notImplemented() ' . debugInfo(), API_LOG); header('HTTP/1.1 501 Not Implemented'); header('Content-Type: text/plain; charset=UTF-8'); die('Not Implemented!'); } function serviceUnavailable() { - Minz_Log::warning('serviceUnavailable() ' . debugInfo(), API_LOG); + Minz_Log::warning('GReader API: ' . __METHOD__, API_LOG); + Minz_Log::debug('serviceUnavailable() ' . debugInfo(), API_LOG); header('HTTP/1.1 503 Service Unavailable'); header('Content-Type: text/plain; charset=UTF-8'); die('Service Unavailable!'); } function checkCompatibility() { - Minz_Log::warning('checkCompatibility() ' . debugInfo(), API_LOG); + Minz_Log::warning('GReader API: ' . __METHOD__, API_LOG); + Minz_Log::debug('checkCompatibility() ' . debugInfo(), API_LOG); header('Content-Type: text/plain; charset=UTF-8'); if (PHP_INT_SIZE < 8 && !function_exists('gmp_init')) { die('FAIL 64-bit or GMP extension! Wrong PHP configuration.'); @@ -172,8 +177,7 @@ function authorizationToUser() { if ($headerAuthX[1] === sha1(FreshRSS_Context::$system_conf->salt . $user . FreshRSS_Context::$user_conf->apiPasswordHash)) { return $user; } else { - Minz_Log::warning('Invalid API authorisation for user ' . $user . ': ' . $headerAuthX[1], API_LOG); - Minz_Log::warning('Invalid API authorisation for user ' . $user . ': ' . $headerAuthX[1]); + Minz_Log::warning('Invalid API authorisation for user ' . $user); unauthorized(); } } else { |
