재우니의 블로그

 

 

ASP.NET Core에서 정적 파일 제공 시 보안적인 측면을 고려하여 어떻게 구성하면 좋을지 알아보죠.


✅ 1. 기본 정적 파일 보안 고려

기본적인 정적 파일 제공 방식과 간단한 보안 설정을 익히는 것이 중요해요.

📌 기본적인 정적 파일 보안 설정

ASP.NET Core에서 기본적으로 wwwroot 폴더 안의 파일은 브라우저에서 직접 접근할 수 있어요. 하지만 이 외의 폴더에 있는 파일은 기본적으로 보호되어 접근할 수 없어요.

✅ 1) wwwroot 내부에서만 정적 파일 제공하기

🔹 Program.cs 설정

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

// 기본 정적 파일 제공
app.UseStaticFiles();

app.Run();

 

이렇게 하면 wwwroot 폴더 내의 파일만 접근 가능하며, 외부 폴더(예: secret/, config/)는 접근이 차단돼요.

 

✅ 2) wwwroot 바깥의 파일은 기본적으로 차단됨

예를 들어, MyWebApp/secret/config.json 파일이 있다고 가정해 볼게요.

{
    "connectionString": "Server=myServer;Database=myDB;User=myUser;Password=myPassword;"
}

 

이 파일은 브라우저에서 http://localhost:5000/secret/config.json으로 접근할 수 없어요.
이것이 기본적인 보안 설정이에요.


📌 추가 보안 설정

✅ 3) 특정 파일만 허용하기

특정 폴더에서 일부 파일만 제공하고 싶다면 MIME 타입을 제한하는 방법이 있어요.

app.UseStaticFiles(new StaticFileOptions
{
    FileProvider = new PhysicalFileProvider(Path.Combine(Directory.GetCurrentDirectory(), "public")),
    RequestPath = "/public",
    OnPrepareResponse = ctx =>
    {
        // 특정 파일 유형만 허용
        var allowedExtensions = new[] { ".jpg", ".png", ".css", ".js" };
        var fileExtension = Path.GetExtension(ctx.File.Name).ToLower();

        if (!allowedExtensions.Contains(fileExtension))
        {
            ctx.Context.Response.StatusCode = StatusCodes.Status403Forbidden;
            ctx.Context.Response.Body = Stream.Null;
        }
    }
});

 

📌 설명

  • public/ 폴더의 파일을 /public 경로에서 제공
  • .jpg, .png, .css, .js 확장자만 허용하고, 나머지는 403 Forbidden 처리

✅ 2. 정적 파일 보안 강화

좀 더 많은 보안 문제를 고려해 보죠.
대표적으로 MIME 스니핑 공격 방지, 캐싱 설정, 폴더 접근 제어, 서브도메인 분리 등이 있어요.


📌 보안 강화를 위한 코드 예제

✅ 1) X-Content-Type-Options: nosniff 헤더 추가

MIME 스니핑 공격을 방지하기 위해 X-Content-Type-Options 헤더를 추가해요.

app.UseStaticFiles(new StaticFileOptions
{
    OnPrepareResponse = ctx =>
    {
        ctx.Context.Response.Headers["X-Content-Type-Options"] = "nosniff";
    }
});

 

📌 설명

  • 브라우저가 파일 형식을 자동 감지하지 못하도록 강제
  • .txt 파일이 실행 파일(.exe)처럼 해석되는 공격 방지

✅ 2) 특정 IP에서만 정적 파일 접근 허용

외부에서 정적 파일을 제한하고 내부 IP에서만 접근하도록 설정할 수도 있어요.

app.UseStaticFiles(new StaticFileOptions
{
    OnPrepareResponse = ctx =>
    {
        var remoteIpAddress = ctx.Context.Connection.RemoteIpAddress?.ToString();
        var allowedIp = "192.168.1.100"; // 내부망 IP 예제

        if (remoteIpAddress != allowedIp)
        {
            ctx.Context.Response.StatusCode = StatusCodes.Status403Forbidden;
            ctx.Context.Response.Body = Stream.Null;
        }
    }
});

 

📌 설명

  • 특정 IP에서만 정적 파일 요청 가능
  • 외부 사용자는 403 Forbidden 응답을 받음

✅ 3) 파일 확장자별 접근 제한 (예: .config, .json 파일 차단)

기본적으로 .json, .config 같은 설정 파일은 노출되면 안 돼요.
이를 방지하려면 특정 확장자를 차단하는 설정을 추가할 수 있어요.

app.UseStaticFiles(new StaticFileOptions
{
    OnPrepareResponse = ctx =>
    {
        var forbiddenExtensions = new[] { ".config", ".json", ".env", ".cshtml" };
        var fileExtension = Path.GetExtension(ctx.File.Name).ToLower();

        if (forbiddenExtensions.Contains(fileExtension))
        {
            ctx.Context.Response.StatusCode = StatusCodes.Status403Forbidden;
            ctx.Context.Response.Body = Stream.Null;
        }
    }
});

 

