Aikido

深度保護なしに再帰を避けるべき理由

バグのリスク

ルール
避ける 再帰を 再帰を 再帰の 再帰防止策なし。
再帰 深さ保護なし。 適切な 深さ 深さの リスク スタック
スタック および 生成 DoS 脆弱性を から 悪意のある
入力から 再帰 を 再帰の 再帰の 制限 および 適切な
境界 チェック は 許容されます。

言語サポート: 45以上

はじめに

深度制限のない再帰関数はコールスタックを使い果たし、クラッシュを引き起こす可能性があります。深くネストされたJSONオブジェクトや循環データ構造のような悪意のある入力は、意図的に無限再帰をトリガーする可能性があります。単一の細工されたリクエストは、スタック制限を超過することでサービスをクラッシュさせ、エクスプロイトが容易なサービス拒否脆弱性を生み出します。

なぜ重要なのか

セキュリティ上の影響(DoS攻撃): 攻撃者は、深い再帰をトリガーする入力を巧妙に作成し、アプリケーションをクラッシュさせる可能性があります。深くネストされたJSON、XML、またはリンクされたデータ構造は、一般的な攻撃ベクトルです。単一の悪意のあるリクエストがスタックを使い果たし、すべてのユーザーに対してサービス全体を停止させます。

システムの安定性: スタックオーバーフローエラーは、グレースフルデグラデーションなしにプロセスを即座にクラッシュさせます。本番環境では、これはリクエストの破棄、トランザクションの中断、およびサービス利用不可を意味します。回復にはアプリケーション全体の再起動が必要です。

リソース枯渇: 無限再帰はスタックメモリを指数関数的に消費します。各再帰呼び出しはスタックフレームを追加し、深い再帰チェーンは数メガバイトのメモリを消費する可能性があります。これは同じサーバー上の他のプロセスに影響を与え、メモリ不足の状態を引き起こす可能性があります。

コード例

❌ 非準拠:

function processNestedData(obj) {
    if (typeof obj !== 'object' || obj === null) {
        return obj;
    }

    const result = {};
    for (const key in obj) {
        result[key] = processNestedData(obj[key]);
    }
    return result;
}

誤っている理由: 深度制限がないため、攻撃者はスタック制限を超える深くネストされたオブジェクトを送信できます。入力例: {a: {a: {a: {...}}}} 10,000レベルの深さまでネストすると、スタックオーバーフローでアプリケーションがクラッシュします。その関数は深さをチェックせずに無制限に再帰します。

✅ 準拠済み:

function processNestedData(obj, depth = 0, maxDepth = 100) {
    if (depth > maxDepth) {
        throw new Error('Maximum nesting depth exceeded');
    }

    if (typeof obj !== 'object' || obj === null) {
        return obj;
    }

    const result = {};
    for (const key in obj) {
        result[key] = processNestedData(obj[key], depth + 1, maxDepth);
    }
    return result;
}

これが重要である理由: 会社情報 maxDepth パラメーターは再帰を100レベルに制限し、スタックオーバーフローを防ぎます。この制限は、正当なネストされたデータ構造(ほとんどの実際のデータは10〜20レベルを超えることはめったにありません)には十分高く、かつ、大量のスタックメモリを消費する前に攻撃を阻止するのに十分低く設定されています。悪意のある深くネストされた入力は、アプリケーションをクラッシュさせる代わりにエラーをスローします。深さチェックは処理前に実行され、制限を超えた場合は迅速に失敗します。

まとめ

外部データを処理するすべての再帰関数に深度パラメーターを追加します。想定されるデータ構造の複雑さに基づいて、妥当な最大深度を設定してください。深度制限を超過した場合は、クラッシュする代わりにエラーをスローするか、デフォルト値を返します。

よくある質問

ご質問がありますか?

妥当な最大再帰深度はどのくらいですか?

データ構造に依存します。JSONパースやツリートラバーサルでは、100〜1000レベルが妥当です。ほとんどの正当なデータ構造は10〜20レベルを超えません。ドメインに基づいて制限を設定しますが、常に制限を設けてください。実際の深さを確認し、それに応じて調整するために本番環境を監視してください。

再帰をイテレーションにどのように変換しますか?

明示的なスタックまたはキューを使用してください。再帰呼び出しを、項目をスタックにプッシュし、その後ポップして処理するループに置き換えてください。これにより、メモリ使用量と深さを完全に制御できます。木構造の走査では、明示的なデータ構造を用いた幅優先または深さ優先のイテレーションにより、スタックオーバーフローを防ぎます。

再帰呼び出しの開始時または終了時に深さをチェックすべきですか?

開始時、いかなる処理の前にも。これにより、制限を超えた場合に早期に失敗し、拒否されるデータに対する無駄な計算を防ぎます。関数エントリーのガード句は、深度チェックを明示的にし、監査を容易にします。

再帰関数内の循環参照をどのように処理しますか?

SetまたはWeakSetで訪問済みオブジェクトを追跡します。再帰処理の前に、オブジェクトがすでに訪問済みであるかを確認します。訪問済みの場合、スキップするか、エラーをスローするか、プレースホルダーを返します。これにより、循環データ構造(obj.child.parent === obj)による無限再帰を防ぎます。

今すぐ、安全な環境へ。

コード、クラウド、ランタイムを1つの中央システムでセキュアに。
脆弱性を迅速に発見し、自動的に修正。

クレジットカードは不要です。 | スキャン結果は32秒で表示されます。