From 6cb2c8b2d9a4e954388ffb25b42c225da2a2a4be Mon Sep 17 00:00:00 2001 From: Kairat Jenishev Date: Tue, 10 Dec 2024 23:44:41 +0600 Subject: [PATCH 1/4] An ability to have wildcards in `yii\log\Target::$maskVars` array #20295 --- framework/CHANGELOG.md | 1 + framework/log/Target.php | 58 ++++++++++++++++++++++++++++-- tests/framework/log/TargetTest.php | 39 ++++++++++++++++++++ 3 files changed, 95 insertions(+), 3 deletions(-) diff --git a/framework/CHANGELOG.md b/framework/CHANGELOG.md index 60cc9c34596..18ac40550cc 100644 --- a/framework/CHANGELOG.md +++ b/framework/CHANGELOG.md @@ -22,6 +22,7 @@ Yii Framework 2 Change Log - Bug #20140: Fix compatibility with PHP 8.4: calling `session_set_save_handler()` (Izumi-kun) - New #20185: Add `BackedEnum` support to `AttributeTypecastBehavior` (briedis) - Bug #17365: Fix "Trying to access array offset on null" warning (xcopy) +- Enh #20295: An ability to have wildcards in `yii\log\Target::$maskVars` array (xcopy) 2.0.51 July 18, 2024 -------------------- diff --git a/framework/log/Target.php b/framework/log/Target.php index b2335949dc8..8e87160c496 100644 --- a/framework/log/Target.php +++ b/framework/log/Target.php @@ -11,6 +11,7 @@ use yii\base\Component; use yii\base\InvalidConfigException; use yii\helpers\ArrayHelper; +use yii\helpers\StringHelper; use yii\helpers\VarDumper; use yii\web\Request; @@ -161,6 +162,54 @@ public function collect($messages, $final) } } + /** + * Flattens a multidimensional array into a one-dimensional array. + * + * This method recursively traverses the input array and concatenates the keys + * to form a new key in the resulting array. + * + * Example: + * + * ```php + * $array = [ + * 'A' => [1, 2], + * 'B' => [ + * 'C' => 1, + * 'D' => 2, + * ], + * 'E' => 1, + * ]; + * $result = \yii\log\Target::flatten($array); + * // result will be: + * // [ + * // 'A.0' => 1 + * // 'A.1' => 2 + * // 'B.C' => 1 + * // 'B.D' => 2 + * // 'E' => 1 + * // ] + * ``` + * + * @param array $array the input array to be flattened. + * @param string $prefix the prefix to be added to each key in the resulting array. + * + * @return array the flattened array. + */ + private static function flatten($array, $prefix = ''): array + { + $result = []; + + foreach ($array as $key => $value) { + if (is_array($value)) { + $result = array_merge($result, self::flatten($value, $prefix . $key . '.')); + } else { + $result[$prefix . $key] = $value; + } + } + + return $result; + } + /** * Generates the context information to be logged. * The default implementation will dump user information, system variables, etc. @@ -169,9 +218,12 @@ public function collect($messages, $final) protected function getContextMessage() { $context = ArrayHelper::filter($GLOBALS, $this->logVars); + $items = self::flatten($context); foreach ($this->maskVars as $var) { - if (ArrayHelper::getValue($context, $var) !== null) { - ArrayHelper::setValue($context, $var, '***'); + foreach ($items as $key => $value) { + if (StringHelper::matchWildcard($var, $key, ['caseSensitive' => false])) { + ArrayHelper::setValue($context, $key, '***'); + } } } $result = []; @@ -292,7 +344,7 @@ public static function filterMessages($messages, $levels = 0, $categories = [], */ public function formatMessage($message) { - list($text, $level, $category, $timestamp) = $message; + [$text, $level, $category, $timestamp] = $message; $level = Logger::getLevelName($level); if (!is_string($text)) { // exceptions may not be serializable if in the call stack somewhere is a Closure diff --git a/tests/framework/log/TargetTest.php b/tests/framework/log/TargetTest.php index 7548092b788..06eb74d38fe 100644 --- a/tests/framework/log/TargetTest.php +++ b/tests/framework/log/TargetTest.php @@ -345,6 +345,45 @@ public function testFlushingWithProfilingEnabledAndOverflow() $logger->log('token.b', Logger::LEVEL_PROFILE_END, 'category'); $logger->log('token.a', Logger::LEVEL_PROFILE_END, 'category'); } + + public function testWildcardsInMaskVars() + { + $keys = [ + 'PASSWORD', + 'password', + 'password_repeat', + 'repeat_password', + 'repeat_password_again', + '1password', + 'password1', + ]; + + $password = '!P@$$w0rd#'; + + $items = array_fill_keys($keys, $password); + + $GLOBALS['_TEST'] = array_merge( + $items, + ['a' => $items], + ['b' => ['c' => $items]], + ['d' => ['e' => ['f' => $items]]], + ); + + $target = new TestTarget([ + 'logVars' => ['_SERVER', '_TEST'], + 'maskVars' => [ + // option 1: exact value(s) + '_SERVER.DOCUMENT_ROOT', + // option 2: pattern(s) + '_TEST.*password*', + ] + ]); + + $message = $target->getContextMessage(); + + $this->assertStringContainsString("'DOCUMENT_ROOT' => '***'", $message); + $this->assertStringNotContainsString($password, $message); + } } class TestTarget extends Target From 80567480f4356bc9d3c2da201a2eb7d4b562d485 Mon Sep 17 00:00:00 2001 From: Alexander Makarov Date: Wed, 11 Dec 2024 02:17:23 +0300 Subject: [PATCH 2/4] Update framework/CHANGELOG.md --- framework/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/CHANGELOG.md b/framework/CHANGELOG.md index 7bcfe78f4b1..a76e88c4d65 100644 --- a/framework/CHANGELOG.md +++ b/framework/CHANGELOG.md @@ -22,7 +22,7 @@ Yii Framework 2 Change Log - Bug #20140: Fix compatibility with PHP 8.4: calling `session_set_save_handler()` (Izumi-kun) - New #20185: Add `BackedEnum` support to `AttributeTypecastBehavior` (briedis) - Bug #17365: Fix "Trying to access array offset on null" warning (xcopy) -- Enh #20295: An ability to have wildcards in `yii\log\Target::$maskVars` array (xcopy) +- Enh #20295: Add an ability to have wildcards in `yii\log\Target::$maskVars` array (xcopy) - Bug #20296: Fix broken enum test (briedis) 2.0.51 July 18, 2024 From 47e4c111df1358e0432d8bb2df109bbaf279a779 Mon Sep 17 00:00:00 2001 From: Kairat Jenishev Date: Thu, 12 Dec 2024 22:59:52 +0600 Subject: [PATCH 3/4] Update documentation and annotation related to `$maskVars` property #20295 --- docs/guide/runtime-logging.md | 10 ++++++++-- framework/log/Target.php | 5 +++++ 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/docs/guide/runtime-logging.md b/docs/guide/runtime-logging.md index 041150abd2c..7bccee119c1 100644 --- a/docs/guide/runtime-logging.md +++ b/docs/guide/runtime-logging.md @@ -217,14 +217,20 @@ Or if you want to implement your own way of providing context information, you m [[yii\log\Target::getContextMessage()]] method. In case some of your request fields contain sensitive information you would not like to log (e.g. passwords, access tokens), -you may additionally configure `maskVars` property. By default, the following request parameters will be masked with `***`: +you may additionally configure `maskVars` property, which can contain both exact values and (case-insensitive) patterns. By default, +the following request parameters will be masked with `***`: `$_SERVER[HTTP_AUTHORIZATION]`, `$_SERVER[PHP_AUTH_USER]`, `$_SERVER[PHP_AUTH_PW]`, but you can set your own: ```php [ 'class' => 'yii\log\FileTarget', 'logVars' => ['_SERVER'], - 'maskVars' => ['_SERVER.HTTP_X_PASSWORD'] + 'maskVars' => [ + '_SERVER.HTTP_X_PASSWORD', + '_SERVER.*_SECRET', // matches all ending with "_SECRET" + '_SERVER.SECRET_*', // matches all starting with "SECRET_" + '_SERVER.*SECRET*', // matches all containing "SECRET" + ] ] ``` diff --git a/framework/log/Target.php b/framework/log/Target.php index 8e87160c496..a56d9efbf00 100644 --- a/framework/log/Target.php +++ b/framework/log/Target.php @@ -92,6 +92,11 @@ abstract class Target extends Component * - `var` - `var` will be logged as `***` * - `var.key` - only `var[key]` will be logged as `***` * + * In addition, this property accepts (case-insensitive) patterns. For example: + * - `_SERVER.*_SECRET` matches all ending with `_SECRET`, such as `$_SERVER['TOKEN_SECRET']` etc. + * - `_SERVER.SECRET_*` matches all starting with `SECRET_`, such as `$_SERVER['SECRET_TOKEN']` etc. + * - `_SERVER.*SECRET*` matches all containing `SECRET` i.e. both of the above. + * * @since 2.0.16 */ public $maskVars = [ From 15e22915c7d5d6d0f9e16b87380510e6ccc1cbc3 Mon Sep 17 00:00:00 2001 From: Kairat Jenishev Date: Thu, 12 Dec 2024 23:21:57 +0600 Subject: [PATCH 4/4] Added documentation in Russian language regarding the `$maskVars` property #20295 --- docs/guide-ru/runtime-logging.md | 17 +++++++++++++++++ docs/guide/runtime-logging.md | 2 +- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/docs/guide-ru/runtime-logging.md b/docs/guide-ru/runtime-logging.md index 2f45900e2ca..e805507097b 100644 --- a/docs/guide-ru/runtime-logging.md +++ b/docs/guide-ru/runtime-logging.md @@ -173,6 +173,23 @@ return [ При задании значением свойства `logVars` пустого массива, общая информация не будет выводиться. Для определения собственного алгоритма подключения общей информации, следует переопределить метод [[yii\log\Target::getContextMessage()]]. +Если некоторые из полей вашего запроса содержат конфиденциальную информацию, которую вы не хотели бы логировать (например, пароли, токены доступа), +вы можете дополнительно настроить свойство `maskVars`, которое может содержать как точные значения, так и шаблоны (без учета регистра). +По умолчанию следующие параметры запроса будут замаскированы с помощью `***`: +`$_SERVER[HTTP_AUTHORIZATION]`, `$_SERVER[PHP_AUTH_USER]`, `$_SERVER[PHP_AUTH_PW]`, но вы можете задать свои собственные. Например: + +```php +[ + 'class' => 'yii\log\FileTarget', + 'logVars' => ['_SERVER'], + 'maskVars' => [ + '_SERVER.HTTP_X_PASSWORD', + '_SERVER.*_SECRET', // соответствует всем, заканчивающимся на "_SECRET" + '_SERVER.SECRET_*', // соответствует всем, начинающимся с "SECRET_" + '_SERVER.*SECRET*', // соответствует всем содержащим "SECRET" + ] +] +``` ### Уровень отслеживания выполнения кода diff --git a/docs/guide/runtime-logging.md b/docs/guide/runtime-logging.md index 7bccee119c1..5185c0916e7 100644 --- a/docs/guide/runtime-logging.md +++ b/docs/guide/runtime-logging.md @@ -219,7 +219,7 @@ Or if you want to implement your own way of providing context information, you m In case some of your request fields contain sensitive information you would not like to log (e.g. passwords, access tokens), you may additionally configure `maskVars` property, which can contain both exact values and (case-insensitive) patterns. By default, the following request parameters will be masked with `***`: -`$_SERVER[HTTP_AUTHORIZATION]`, `$_SERVER[PHP_AUTH_USER]`, `$_SERVER[PHP_AUTH_PW]`, but you can set your own: +`$_SERVER[HTTP_AUTHORIZATION]`, `$_SERVER[PHP_AUTH_USER]`, `$_SERVER[PHP_AUTH_PW]`, but you can set your own. For example: ```php [