📌 설명

  • .config, .json, .env, .cshtml 파일은 403 Forbidden 응답

✅ 4) 특정 폴더에 대한 접근 제한

/admin, /private 같은 폴더에 대한 접근을 차단할 수도 있어요.

app.UseStaticFiles(new StaticFileOptions
{
    OnPrepareResponse = ctx =>
    {
        var restrictedPaths = new[] { "/admin", "/private" };
        var requestPath = ctx.Context.Request.Path.Value;

        if (restrictedPaths.Any(p => requestPath.StartsWith(p)))
        {
            ctx.Context.Response.StatusCode = StatusCodes.Status403Forbidden;
            ctx.Context.Response.Body = Stream.Null;
        }
    }
});

 

📌 설명

  • /admin, /private 경로에 대한 모든 접근을 차단

✅ 5) 정적 파일 캐싱 설정

보안뿐만 아니라 성능 최적화를 위해 캐싱을 설정하는 것이 좋아요.

app.UseStaticFiles(new StaticFileOptions
{
    OnPrepareResponse = ctx =>
    {
        ctx.Context.Response.Headers["Cache-Control"] = "public, max-age=604800"; // 7일 캐시
    }
});

 

📌 설명

  • Cache-Control: public, max-age=604800
  • 7일 동안 브라우저 캐싱 유지 → 성능 향상

✅ 결론 (기본 보안 vs + 추가 제한 보안)

 

기본 정적 파일 제공 app.UseStaticFiles() StaticFileOptions 사용하여 보안 설정 강화
MIME 스니핑 방지 기본 설정 사용 X-Content-Type-Options: nosniff 추가
IP 제한 없음 내부망 IP만 접근 가능하도록 설정
파일 확장자 제한 .config, .json 기본적으로 접근 불가 특정 파일 확장자 강제 차단
폴더 접근 제한 기본 제공되는 보안 사용 /admin, /private 폴더 직접 차단
캐싱 설정 없음 Cache-Control 헤더 설정

✅ 결론

  • 기본: wwwroot 폴더 사용, UseStaticFiles() 적용, 기본적인 보안 개념 이해
  • 추가보안: 추가적인 보안 설정 적용, MIME 스니핑 방지, IP 제한, 특정 파일 차단, 캐싱 설정 등 고려

 

 


 

보충 설명

 

 

아래는 제품 환경(Production)에서 정적 파일 제공 시 고려해야 할 보안 측면을 포함한 예제입니다. 개발자 입장에서 제품화 시 안전하고 효율적인 정적 파일 위해 다음과 같은 사항들을 고려할 수 있습니다.


1. 주요 보안 고려 사항

  • 민감 파일 분리:
    공개할 리소스는 반드시 wwwroot 또는 인증이 완료된 별도 폴더에 위치시키고, 민감 정보(예: 설정 파일, 데이터베이스 파일 등)는 절대 공개 경로에 포함시키지 않습니다.
  • 디렉토리 브라우징 금지:
    기본적으로 ASP.NET Core에서는 디렉토리 브라우징이 비활성화되어 있지만, 혹시 활성화된 경우 이를 반드시 해제해야 합니다.
  • 보안 헤더 적용:
    응답 헤더에 X-Content-Type-Options: nosniff, Content-Security-Policy, X-Frame-Options 등 보안 헤더를 추가하여 MIME 스니핑 공격 및 클릭재킹(clickjacking) 등을 방지합니다.
  • 캐싱 정책 제어:
    민감 리소스는 캐싱을 금지(no-store)하고, 공개 리소스는 상황에 맞는 캐싱 기간을 설정합니다.
  • HTTPS 사용 강제:
    정적 파일 전송 시 반드시 HTTPS를 사용해 데이터 암호화를 보장합니다.
  • 사용자 정의 MIME 타입 매핑:
    기본 MIME 매핑을 재정의하거나 추가 파일 형식에 대해 올바른 MIME 타입을 설정합니다.
  • 응답 압축 최적화:
    Brotli 또는 Gzip 압축을 통해 네트워크 대역폭을 절약하고 응답 속도를 높입니다.

2. 고급 보안 코드 예제

아래 예제는 제품화된 환경에서 정적 파일 제공 시 보안과 성능을 모두 고려한 구성입니다.

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.ResponseCompression;
using Microsoft.AspNetCore.StaticFiles;
using Microsoft.Extensions.FileProviders;
using Microsoft.Extensions.Hosting;
using System.IO;
using System.IO.Compression;

var builder = WebApplication.CreateBuilder(args);

