CodeQL 2.26.1でRust暗号値の誤検知を削減|nonce判定の変更点

CodeQL 2.26.1では、Rustで固定値を実行時データと組み合わせて生成したnonceや暗号用パラメータが、rust/hard-coded-cryptographic-valueによって誤検知されにくくなりました。算術演算、ビット演算、文字列追加がデータフロー上の「バリア」として扱われるようになったためです。

たとえば、固定の基準値に実行時カウンターを加えてnonceを作るコードや、固定接頭辞にリクエストIDを追加するコードは、従来より正確に判定されます。一方、固定値を直接使用するコードや、定数同士を演算しただけの値は、引き続き検出対象です。(The GitHub Blog)

目次

実行時データと結合したRustのnonceをCodeQL 2.26.1は誤検知する?

CodeQL 2.26.1で改善対象になった形式であれば、従来発生していた誤検知は減ります。

特に対象となるのは、ハードコードされた定数を次のような非定数データと組み合わせるケースです。

  • 関数の引数として受け取ったカウンター
  • 実行時に取得した連番
  • ランダム値
  • リクエストIDやセッションID
  • 実行中に変化する状態値

代表的なコードパターンと、CodeQL 2.26.1で期待される判定傾向は次のとおりです。

コードの形式2.26.1での判定傾向理由
let nonce = [0u8; 12];引き続き検出される最終的なnonceが完全な固定値
let nonce = 0x1000u64 + counter;従来の誤検知が減る固定値と実行時データの算術演算
let nonce = PREFIX ^ runtime_mask;従来の誤検知が減る固定値と実行時データのビット演算
nonce += request_id;従来の誤検知が減る固定接頭辞への可変文字列追加
let nonce = 0x1000u64 + 1;引き続き検出される両辺が定数で、結果も固定値
独自関数やマクロで値を組み立てる分析モデルによる今回の変更対象だけでは一律に判断できない

重要なのは、「固定値がコード中に含まれているか」だけでなく、最終的な暗号値が実行時データによって変化するかどうかです。CodeQL 2.26.1は、この違いを以前より正確に追跡します。

CodeQL 2.26.1で変更されたクエリ

今回修正されたのは、Rust向けの次のクエリです。

項目内容
クエリIDrust/hard-coded-cryptographic-value
検出対象ハードコードされたパスワード、鍵、IV、nonce、saltなど
クエリ種別path-problem
Severitywarning
Security severity9.8
Precisionhigh
標準コードスキャン対象

このクエリは、単純に文字列や数値リテラルを検索しているわけではありません。ハードコードされた定数をデータフローの出発点として扱い、その値が暗号処理に関係する引数まで到達するかを追跡します。(CodeQL)

バリアとは何か

CodeQLのデータフロー分析では、概念的に次の3要素を扱います。

  • Source:追跡を開始する値
  • Sink:問題となる値の到達先
  • Barrier:それ以上データフローを伝播させない境界

rust/hard-coded-cryptographic-valueの場合、数値リテラル、文字列リテラル、定数配列などがSourceになります。暗号処理に使われるkey、IV、nonce、salt、passwordなどがSinkです。

CodeQL 2.26.0以前では、固定値を実行時データと演算しても、固定値からSinkまでデータフローが続いていると判断されることがありました。

CodeQL 2.26.1では、次の操作が新たにバリアとして扱われます。

  • 算術演算
  • ビット演算
  • 算術複合代入
  • ビット複合代入
  • +や+=による文字列連結・追加

実装上も、BinaryArithmeticOperation、BinaryBitwiseOperation、AssignArithmeticOperation、AssignBitwiseOperationのオペランドがバリアとして定義されています。代表例は+、^、+=、^=です。

定数同士の演算は見逃さない

演算をバリアにすると、次のような固定値まで見逃してしまうように思えるかもしれません。

fn use_nonce(nonce: u64) {
    // 暗号処理
}

fn example() {
    let nonce = 0x1000u64 + 1;
    use_nonce(nonce);
}

しかし、CodeQL 2.26.1では、左右のオペランドが両方とも定数の場合、演算結果そのものを新しい定数Sourceとして扱います。

つまり、次の2つは区別されます。

let fixed_nonce = 0x1000u64 + 1;
let runtime_nonce = 0x1000u64 + runtime_counter;

前者は結果が常に同じなので、ハードコードされた暗号値として追跡されます。後者は実行時カウンターによって変化するため、固定値からのデータフローが演算部分で遮断されます。

