aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Alexandre Alapetite <alexandre@alapetite.fr> 2023-01-11 23:27:14 +0100
committerGravatar GitHub <noreply@github.com> 2023-01-11 23:27:14 +0100
commit075cf4c800063e3cc65c3d41a9c23222e8ebb554 (patch)
tree6fb7d9c66fdbafea83f160c9043d9fd688844c1b
parentc75baefe40952e6ae80aa8570c0acfc9baf7d997 (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.conf2
-rwxr-xr-xcli/sensitive-log.sh9
-rw-r--r--lib/lib_rss.php25
-rw-r--r--p/api/fever.php11
-rw-r--r--p/api/greader.php26
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 {