// 1. 응답 압축 설정 (Brotli, Gzip)
builder.Services.AddResponseCompression(options =>
{
    options.EnableForHttps = true;
    // 필요한 MIME 타입만 필터링할 수 있음
});
builder.Services.Configure<BrotliCompressionProviderOptions>(options =>
{
    options.Level = CompressionLevel.Fastest;
});
builder.Services.Configure<GzipCompressionProviderOptions>(options =>
{
    options.Level = CompressionLevel.Fastest;
});

var app = builder.Build();

// 2. HTTPS 강제 (제품 환경에서 SSL 사용)
app.UseHttpsRedirection();

// 3. 공통 보안 헤더 미들웨어 (추가 보안 헤더 적용)
app.Use(async (context, next) =>
{
    // MIME 스니핑 방지
    context.Response.Headers["X-Content-Type-Options"] = "nosniff";
    // 클릭재킹 방지
    context.Response.Headers["X-Frame-Options"] = "DENY";
    // 기본 Content-Security-Policy (필요에 따라 세부 조정)
    context.Response.Headers["Content-Security-Policy"] = "default-src 'self'";
    await next();
});

// 4. 정적 파일 압축 적용
app.UseResponseCompression();

// 5. 기본 wwwroot 정적 파일 제공 (공개 리소스)
app.UseStaticFiles(new StaticFileOptions
{
    OnPrepareResponse = context =>
    {
        // 공개 리소스: 1시간 캐시
        context.Context.Response.Headers["Cache-Control"] = "public, max-age=3600";
    }
});

// 6. 민감 혹은 인증 후 접근이 필요한 리소스 제공 (SecureFiles 예제)
// 예: 인증 미들웨어를 추가하여 /secure 경로에 접근할 때 인증된 사용자만 접근하도록 구성
app.UseStaticFiles(new StaticFileOptions
{
    FileProvider = new PhysicalFileProvider(Path.Combine(Directory.GetCurrentDirectory(), "SecureFiles")),
    RequestPath = "/secure",
    OnPrepareResponse = context =>
    {
        // 민감 리소스: 캐시 금지
        context.Context.Response.Headers["Cache-Control"] = "no-store";
    }
});

// 7. 사용자 정의 MIME 타입 매핑 (예: .custom 확장자)
var provider = new FileExtensionContentTypeProvider();
provider.Mappings[".custom"] = "text/plain"; // 필요에 따라 적절한 MIME 타입 지정
app.UseStaticFiles(new StaticFileOptions
{
    ContentTypeProvider = provider,
    OnPrepareResponse = context =>
    {
        // MIME 스니핑 방지
        context.Context.Response.Headers["X-Content-Type-Options"] = "nosniff";
    }
});

// 8. 애플리케이션 엔드포인트 (예시)
app.MapGet("/", () => "제품 환경을 위한 고급 정적 파일 제공 예제입니다.");

// 9. 애플리케이션 실행
app.Run();

3. 추가 고려 사항

  • 인증 및 권한 검사:
    만약 /secure와 같이 민감한 리소스가 존재한다면, 정적 파일 미들웨어 전에 인증 미들웨어를 배치하여 접근 제어를 실시하는 것이 좋습니다. 예를 들어, app.UseAuthentication() 및 app.UseAuthorization()을 적절히 배치한 후 정적 파일 제공을 구성할 수 있습니다.
  • 환경별 설정 분리:
    개발, 스테이징, 프로덕션 환경별로 캐싱 정책, 보안 헤더, 응답 압축 등 설정을 분리해서 관리하면 더욱 유연합니다.
    예를 들어, appsettings.Production.json에서 환경별 설정을 읽어오는 방식을 고려해보세요.
  • Content-Security-Policy (CSP) 설정:
    CSP는 매우 세밀하게 구성할 필요가 있으므로, 실제 제품 환경에 맞게 스크립트, 스타일, 이미지 등 각 리소스의 출처를 명시하여 설정하세요.
  • 정적 파일 접근 로그 모니터링:
    제품 환경에서는 정적 파일 접근 로그를 모니터링하여 비정상적인 접근 패턴이나 공격 시도를 감지하는 것도 좋은 보안 관리 방법입니다.

결론

제품 환경에서 정적 파일을 제공할 때는 기본적인 UseStaticFiles() 호출을 넘어서, HTTPS 강제, 응답 압축, 보안 헤더 적용, 캐싱 제어, 사용자 정의 MIME 타입 매핑, 그리고 민감 파일에 대한 접근 제어 등 여러 보안 및 성능 최적화 요소를 반드시 고려해야 합니다.

 

위의 예제 코드는 이러한 보안 코드 측면을 모두 포함하고 있으며, 환경별로 세부 설정을 조정하여 안전하고 효율적인 정적 파일 제공 방식을 구현할 수 있습니다.