この仕組みにより、単に検出範囲を狭めるのではなく、定数式の検出を維持しながら、非定数データを含む処理の誤検知を減らしています。

公開日を確認するときの注意点

GitHub Changelogでこの改善が案内されたのは2026年7月29日です。一方、CodeQL CLIの詳細な変更履歴では、CodeQL 2.26.1の日付が2026年7月15日と記載されています。

資料を確認するときは、7月29日をGitHub Changelog上の告知日、7月15日をCodeQL CLI変更履歴に記載されたバージョン日として区別すると混乱しません。どちらも同じrust/hard-coded-cryptographic-valueの改善について説明しています。(The GitHub Blog)

誤検知が減るRustコードの具体例

以下は、変更点を理解するために簡略化したコードです。実際の検出結果は、使用する暗号ライブラリのモデルや関数構成によって変わる可能性があります。

実行時カウンターを加算するnonce

fn encrypt_record(nonce: u64) {
    // nonceを使用する暗号処理
}

fn process_record(runtime_counter: u64) {
    let nonce = 0xA5A5_0000u64 + runtime_counter;
    encrypt_record(nonce);
}

0xA5A5_0000u64は固定値ですが、最終的なnonceはruntime_counterによって変化します。

以前のクエリでは、固定値からencrypt_recordのnonce引数までデータフローが続いていると判断され、ハードコード済みnonceとして報告される可能性がありました。

CodeQL 2.26.1では、+演算がバリアになります。固定値側から結果への追跡が止まり、今回の改善対象となる誤検知が抑制されます。

ビット演算で実行時データを組み合わせる

fn encrypt_record(nonce: u64) {
    // nonceを使用する暗号処理
}

fn process_record(runtime_bits: u64) {
    let nonce = 0xA5A5_0000_0000_0000u64 ^ runtime_bits;
    encrypt_record(nonce);
}

この例では、固定値とruntime_bitsをXORしています。

CodeQL 2.26.1はビット演算もバリアとして扱うため、固定値がそのままnonceとして使用されたというデータフローは成立しにくくなります。(The GitHub Blog)

固定接頭辞に可変文字列を追加する

fn submit_nonce(nonce: &str) {
    // nonceを使用する処理
}

fn process_request(request_id: &str) {
    let mut nonce = String::from("tenant-a:");
    nonce += request_id;

    submit_nonce(&nonce);
}

tenant-a:は固定接頭辞ですが、最終的な文字列には実行時のrequest_idが追加されています。

CodeQL 2.26.1では、+=による文字列追加もバリアの対象です。固定接頭辞だけを根拠に、最終的な文字列全体をハードコード済み暗号値と判断する従来の誤検知が減ります。

ただし、今回明示されているのは算術演算、ビット演算、文字列追加です。format!マクロ、独自の文字列ビルダー、ヘルパー関数、外部クレートを経由した値の生成まで、すべて同じように扱われるとは限りません。

CodeQL 2.26.1でもアラートが残るケース

アップデート後もrust/hard-coded-cryptographic-valueが表示される場合、必ずしも不具合ではありません。

暗号値を固定値のまま渡している

fn encrypt_record(nonce: [u8; 12]) {
    // 暗号処理
}

fn process_record() {
    let nonce = [0u8; 12];
    encrypt_record(nonce);
}

このコードでは、nonceが毎回同じです。実行時データとの演算もないため、引き続き正当な検出対象です。

固定されたkey、password、IV、nonce、saltを暗号処理に直接渡している場合は、アラートを誤検知として閉じるのではなく、値の生成・取得方法を見直す必要があります。

演算しているが両方とも定数である

let nonce = 1000u64 + 1;

演算子が含まれていても、結果は常に1001です。CodeQL 2.26.1では、このような定数式全体が定数Sourceとして認識されます。

「演算を入れれば検出されなくなる」という変更ではありません。

実行時データが最終値に反映されていない

次のようなコードでは、可変データを取得していても、実際に暗号処理へ渡しているのは固定値です。

fn encrypt_record(nonce: u64) {
    // 暗号処理
}

fn process_record(runtime_counter: u64) {
    let _calculated = 0x1000u64 + runtime_counter;

    encrypt_record(0x1000u64);
}

CodeQLが追跡するのは、変数が存在するかどうかではなく、SourceからSinkまでのデータフローです。未使用の可変値が近くにあっても、固定値を直接渡していれば検出されます。

今回のバリア対象ではない方法で値を生成している

