Aikido

深さ制限なしの再帰を避けるべき理由

バグのリスク

ルール
回避 再帰 なし 深さ 保護
再帰 なし 適切な 深さ 制限 リスク スタック
オーバーフロー および 生成する DoS 脆弱性を から 悪意のある
入力から 再帰 による 強制された 深さ 制限 および 適切な
境界 チェック  許容される。

言語サポート: 45+

はじめに

深さ制限のない再帰関数はコールスタックを使い果たし、クラッシュを引き起こす可能性があります。深くネストされたJSONオブジェクトや循環データ構造といった悪意のある入力は、意図的に無制限の再帰を誘発します。巧妙に作成された単一のリクエストがスタック制限を超過することでサービスをクラッシュさせ、悪用が極めて容易なサービス拒否(DoS)脆弱性を生み出します。

なぜそれが重要なのか

セキュリティ上の影響(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;
}

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

結論

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

よくある質問

ご質問は?

合理的な最大再帰深度はどれくらいですか?

データ構造次第です。JSON解析や木構造探索の場合、100~1000階層は妥当な範囲です。正当なデータ構造の大半は10~20階層を超えません。ドメインに基づいて制限を設定しますが、常に上限を設けてください。本番環境で実際の深さを監視し、必要に応じて調整します。

再帰を反復に変換するにはどうすればよいですか?

明示的なスタックやキューを使用する。再帰呼び出しを、要素をスタックにプッシュし、その後ポップして処理するループに置き換える。これによりメモリ使用量と深さを完全に制御できる。木探索では、明示的なデータ構造を用いた幅優先探索または深さ優先探索によりスタックオーバーフローを防止できる。

再帰呼び出しの開始時と終了時のどちらで深さを確認すべきか?

処理開始時、いかなる処理が行われる前。制限値を超えた場合、これは迅速に失敗し、拒否されるデータに対する無駄な計算を防止する。関数エントリでのガード句は、深さのチェックを明示的にし、監査を容易にする。

再帰関数における循環参照をどのように処理すればよいですか?

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

今すぐ安全を確保しましょう

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

クレジットカードは不要。