説明
ユーザーが指定したURLを、HttpClient、WebRequest、Uri を使うAPIなどによるサーバー側のリクエストに渡すと、内部サービスやメタデータのエンドポイントにリクエストを送らされるおそれがあります。
想定される影響
- 内部ネットワークの調査、クラウドのメタデータの窃取、内部APIの呼び出しにつながる可能性があります。
対処方法
URLやホストをユーザーから直接受け取らず、サーバーが管理する識別子と固定URIの対応表から接続先を選ぶ方法を優先します。任意のURLが必須なら、スキーム・ホスト・ポートを許可リストで厳密に制限し、DNS応答のすべてのIPについてループバック、プライベート、リンクローカル、メタデータのアドレスを拒否します。また、自動リダイレクトを無効にするか、各リダイレクト先を再検証してください。
例
変更前
csharp
var response = await httpClient.GetAsync(url);
変更後
csharp
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
public sealed class StatusController : ControllerBase
{
private static readonly IReadOnlyDictionary<string, Uri> Endpoints =
new Dictionary<string, Uri>(StringComparer.Ordinal)
{
["catalog"] = new Uri("https://catalog-api.example.com/health"),
["billing"] = new Uri("https://billing-api.example.com/health")
};
private static readonly HttpClient Client = new HttpClient(
new HttpClientHandler { AllowAutoRedirect = false });
[HttpGet("/status")]
public async Task<IActionResult> GetStatus([FromQuery] string service)
{
if (service == null || !Endpoints.TryGetValue(service, out Uri endpoint))
{
return BadRequest("Unknown service");
}
using HttpResponseMessage response = await Client.GetAsync(endpoint);
return StatusCode((int)response.StatusCode);
}
}
説明:
- 変更前: ユーザーが制御するURLを外部へのリクエストAPIに渡すと、SSRFにつながるおそれがあります。
- 変更後: ユーザーが選べるのはサービス識別子だけで、実際のURIはサーバーコード内の固定の対応表から取得します。不明な識別子は拒否し、自動リダイレクトも無効にしているため、入力やリダイレクトから別の接続先を指定できません。