次のような処理は、利用しているAPIやCodeQLのモデルによって結果が異なります。

  • format!で文字列を生成する
  • 複数のヘルパー関数を通す
  • 独自型のメソッドでnonceを構築する
  • シリアライザーやエンコーダーを経由する
  • 外部クレート内で値を変更する

アラートが残ったからといって、CodeQLを回避するためだけにコードを+や+=へ書き換えるべきではありません。まずアラートのデータフローパスを確認し、どの固定値がどのSinkまで到達していると判定されたのかを調べます。

誤検知が減っても暗号設計が安全とは限らない

CodeQL 2.26.1の変更は、rust/hard-coded-cryptographic-valueの判定精度を改善するものです。生成されたnonceや鍵の安全性を証明するものではありません。

たとえば、次のコードは固定値だけではないため、今回の誤検知削減対象になり得ます。

let nonce = 0x1000u64 + runtime_counter;

しかし、実運用では少なくとも次の点を確認する必要があります。

  • 同じ鍵で同じnonceが再利用されないか
  • プロセス再起動後にカウンターが初期値へ戻らないか
  • カウンターがオーバーフローしないか
  • 複数スレッドや複数サーバーで値が重複しないか
  • 暗号アルゴリズムが要求する長さや形式を満たすか
  • 変換や切り詰めによって異なる値が同じnonceにならないか

また、固定値に時刻や短いカウンターをXORしただけの鍵を、安全な暗号鍵と判断することもできません。

let key = FIXED_KEY ^ current_timestamp;

このようなコードでアラートが減る可能性があっても、鍵のエントロピー、予測可能性、鍵管理などは別途評価が必要です。

つまり、今回の変更で分かるのは「完全なハードコード値ではない」という点までです。「暗号学的に安全である」という結論には直結しません。

CodeQL 2.26.1の適用状況を確認する方法

環境によって、精度改善が自動的に反映されるか、手動更新が必要かが異なります。

利用環境対応
GitHub.comのCodeQL code scanning原則として新バージョンが自動展開される
CodeQL CLIを直接利用CLIとクエリを含むCodeQL bundleを更新する
GitHub Enterprise ServerGHESの対応状況または手動更新方法を確認する
バージョン固定したクエリパック固定されているパックのバージョンも確認する

GitHub.comのcode scanning

GitHubの案内では、CodeQLの新バージョンはGitHub.comのcode scanning利用者へ自動的に展開されます。そのため、標準的なDefault setupやAdvanced setupでは、通常、この修正のためだけにワークフローファイルを書き換える必要はありません。(The GitHub Blog)

既存アラートを確認するときは、新しいCodeQLが使われる状態で解析を再実行します。

複数のcode scanning構成が存在するリポジトリでは、古い構成の解析結果が残る場合があります。アラート状態を正しく更新するには、対象ブランチの各構成を再実行することが重要です。(GitHub Docs)

CodeQL CLIを直接利用している場合

使用中のCLIバージョンは、次のコマンドで確認できます。

codeql version

JSON形式で確認する場合は次のとおりです。

codeql version --format=json

codeql versionは、現在使用しているCodeQLツールチェーンのバージョンを表示する公式コマンドです。(GitHub Docs)

更新時は、CLI実行ファイルだけでなくCodeQL bundleを利用します。公式のbundleには、次のものが互換性を保った状態で含まれています。

  • CodeQL CLI
  • CodeQLのクエリとライブラリ
  • コンパイル済みクエリ

CLIだけを2.26.1以降へ変更し、古いRustクエリを別途参照している構成では、今回の改善が反映されない可能性があります。GitHubも、互換性と解析性能の観点からCodeQL bundleの使用を推奨しています。(GitHub Docs)

なお、CodeQL 2.26.1には利用者向けのCLIコマンド変更はありません。今回のRust修正は、Query Packsの「Minor Analysis Improvements」として提供されています。既存の解析コマンドを書き換えるのではなく、使用するbundleまたはクエリパックを更新する対応が中心です。(CodeQL)

GitHub Enterprise Serverを利用している場合

GitHubの2026年7月29日付Changelogでは、CodeQL 2.26.1の機能は将来のGHESリリースに含まれると案内されています。古いGHESでは、CodeQLの手動アップグレードが必要になる場合があります。(The GitHub Blog)

ただし、GHESではアプライアンスとの互換性があるCodeQL bundleを使う必要があります。GitHub.com向けの最新bundleを無条件に置き換えるのではなく、利用中のGHESバージョンに対応した手順を確認してください。

