Perl Advent Calenderなんと2024年になってもやってます。本当にめでたいですね!
さて、掲題のString::Secretは2020年にリリースしたっきりのCPANモジュールですが、(たぶん)そういえば紹介してなかったなと思ったのでこの機会に紹介しようと思います。完全に忘れていてネタがなかったんです。
SYNOPSIS
なにしてくれるやつなのよということでSYNOPSIS。
use String::Secret;
use String::Compare::ConstantTime;
use JSON::PP ();
my $secret = String::Secret->new('mysecret');
# safe secret for logging
MyLogger->warn("invalid secret: $secret"); # oops! but the secret is hidden: "invalid secret: ********"
# and safe secret for serialization
# MyLogger->warn("invalid secret: ".JSON::PP->new->convert_blessed->encode({ secret => $secret })); # oops! but the secret is hidden: invalid secret: {"secret":"********"}
unless (String::Compare::ConstantTime::equals($secret->unwrap, SECRET)) {
die "secret mis-match";
}
# and can it convert to serializable
MyDB->credentials->new(
id => 'some id',
secret => $secret->to_serializable, # or $secret->unwrap
)->save();
要はパスワードとかメールアドレスとか下手にうっかり永続化されてほしくないシークレットな情報を扱うとき、それらがうっかりログなどに残らないようサポートしてくれるくんです。
"password: $password"
みたいにしちゃったとき、$password
がString::Secretのインスタンスだったら"password: ********"
相当にマスクしてくれます。
同様に、JSONに埋めてエンコードしちゃったときにconvert_blessed
をつけてたら{ secret => $secret }
を{"secret":"********"}
にしてくれるし、同様にallow_blessed
なら{"secret":null}
にしてくれます。allow_tags
でも{"secret":("String::Secret")[********]}
と同様にマスクしてくれるので安心安全です。
同様に、CBOR、Serial、Storableに対してもマスクした値をシリアライズするため安全です。
もちろん、Data::DumperやData::Printer(DDP)に対しても安全です。たとえば、Dumper $secret
は $VAR1 = bless( do{\(my $o = '********')}, 'String::Secret' );
になります。
こんな感じで、出力に絡むだいたいのものに対して対応しています。
なお、$secret->unwrap
のようにunwrap
メソッドを呼び出すことで本当の中身を取り出すことができます。
仕組み
インサイドアウトオブジェクト、演算子オーバーロードというテクニックを使います。
インサイドアウトオブジェクト
この記事が詳しいので説明を譲ります。
要は、このテクニックを使うと外側からは特殊な黒魔術を駆使したりでもしない限りはアクセスできない領域にインスタンスの持つ値を保存することができるので、Data::Dumperとかでも中身が漏れない。という感じです。
演算子オーバーロード
Perlの演算子オーバーロードではblessされたオブジェクトを特定のコンテキストで評価したときに、任意のメソッドを呼び出すといった処理が記述できます。
use overload
'""' => 'to_string',
fallback => 1;
こんな感じで記述することによって文字列コンテキストで評価されるとto_string
メソッドが呼び出されるようになります。
あとはto_string
で"********"
を返してあげれば文字列展開されてもマスクされてる感のあるテキストになってくれて安心というわけです。
永続化層のサポート
いうて永続化層に保存したいときまでマスクされては困る。という場面もあるでしょう。
そんなときのために String::Secret::Serializable
というやつも用意しています。
これは中身を保ったままシリアライズできるという点だけが異なります。Data::Dumperや文字列展開に対しては同様に保護ができます。
それぞれ、$secret->to_serializable
あるいはString::Secret->from_serializable($secret)
というような感じで相互変換ができます。
たとえば永続化層への保存をする場面でto_serializable
して、永続化層から取り出したらfrom_serializable
するといった使い方が可能です。
まとめ
Perlはこういうの簡単に作れて便利!明日は @mackee_w さんです!