🔞

#[SensitiveParameter] の挙動の確認

に公開

SensitiveParameter アトリビュートとはなにか

関数やメソッドのパラメータに #[SensitiveParameter] アトリビュートを付与することで、そのパラメータの値をスタックトレースから隠蔽することができる機能です。

https://www.php.net/manual/ja/class.sensitiveparameter.php

例えば、PDO のコンストラクタでは$passwordを受け取っていますが、コンストラクタを呼び出した時に行われるデータベースへの接続で失敗すると、スタックトレースにデータベースのパスワードが出力されてしまいます。

これらの課題を解決するために、 #[\SensitiveParameter] アトリビュートは PHP 8.2 から導入されました。

https://wiki.php.net/rfc/redact_parameters_in_back_traces

挙動の確認

こちらのコミットのテストを読んでいきます。

https://github.com/php/php-src/commit/90851977348cbb8c65fce19a1670868422414fae

スタックトレースの出力は隠蔽される

debug_print_backtracedebug_backtraceException::getTraceなどのスタックトレース中では値が隠蔽される。

--FILE--
<?php

function test(#[SensitiveParameter] $sensitive)
{
    debug_print_backtrace();
}

test('sensitive');
?>
--EXPECTF--
#0 %ssensitive_parameter.php(10): test(Object(SensitiveParameterValue))

スタックトレースではない出力は隠蔽されない

var_dumpprint_rなどのスタックトレースではない出力では値は隠蔽されない。

--FILE--
<?php

function test(#[SensitiveParameter] $sensitive)
{
    var_dump($sensitive);
}

test('sensitive');
?>
--EXPECTF--
string(8) "sensitive"

可変長引数にも対応している

...$argsのような可変長引数の場合でも#[SensitiveParameter]は全てにつきます。

--FILE--
<?php

function test(
    #[SensitiveParameter] ...$args
) {
    debug_print_backtrace();
}

test('foo', 'bar', 'baz');

?>
--EXPECTF--
#0 %ssensitive_parameter_variadic_arguments.php(12): test(Object(SensitiveParameterValue), Object(SensitiveParameterValue), Object(SensitiveParameterValue))

#[SensitiveParameter]はその関数内でしか有効ではない

呼び出し元のメソッドは#[SensitiveParameter]を付与していても、呼び出し先のメソッドは付与していない場合、呼び出し先のメソッドでは値は隠蔽されない。
その逆も同様。

--FILE--
<?php

function test(#[SensitiveParameter] $sensitive)
{
    test2($sensitive);
}

function test2($sensitive)
{
    debug_print_backtrace();
}

test('sensitive');
?>
--EXPECTF--
#0 %ssensitive_parameter.php(13): test2('sensitive')
#1 %ssensitive_parameter.php(21): test(Object(SensitiveParameterValue))
--FILE--
<?php

function test($sensitive)
{
    test2($sensitive);
}

function test2(#[SensitiveParameter] $sensitive)
{
    debug_print_backtrace();
}

test('sensitive');
?>
--EXPECTF--
#0 %ssensitive_parameter.php(13): test2(Object(SensitiveParameterValue))
#1 %ssensitive_parameter.php(21): test('sensitive')

配列やオブジェクトでも#[SensitiveParameter]は有効

引数に配列やオブジェクトを渡した場合、debug_print_backtraceではArrayObjectと表示される。
しかし、debug_backtraceargsを表示すると隠蔽したい値も表示されてしまう。

--FILE--
readonly class Password
{
    public function __construct(
       #[SensitiveParameter] public string $sensitive,
    ) {}
}

function test($sensitive)
{
    debug_print_backtrace();
    var_dump(debug_backtrace()[0]['args'][0]);
}

test(['password' => 'sensitive']);
test(new Password('sensitive'));
?>
--EXPECTF--
#0 %ssensitive_parameter.php(24): test(Array)
array(1) {
  ["password"]=>
  string(9) "sensitive"
}
#0 %ssensitive_parameter.php(25): test(Object(Password))
object(Password)#2 (1) {
  ["sensitive"]=>
  string(9) "sensitive"
}

ちゃんと#[SensitiveParameter]を付けることで、debug_backtraceargsでも隠蔽される。

--FILE--
readonly class Password
{
    public function __construct(
       #[SensitiveParameter] public string $sensitive,
    ) {}
}

function test(#[SensitiveParameter] $sensitive)
{
    debug_print_backtrace();
    var_dump(debug_backtrace()[0]['args'][0]);
}

test(['password' => 'sensitive']);
test(new Password('sensitive'));
?>
--EXPECTF--
#0 %ssensitive_parameter.php(24): test(Array)
object(SensitiveParameterValue)#2 (0) {
}
#0 %ssensitive_parameter.php(25): test(Object(Password))
object(SensitiveParameterValue)#2 (0) {
}

使われどころ

接続情報の隠蔽

PDOなど、データベース接続時のクレデンシャル情報を隠蔽する。

public PDO::__construct(
    string $dsn,
    ?string $username = null,
    #[\SensitiveParameter] ?string $password = null,
    ?array $options = null
)

https://www.php.net/manual/ja/pdo.construct.php

セキュリティ関連のライブラリの平文の値を隠蔽

OpenSSLやSodiumなどのセキュリティ関連のライブラリで、平文の値を隠蔽する。

openssl_encrypt(
    #[\SensitiveParameter] string $data,
    string $cipher_algo,
    #[\SensitiveParameter] string $passphrase,
    int $options = 0,
    string $iv = "",
    string &$tag = null,
    string $aad = "",
    int $tag_length = 16
): string|false

https://www.php.net/manual/ja/function.openssl-encrypt.php

hash_equals(#[\SensitiveParameter] string $known_string, #[\SensitiveParameter] string $user_string): bool

https://www.php.net/manual/ja/function.hash-equals.php

トークンなど認証情報の隠蔽

SymfonyやLaravelなどのフレームワークで、トークン等の認証情報を隠蔽する。

    public function validate(#[\SensitiveParameter] array $credentials = [])
    {
        return ! is_null((new static(
            $this->callback, $credentials['request'], $this->getProvider()
        ))->user());
    }

https://github.com/laravel/framework/blob/fba3b98d1ff22f75ea4bd70f8200cfa0b6a3f43d/src/Illuminate/Auth/RequestGuard.php#L61-L72

おわり

PHP 8.2 から追加された #[\SensitiveParameter] アトリビュートは、スタックトレースに出力される値を隠蔽することが出来ます。
機密情報を扱う関数やメソッドでは可能な限り #[\SensitiveParameter] を付与することがオススメです。

また、PHP標準のExtensionや、ライブラリ・フレームワークなどで、#[SensitiveParameter]が使われている関数を使用する際は、呼び出し元となるメソッドにも#[SensitiveParameter]を付与するようにしましょう。

GitHubで編集を提案

Discussion