クエリパックを固定している場合

Advanced setupや外部CIで、クエリパックのバージョンを明示的に固定している場合は注意が必要です。

CodeQL CLIが新しくても、解析で古いRustクエリパックを使用していれば、rust/hard-coded-cryptographic-valueの旧ロジックが実行される可能性があります。

確認すべき対象は、CLIバージョンだけではありません。

  • CodeQL bundleのバージョン
  • 実際に読み込まれたRustクエリパック
  • qlpack.lock.ymlなどの固定情報
  • GitHub Actionsの実行ログ
  • 社内ミラーやキャッシュに保存されたbundle

公開済みクエリパックは、パックまたはCLIをアップグレードするまで同じ解析結果を再現できる仕組みです。そのため、再現性のために固定したバージョンが、精度改善の適用を妨げていないか確認します。(GitHub Docs)

既存のRust暗号値アラートを確認する手順

CodeQL 2.26.1への更新後は、次の順序でトリアージすると効率的です。

クエリIDを確認する

対象アラートが次のIDであることを確認します。

rust/hard-coded-cryptographic-value

別のクエリによるアラートであれば、今回の変更は関係ありません。

データフローパスを確認する

アラート画面で、固定値から暗号値の使用箇所までのパスを確認します。

特に見るべきなのは次の点です。

  • Sourceになっているリテラルや定数
  • 算術演算またはビット演算の有無
  • 演算のもう一方が実行時データか
  • 最終的に渡される変数
  • Sinkとして認識された引数

非定数データが本当に反映されているか確認する

変数名だけで判断せず、値の由来を確認します。

fn build_nonce(counter: u64) -> u64 {
    0x1000u64 + counter
}

このcounterは関数引数なので、通常は非定数データです。

一方、呼び出し元が常に固定値を渡している場合、アプリケーション全体としては実質的に固定されたnonceになる可能性があります。

let nonce = build_nonce(1);

CodeQLの今回の変更だけで、呼び出し元を含む暗号設計の妥当性まで保証されるわけではありません。

使用バージョンとクエリパックを確認する

CLI環境ではcodeql versionを確認します。GitHub Actionsでは実行ログを確認し、古いbundleや社内キャッシュが使われていないか調べます。

最新条件で解析を再実行する

古い解析結果を見ながら判断するのではなく、更新後のCodeQLで再解析します。

再解析後に結果が出なくなれば、従来の誤検知だった可能性が高いと判断できます。

アラートが残る場合は直接固定値を再確認する

アラートが残った場合は、次のいずれかを疑います。

状況対応
固定値を直接渡しているランダム生成や安全な鍵管理へ変更する
定数同士を演算している結果は固定値なので実装を見直す
古いクエリパックを使用しているbundleまたはパックを更新する
独自APIを経由しているデータフローパスとモデルを確認する
nonceの安全性を判断できない重複、再起動、並列実行を含めて設計レビューする
CodeQLの追跡が不正確と思われる根拠を記録してから誤検知として扱う

最初からアラートをdismissするのではなく、バージョン更新、再解析、データフローパスの確認を先に行うことが重要です。

CodeQL 2.26.1のRust暗号値改善で取るべき対応

CodeQL 2.26.1では、rust/hard-coded-cryptographic-valueが算術演算、ビット演算、文字列追加をバリアとして扱うようになりました。

これにより、固定値を実行時カウンターで増分するnonce、固定値と実行時データをXORした値、固定接頭辞へ可変文字列を追加した値などで、従来発生していた誤検知が減ります。

一方、固定値の直接使用や定数同士の演算は、引き続き検出されます。アラートが消えた場合も、nonceの重複、カウンターの初期化、鍵管理、値の予測可能性といった暗号設計上の確認は必要です。

既存アラートがある場合は、まずCodeQLまたはRustクエリパックのバージョンを確認し、最新条件で解析を再実行してください。そのうえで、SourceからSinkまでのデータフローパスを確認すれば、実装上の問題と旧バージョン由来の誤検知を切り分けられます。

この記事を書いた人

実務の現場で詰まりがちなポイントを地図にするITブログ「IT trip」を運営。Windows/Office(Teams・Excel)からSQL、サーバ運用、ガジェットまで、再現性のある手順と“なぜそうなるか”を丁寧に解説します。読んだらすぐ試せること、そして迷った人の次の一歩が見えることを大切にしています。

コメント

コメントする

目次