以下の内容はhttps://uga-box.hatenablog.com/entry/2024/05/20/000000より取得しました。


【Next.js】Server ActionsでSSRFが起こせるという話

Next.jsのServer ActionsでSSRFが起こせるという話が話題になった

www.assetnote.io

SSRFに馴染みがなかったので、まずはそれから調べた

SSRFとは

SSRF (Server-Side Request Forgery) とは、攻撃者がサーバー側のアプリケーションにリクエストを送信させることを目的とする攻撃手法のこと

これにより、攻撃者は本来アクセスできない内部システムやリソースにアクセスできる可能性がある

SSRFの仕組み

SSRFは、サーバーが外部のリソースにアクセスする機能を悪用する

例えば、あるウェブアプリケーションがユーザーからの入力に基づいて外部のURLにリクエストを送信する機能を持っている場合、攻撃者はその入力を操作してサーバーに任意のリクエストを送信させることができる

SSRF攻撃の例

  • 内部ネットワークへのアクセス:
    • 攻撃者がサーバーを利用して内部ネットワーク内の他のサーバーやサービスにアクセスすることができる
    • これにより、内部のAPIエンドポイントや管理インターフェースにアクセスされる可能性がある
  • メタデータの取得:
    • クラウド環境(例:AWS)の場合、特定のエンドポイントにアクセスするとメタデータ(認証情報など)を取得できることがある
    • 攻撃者はこの情報を利用してさらに侵入することができる
  • 外部サービスへの不正アクセス:
    • サーバーが外部のURLにリクエストを送信する機能を持っている場合、攻撃者はこれを悪用して外部サービスへの不正アクセスDDoS攻撃を仕掛けることができる

今回のNext.jsのSSRFの問題

今回のNext.jsのSSRFの問題は、CVEに登録されていて CVE-2024-34351 となっている CVE - CVE-2024-34351

CVE-2024-34351 はServer Actionを利用している場合に、httpヘッダーの Host を改ざんしてリクエストすると、self hostingなNext.jsサーバーから任意のhttpリクエストを送信できてしまう脆弱性らしい

Next.jsのソースを見ると、Server Actionを呼び出してリダイレクトでレスポンスすると、次の関数が呼び出されている

async function createRedirectRenderResult(
  req: IncomingMessage,
  res: ServerResponse,
  redirectUrl: string,
  basePath: string,
  staticGenerationStore: StaticGenerationStore
) {
  res.setHeader('x-action-redirect', redirectUrl)
  // if we're redirecting to a relative path, we'll try to stream the response
  if (redirectUrl.startsWith('/')) {
    const forwardedHeaders = getForwardedHeaders(req, res)
    forwardedHeaders.set(RSC_HEADER, '1')

    const host = req.headers['host']
    const proto =
      staticGenerationStore.incrementalCache?.requestProtocol || 'https'
    const fetchUrl = new URL(`${proto}://${host}${basePath}${redirectUrl}`)
    // .. snip ..
    try {
      const headResponse = await fetch(fetchUrl, {
        method: 'HEAD',
        headers: forwardedHeaders,
        next: {
          // @ts-ignore
          internal: 1,
        },
      })

      if (
        headResponse.headers.get('content-type') === RSC_CONTENT_TYPE_HEADER
      ) {
        const response = await fetch(fetchUrl, {
          method: 'GET',
          headers: forwardedHeaders,
          next: {
            // @ts-ignore
            internal: 1,
          },
        })
        // .. snip ..
        return new FlightRenderResult(response.body!)
      }
    } catch (err) {
      // .. snip ..
    }
  }

  return RenderResult.fromStatic('{}')
}

ここでhostがクライアントから取得されているので、ホストヘッダーを偽造すると、確かに任意のhttpリクエストを送信できてしまう

その後のロジックにより、特定の条件でしか発生しないようだが、確かにSSRFができてしまうようだ

気になるのは、redirectの処理内でfetchをしているところで、なんでこんなことをしているかというのは@akfmさんの記事で言及されていて面白かった

zenn.dev

私もこれしかやり方ってないのかな?と思うが、今のところこれしかないんだろう

問題自体はv14.1.1にvupすれば解決する




以上の内容はhttps://uga-box.hatenablog.com/entry/2024/05/20/000000より取得しました。
このページはhttp://font.textar.tv/のウェブフォントを使用してます

不具合報告/要望等はこちらへお願いします。
モバイルやる夫Viewer Ver0.14