世界を動かす技術を、日本語で。

base64エンコードされたJSON、証明書、プライベートキーの検出

2025年8月6日原文(ergaster.org)

概要

  • base64エンコードされたJSON や証明書の見分け方を解説
  • 先頭の文字列から エンコード内容 を推測する方法
  • PEM形式証明書 ・秘密鍵の識別例
  • 注意点 や誤検出パターンも紹介
  • シンプルで便利な 小技 の共有

base64エンコード文字列から内容を推測する技

  • homelabで 暗号化ファイル を調査
  • ファイル内に key_provider.pbkdf2.password_key のようなbase64文字列を発見
  • 同僚のアドバイスで base64デコード を試した結果、 JSON形式 の鍵情報を取得
  • 例:
    • echo "eyJzYW..." | base64 -d
    • 出力:{"salt":"...","iterations":600000,"hash_function":"sha512","key_length":32}
  • JSONがbase64エンコード されている場合、先頭がeyで始まるケースが多い
    • ey{"のbase64表現
  • 追加例:
    • echo "{\"" | base64eyIK
    • echo "{\"s" | base64eyJzCg==
    • 先頭がeyなら base64 JSON の可能性が高い

PEM証明書や秘密鍵のbase64判別

  • 証明書や秘密鍵 (PEM形式)は-----BEGIN CERTIFICATE-----などで始まる
  • これをbase64エンコードすると先頭がLSになる
    • echo -en "-----BEGIN CERTIFICATE-----" | base64LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0t
  • LS---(ハイフン3つ)のbase64表現
  • 秘密鍵やその他PEMデータ も同様に判別可能

注意点と誤検出パターン

  • YAMLファイル---(ドキュメント開始記号)も同じくLS0tLS0Kで始まる
    • echo "---\n" | base64LS0tLS0K
  • PEM形式以外 のデータもLSで始まる場合があるため、 完全な判別法ではない
  • base64の先頭文字列 だけで内容を断定せず、必要に応じて デコードで確認 推奨

まとめと小技の活用

  • base64エンコード文字列の先頭 を見るだけで、ある程度 中身を推測 できる
  • eyJSONLSPEM証明書/秘密鍵やYAML
  • 完全な判定法ではない が、 素早い調査やデバッグ に有効
  • Davide、Denis、tyzbit によるシンプルかつ便利なテクニック

Hackerたちの意見

Base64の準固定ポイントがあるよ: $ echo -n Vm0 | base64 Vm0w これは一文字ずつ無限に拡張できるけど、常に何らかのサフィックスが付くんだ。

サフィックスは入力に応じて長くなるから、どんどん面白くなくなっていくよ。(出力は必ず入力の8/6のサイズになるから、サフィックスは常に長さに33%を追加するんだ。)

参考までに、クワジ固定点をゼロから生成するプログラム:

#!/usr/bin/env python3
import base64

def len_common_prefix(a, b):
    assert len(a) == length
    return tmp[:length]

print(tmp[:l].decode('ascii'), tmp[l:].decode('ascii'), sep='\v')
# バッファの終わりを超えてスライスすると、Pythonでは安全に切り捨てられる。
start = tmp[:l*4//3+4]
# TODO これって理想的なの?
if __name__ == '__main__':
    final = calculate_quasi_fixed_point(b'\0', 80)
    print(final.decode('ascii'))

最終的にはこれが生成される: Vm0wd2QyUXlVWGxWV0d4V1YwZDRWMVl3WkRSV01WbDNXa1JTVjAxV2JETlhhMUpUVmpBeFYySkVUbGho

逆に言うと、それは尾を食うユニクインって呼ぶの?

これ、あんまり好きじゃないな。無駄に感じる。JWTも同じことしてるし。この例でも、文字列(ソルト)を二重にBase64エンコードしてる。jsonのようなものが本当にないのが残念だね。みんなそれを使って書けるのに。protobufみたいなものが、スキーマなしで書きやすくて読みやすければいいのに。

構造を説明するヘッダーにn*field_countを犠牲にする必要があるね。許可されるタイプも定義しないと。

すべてがそれを話し、書くことができる。ASN.1は超いい感じだよ -- すべてがそれを話し、ツールも素晴らしい(逃げて隠れる)

これの何が悪いの?Base64の目的はデータ、特にバイナリデータを限られたASCII文字のセットにエンコードして、テキストベースのプロトコルでの送信を可能にすることなんだ。暗号ライブラリでもオブfuscationツールでもないよ。機密データをBase64でエンコードしたり、JWTペイロードに機密データを含めたりするのは、最初に暗号化されていない限り避けるべきだよ。

本当に残念だけど、jsonのようなものは他にないんだよね。messagepackやcborはjsonに似てるけど(スキーマなしで、似たようなプリミティブ型)、バイナリデータもサポートしてる。bsonも似たような選択肢だね。どれも多くの言語で実装があって、大きな成熟したプロジェクトで使われてるよ。

protobufみたいなものがスキーマなしで書きやすくて読みやすかったらいいのにね。もし汎用的なバイナリの階層型長さ値エンコーディングが欲しいなら、https://en.wikipedia.org/wiki/Interchange_File_Format を考えたことある?広くサポートされているIFFライブラリがあるわけじゃなくて、フォーマットがシンプルすぎて、言語にバイト配列型があれば、バグのないIFFエンコーダ/デコーダを5分で実装できるんだ。 (だから、JSONやXMLのライブラリのような汎用的なIFFメタフォーマットライブラリがないんだよね。「あまりにもシンプルすぎて、みんなが私のライブラリに依存するのを面倒にしたくない」から、みんな自分のIFFベースの具体的なファイルフォーマットのパーサーやジェネレーターの一部としてIFFエンコーディング/デコーディングを実装してる。)IFFは何に使われてるかというと?AIFF、RIFF(だからWAV、AVI、ANI、そして驚くべきことにWebP)、JPEG2000、PNG [ちょっとした調整あり]… • それに、ISO Base Media File Format(「BMFF」)という子孫メタフォーマットもあって、これによってMP4、MOV、HEIF/HEICも汎用的なIFFパーサーで解析できるよ(ただし、BMFF特有のパーサーを使わないと、チャンクボディからメタデータフィールドを取り出すのを見逃すかもしれないけど)。 • それから、https://en.wikipedia.org/wiki/Extensible_Binary_Meta_Languag...(「EBML」)という代替案もあって、これは基本的にIFFだけど、TLVの「型」と「長さ」の部分を可変長整数でエンコードしてる(https://matroska-org.github.io/libebml/specs.htmlを参照)。これは現在、Matroska(MKV)フォーマットのメタフォーマットとして使われてるよ。ちょっと複雑すぎて、独立した汎用コーデックライブラリ(https://github.com/Matroska-Org/libebml)もある。もしディスクに構造化されたバイナリデータをダンプしたいなら、IFFチャンクをダンプ/エクスポート/送信ロジックの中で手動で生成するのが一番だと思う。例えば、printfの呼び出しの中でCSVを手動で出力するのと同じように。「これはIFFベースのフォーマットです」と言うか、.iff拡張子を付けるか、application/x-iffとして送信すれば、エコシステムはそれで動くはずだよ。(JSONと同じように、IFFチャンクに説明的な名前を付ければ、みんなは文脈からチャンクが「何を意味するか」を推測できると思うよ。スキーマドキュメントは必要ない。)

仕事でJWTサポートライブラリを作ったよ(https://github.com/geldata/gel-rust/tree/master/gel-jwt)。JWTは全部「eyyyyyy」って頭の中で聞こえるって確認できるよ。

ええ、笑った

Hacker Newsで議論の続きを見る