설명
사용자 입력 URL이 HttpClient, WebRequest, Uri 기반 서버 측 요청으로 전달되면 공격자가 내부 서비스나 메타데이터 엔드포인트로 요청을 보낼 수 있습니다.
잠재적 영향
- 내부 시스템 스캔, 클라우드 메타데이터 탈취, 내부 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는 서버 코드의 고정 매핑에서 가져옵니다. 알 수 없는 식별자는 거부하며 자동 리다이렉트도 비활성화하므로 사용자 입력이나 리다이렉트가 새로운 요청 목적지를 만들 수 없습니다.