서버 사이드 요청 위조

C# 서버 사이드 요청 위조

설명

사용자 입력 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는 서버 코드의 고정 매핑에서 가져옵니다. 알 수 없는 식별자는 거부하며 자동 리다이렉트도 비활성화하므로 사용자 입력이나 리다이렉트가 새로운 요청 목적지를 만들 수 없습니다.

참조