ASP.NET Core SignalR에서 보안 고려 사항

작성자: Andrew Stanton-Nurse

이 문서에서는 SignalR 보안에 대한 정보를 제공합니다.

원본 간 리소스 공유

CORS(원본 간 리소스 공유) 를 사용하여 브라우저에서 원본 SignalR 간 연결을 허용할 수 있습니다. JavaScript 코드가 SignalR 앱과 다른 도메인에서 호스트되는 경우 JavaScript가 SignalR 앱에 연결될 수 있도록 CORS 미들웨어를 활성화해야 합니다. 신뢰하거나 제어하는 도메인의 원본 간 요청만 허용합니다. 예시:

  • 사이트가 http://www.example.com에 호스트됩니다.
  • SignalR 앱이 http://signalr.example.com에 호스트됩니다.

원본 www.example.com만 허용하도록 SignalR 앱에서 CORS를 구성해야 합니다.

CORS 구성에 대한 자세한 내용은 CORS(원본 간 요청) 사용을 참조하세요. 다음 CORS 정책이 SignalR필요합니다.

  • 특정 예상 원본을 허용합니다. 원본 허용은 가능하지만 안전하지 않거나 권장되지 않습니다.
  • HTTP 메서드 GETPOST가 허용되어야 합니다.
  • cookie에 대한 기반 고정 세션이 제대로 작동하려면 자격 증명을 허용해야 합니다. 인증을 사용하지 않는 경우에도 활성화해야 합니다.

그러나 5.0에서는 TypeScript 클라이언트에서 자격 증명을 사용하지 않는 옵션을 제공했습니다. 자격 증명을 사용하지 않는 옵션은 앱에서 Cookie와 같은 자격 증명이 필요하지 않다는 것을 100% 알고 있는 경우에만 사용해야 합니다(cookie는 고정 세션에 여러 서버를 사용하는 경우 Azure App Service에서 사용됨).

예를 들어 다음 강조 표시된 CORS 정책을 사용하면 호스트된 브라우저 클라이언트가 호스트되는 앱에 https://example.com 액세스할 수 있습니다SignalR.https://signalr.example.comSignalR

using SignalRChat.Hubs;

var MyAllowSpecificOrigins = "_myAllowSpecificOrigins";

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddCors(options =>
{
    options.AddPolicy(name: MyAllowSpecificOrigins,
                      policy =>
                      {
                          policy.WithOrigins("http://example.com");
                          policy.WithMethods("GET", "POST");
                          policy.AllowCredentials();
                      });
});

// Add services to the container.
builder.Services.AddRazorPages();
builder.Services.AddSignalR();

var app = builder.Build();

app.MapHub<ChatHub>("/chatHub");

이전 예제에서 CORS 정책은 특정 원본, 메서드 및 자격 증명을 허용하도록 사용자 지정됩니다. ASP.NET Core에서 CORS 정책 및 미들웨어를 사용자 지정하는 방법에 대한 자세한 내용은 CORS 미들웨어: 명명된 정책 및 미들웨어가 있는 CORS를 참조하세요.

WebSocket 원본 제한

CORS에서 제공하는 보호 기능은 WebSocket에 적용되지 않습니다. WebSocket에 대한 원본 제한은 WebSocket 원본 제한을 참조하세요.

ConnectionId

SignalR 서버 또는 클라이언트 버전이 ASP.NET Core 2.2 이하인 경우 ConnectionId를 노출하면 악의적인 가장이 발생할 수 있습니다. SignalR 서버 및 클라이언트 버전이 ASP.NET Core 3.0 이상인 경우 ConnectionId 대신 ConnectionToken을 비밀로 유지해야 합니다. ConnectionToken은 의도적으로 API에 노출되지 않습니다. 이전 SignalR 클라이언트가 서버에 연결되지 않는지 확인하기 어려울 수 있으므로 SignalR 서버 버전이 ASP.NET Core 3.0 이상인 경우에도 ConnectionId가 노출되지 않아야 합니다.

액세스 토큰 로깅

WebSocket 또는 Server-Sent 이벤트를 사용하는 경우 브라우저 클라이언트는 쿼리 문자열에 액세스 토큰을 보냅니다. 쿼리 문자열을 통해 액세스 토큰을 수신하는 것은 일반적으로 표준 Authorization 헤더를 사용하는 것처럼 안전합니다. 항상 HTTPS를 사용하여 클라이언트와 서버 간의 안전한 엔드투엔드 연결을 보장합니다. 많은 웹 서버는 쿼리 문자열을 포함하여 각 요청에 대한 URL을 기록합니다. URL을 로깅하면 액세스 토큰을 기록할 수 있습니다. ASP.NET Core는 기본적으로 쿼리 문자열을 포함하는 각 요청에 대한 URL을 기록합니다. 예시:

info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1]
      Request starting HTTP/1.1 GET http://localhost:5000/chathub?access_token=1234

서버 로그를 사용하여 이 데이터를 로깅하는 데 문제가 있는 경우 Microsoft.AspNetCore.Hosting 로거를 Warning 수준 이상으로 구성하여 이 로깅을 완전히 비활성화할 수 있습니다(이러한 메시지는 Info 수준에서 작성됨). 자세한 내용은 코드에서 로그 필터 규칙 적용을 참조하세요. 여전히 특정 요청 정보를 기록하려는 경우 미들웨어를 작성하여 필요한 데이터를 기록하고 access_token 쿼리 문자열 값(있는 경우)을 필터링할 수 있습니다.

예외

일반적으로 예외 메시지는 클라이언트에 공개되지 않아야 하는 중요한 데이터로 간주됩니다. 기본적으로 SignalR은 허브 메서드에서 throw된 예외의 세부 정보를 클라이언트에 보내지 않습니다. 대신, 클라이언트는 오류가 발생했음을 나타내는 일반 메시지를 받게 됩니다. EnableDetailedErrors를 사용하여 예외 메시지를 클라이언트에 전달하는 옵션을 재정의할 수 있습니다(예: 개발 또는 테스트). 프로덕션 앱에서는 예외 메시지를 클라이언트에 공개하면 안 됩니다.

버퍼 관리

SignalR은 연결당 버퍼를 사용하여 들어오고 나가는 메시지를 관리합니다. 기본적으로 SignalR은 이러한 버퍼를 32KB로 제한합니다. 클라이언트 또는 서버에서 보낼 수 있는 가장 큰 메시지는 32KB입니다. 메시지에 대한 연결에서 사용하는 최대 메모리는 32KB입니다. 메시지가 항상 32KB보다 작은 경우 제한을 줄일 수 있습니다. 이 제한은 다음과 같습니다.

  • 클라이언트가 더 큰 메시지를 보낼 수 없도록 합니다.
  • 서버는 메시지를 수락하기 위해 큰 버퍼를 할당할 필요가 없습니다.

메시지가 32KB보다 큰 경우 제한을 늘릴 수 있습니다. 이 제한을 늘리면 다음을 의미합니다.

  • 클라이언트로 인해 서버에서 큰 메모리 버퍼를 할당할 수 있습니다.
  • 서버에서 큰 버퍼를 할당하면 동시 연결 수가 줄어들 수 있습니다.

들어오고 나가는 메시지에 대한 제한이 있으며, 둘 다 MapHub에 구성된 HttpConnectionDispatcherOptions 개체에서 구성될 수 있습니다.

  • ApplicationMaxBufferSize는 서버가 버퍼링하는 클라이언트의 최대 바이트 수를 나타냅니다. 클라이언트가 이 제한보다 큰 메시지를 보내려고 하면 연결이 닫혀 있을 수 있습니다.
  • TransportMaxBufferSize는 서버에서 보낼 수 있는 최대 바이트 수를 나타냅니다. 서버가 이 제한보다 큰 메시지(허브 메서드의 반환 값 포함)를 보내려고 하면 예외가 throw됩니다.

제한을 0으로 설정하면 제한이 비활성화됩니다. 제한을 제거하면 클라이언트가 모든 크기의 메시지를 보낼 수 있습니다. 악의적인 클라이언트가 큰 메시지를 보내면 과도한 메모리가 할당될 수 있습니다. 과도한 메모리 사용량은 동시 연결 수를 크게 줄일 수 있습니다.

이 문서에서는 SignalR 보안에 대한 정보를 제공합니다.

원본 간 리소스 공유

CORS(원본 간 리소스 공유) 를 사용하여 브라우저에서 원본 SignalR 간 연결을 허용할 수 있습니다. JavaScript 코드가 SignalR 앱과 다른 도메인에서 호스트되는 경우 JavaScript가 SignalR 앱에 연결될 수 있도록 CORS 미들웨어를 활성화해야 합니다. 신뢰하거나 제어하는 도메인의 원본 간 요청만 허용합니다. 예시:

  • 사이트가 http://www.example.com에 호스트됩니다.
  • SignalR 앱이 http://signalr.example.com에 호스트됩니다.

원본 www.example.com만 허용하도록 SignalR 앱에서 CORS를 구성해야 합니다.

CORS 구성에 대한 자세한 내용은 CORS(원본 간 요청) 사용을 참조하세요. 다음 CORS 정책이 SignalR필요합니다.

  • 특정 예상 원본을 허용합니다. 원본 허용은 가능하지만 안전하지 않거나 권장되지 않습니다.
  • HTTP 메서드 GETPOST가 허용되어야 합니다.
  • cookie에 대한 기반 고정 세션이 제대로 작동하려면 자격 증명을 허용해야 합니다. 인증을 사용하지 않는 경우에도 활성화해야 합니다.

그러나 5.0에서는 TypeScript 클라이언트에서 자격 증명을 사용하지 않는 옵션을 제공했습니다. 자격 증명을 사용하지 않는 옵션은 앱에서 Cookie와 같은 자격 증명이 필요하지 않다는 것을 100% 알고 있는 경우에만 사용해야 합니다(cookie는 고정 세션에 여러 서버를 사용하는 경우 Azure App Service에서 사용됨).

예를 들어 다음 강조 표시된 CORS 정책을 사용하면 호스트된 브라우저 클라이언트가 호스트되는 앱에 https://example.com 액세스할 수 있습니다SignalR.https://signalr.example.comSignalR

using SignalRChat.Hubs;

var MyAllowSpecificOrigins = "_myAllowSpecificOrigins";

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddCors(options =>
{
    options.AddPolicy(name: MyAllowSpecificOrigins,
                      policy =>
                      {
                          policy.WithOrigins("http://example.com");
                          policy.WithMethods("GET", "POST");
                          policy.AllowCredentials();
                      });
});

// Add services to the container.
builder.Services.AddRazorPages();
builder.Services.AddSignalR();

var app = builder.Build();

app.MapHub<ChatHub>("/chatHub");

이전 예제에서 CORS 정책은 특정 원본, 메서드 및 자격 증명을 허용하도록 사용자 지정됩니다. ASP.NET Core에서 CORS 정책 및 미들웨어를 사용자 지정하는 방법에 대한 자세한 내용은 CORS 미들웨어: 명명된 정책 및 미들웨어가 있는 CORS를 참조하세요.

WebSocket 원본 제한

CORS에서 제공하는 보호 기능은 WebSocket에 적용되지 않습니다. WebSocket에 대한 원본 제한은 WebSocket 원본 제한을 참조하세요.

ConnectionId

SignalR 서버 또는 클라이언트 버전이 ASP.NET Core 2.2 이하인 경우 ConnectionId를 노출하면 악의적인 가장이 발생할 수 있습니다. SignalR 서버 및 클라이언트 버전이 ASP.NET Core 3.0 이상인 경우 ConnectionId 대신 ConnectionToken을 비밀로 유지해야 합니다. ConnectionToken은 의도적으로 API에 노출되지 않습니다. 이전 SignalR 클라이언트가 서버에 연결되지 않는지 확인하기 어려울 수 있으므로 SignalR 서버 버전이 ASP.NET Core 3.0 이상인 경우에도 ConnectionId가 노출되지 않아야 합니다.

액세스 토큰 로깅

WebSocket 또는 Server-Sent 이벤트를 사용하는 경우 브라우저 클라이언트는 쿼리 문자열에 액세스 토큰을 보냅니다. 쿼리 문자열을 통해 액세스 토큰을 수신하는 것은 일반적으로 표준 Authorization 헤더를 사용하는 것처럼 안전합니다. 항상 HTTPS를 사용하여 클라이언트와 서버 간의 안전한 엔드투엔드 연결을 보장합니다. 많은 웹 서버는 쿼리 문자열을 포함하여 각 요청에 대한 URL을 기록합니다. URL을 로깅하면 액세스 토큰을 기록할 수 있습니다. ASP.NET Core는 기본적으로 쿼리 문자열을 포함하는 각 요청에 대한 URL을 기록합니다. 예시:

info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1]
      Request starting HTTP/1.1 GET http://localhost:5000/chathub?access_token=1234

서버 로그를 사용하여 이 데이터를 로깅하는 데 문제가 있는 경우 Microsoft.AspNetCore.Hosting 로거를 Warning 수준 이상으로 구성하여 이 로깅을 완전히 비활성화할 수 있습니다(이러한 메시지는 Info 수준에서 작성됨). 자세한 내용은 코드에서 로그 필터 규칙 적용을 참조하세요. 여전히 특정 요청 정보를 기록하려는 경우 미들웨어를 작성하여 필요한 데이터를 기록하고 access_token 쿼리 문자열 값(있는 경우)을 필터링할 수 있습니다.

예외

일반적으로 예외 메시지는 클라이언트에 공개되지 않아야 하는 중요한 데이터로 간주됩니다. 기본적으로 SignalR은 허브 메서드에서 throw된 예외의 세부 정보를 클라이언트에 보내지 않습니다. 대신, 클라이언트는 오류가 발생했음을 나타내는 일반 메시지를 받게 됩니다. EnableDetailedErrors를 사용하여 예외 메시지를 클라이언트에 전달하는 옵션을 재정의할 수 있습니다(예: 개발 또는 테스트). 프로덕션 앱에서는 예외 메시지를 클라이언트에 공개하면 안 됩니다.

버퍼 관리

SignalR은 연결당 버퍼를 사용하여 들어오고 나가는 메시지를 관리합니다. 기본적으로 SignalR은 이러한 버퍼를 32KB로 제한합니다. 클라이언트 또는 서버에서 보낼 수 있는 가장 큰 메시지는 32KB입니다. 메시지에 대한 연결에서 사용하는 최대 메모리는 32KB입니다. 메시지가 항상 32KB보다 작은 경우 제한을 줄일 수 있습니다. 이 제한은 다음과 같습니다.

  • 클라이언트가 더 큰 메시지를 보낼 수 없도록 합니다.
  • 서버는 메시지를 수락하기 위해 큰 버퍼를 할당할 필요가 없습니다.

메시지가 32KB보다 큰 경우 제한을 늘릴 수 있습니다. 이 제한을 늘리면 다음을 의미합니다.

  • 클라이언트로 인해 서버에서 큰 메모리 버퍼를 할당할 수 있습니다.
  • 서버에서 큰 버퍼를 할당하면 동시 연결 수가 줄어들 수 있습니다.

들어오고 나가는 메시지에 대한 제한이 있으며, 둘 다 MapHub에 구성된 HttpConnectionDispatcherOptions 개체에서 구성될 수 있습니다.

  • ApplicationMaxBufferSize는 서버가 버퍼링하는 클라이언트의 최대 바이트 수를 나타냅니다. 클라이언트가 이 제한보다 큰 메시지를 보내려고 하면 연결이 닫혀 있을 수 있습니다.
  • TransportMaxBufferSize는 서버에서 보낼 수 있는 최대 바이트 수를 나타냅니다. 서버가 이 제한보다 큰 메시지(허브 메서드의 반환 값 포함)를 보내려고 하면 예외가 throw됩니다.

제한을 0으로 설정하면 제한이 비활성화됩니다. 제한을 제거하면 클라이언트가 모든 크기의 메시지를 보낼 수 있습니다. 악의적인 클라이언트가 큰 메시지를 보내면 과도한 메모리가 할당될 수 있습니다. 과도한 메모리 사용량은 동시 연결 수를 크게 줄일 수 있습니다.

이 문서에서는 SignalR 보안에 대한 정보를 제공합니다.

원본 간 리소스 공유

CORS(원본 간 리소스 공유) 를 사용하여 브라우저에서 원본 SignalR 간 연결을 허용할 수 있습니다. JavaScript 코드가 SignalR 앱과 다른 도메인에서 호스트되는 경우 JavaScript가 SignalR 앱에 연결될 수 있도록 CORS 미들웨어를 활성화해야 합니다. 신뢰하거나 제어하는 도메인의 원본 간 요청만 허용합니다. 예시:

  • 사이트가 http://www.example.com에 호스트됩니다.
  • SignalR 앱이 http://signalr.example.com에 호스트됩니다.

원본 www.example.com만 허용하도록 SignalR 앱에서 CORS를 구성해야 합니다.

CORS 구성에 대한 자세한 내용은 CORS(원본 간 요청) 사용을 참조하세요. 다음 CORS 정책이 SignalR필요합니다.

  • 특정 예상 원본을 허용합니다. 원본 허용은 가능하지만 안전하지 않거나 권장되지 않습니다.
  • HTTP 메서드 GETPOST가 허용되어야 합니다.
  • cookie에 대한 기반 고정 세션이 제대로 작동하려면 자격 증명을 허용해야 합니다. 인증을 사용하지 않는 경우에도 활성화해야 합니다.

그러나 5.0에서는 TypeScript 클라이언트에서 자격 증명을 사용하지 않는 옵션을 제공했습니다. 자격 증명을 사용하지 않는 옵션은 앱에서 Cookie와 같은 자격 증명이 필요하지 않다는 것을 100% 알고 있는 경우에만 사용해야 합니다(cookie는 고정 세션에 여러 서버를 사용하는 경우 Azure App Service에서 사용됨).

예를 들어 다음 강조 표시된 CORS 정책을 사용하면 호스트된 브라우저 클라이언트가 호스트되는 앱에 https://example.com 액세스할 수 있습니다SignalR.https://signalr.example.comSignalR

using SignalRChat.Hubs;

var MyAllowSpecificOrigins = "_myAllowSpecificOrigins";

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddCors(options =>
{
    options.AddPolicy(name: MyAllowSpecificOrigins,
                      policy =>
                      {
                          policy.WithOrigins("http://example.com");
                          policy.WithMethods("GET", "POST");
                          policy.AllowCredentials();
                      });
});

// Add services to the container.
builder.Services.AddRazorPages();
builder.Services.AddSignalR();

var app = builder.Build();

app.MapHub<ChatHub>("/chatHub");

이전 예제에서 CORS 정책은 특정 원본, 메서드 및 자격 증명을 허용하도록 사용자 지정됩니다. ASP.NET Core에서 CORS 정책 및 미들웨어를 사용자 지정하는 방법에 대한 자세한 내용은 CORS 미들웨어: 명명된 정책 및 미들웨어가 있는 CORS를 참조하세요.

WebSocket 원본 제한

CORS에서 제공하는 보호 기능은 WebSocket에 적용되지 않습니다. WebSocket에 대한 원본 제한은 WebSocket 원본 제한을 참조하세요.

ConnectionId

SignalR 서버 또는 클라이언트 버전이 ASP.NET Core 2.2 이하인 경우 ConnectionId를 노출하면 악의적인 가장이 발생할 수 있습니다. SignalR 서버 및 클라이언트 버전이 ASP.NET Core 3.0 이상인 경우 ConnectionId 대신 ConnectionToken을 비밀로 유지해야 합니다. ConnectionToken은 의도적으로 API에 노출되지 않습니다. 이전 SignalR 클라이언트가 서버에 연결되지 않는지 확인하기 어려울 수 있으므로 SignalR 서버 버전이 ASP.NET Core 3.0 이상인 경우에도 ConnectionId가 노출되지 않아야 합니다.

액세스 토큰 로깅

WebSocket 또는 Server-Sent 이벤트를 사용하는 경우 브라우저 클라이언트는 쿼리 문자열에 액세스 토큰을 보냅니다. 쿼리 문자열을 통해 액세스 토큰을 수신하는 것은 일반적으로 표준 Authorization 헤더를 사용하는 것처럼 안전합니다. 항상 HTTPS를 사용하여 클라이언트와 서버 간의 안전한 엔드투엔드 연결을 보장합니다. 많은 웹 서버는 쿼리 문자열을 포함하여 각 요청에 대한 URL을 기록합니다. URL을 로깅하면 액세스 토큰을 기록할 수 있습니다. ASP.NET Core는 기본적으로 쿼리 문자열을 포함하는 각 요청에 대한 URL을 기록합니다. 예시:

info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1]
      Request starting HTTP/1.1 GET http://localhost:5000/chathub?access_token=1234

서버 로그를 사용하여 이 데이터를 로깅하는 데 문제가 있는 경우 Microsoft.AspNetCore.Hosting 로거를 Warning 수준 이상으로 구성하여 이 로깅을 완전히 비활성화할 수 있습니다(이러한 메시지는 Info 수준에서 작성됨). 자세한 내용은 코드에서 로그 필터 규칙 적용을 참조하세요. 여전히 특정 요청 정보를 기록하려는 경우 미들웨어를 작성하여 필요한 데이터를 기록하고 access_token 쿼리 문자열 값(있는 경우)을 필터링할 수 있습니다.

예외

일반적으로 예외 메시지는 클라이언트에 공개되지 않아야 하는 중요한 데이터로 간주됩니다. 기본적으로 SignalR은 허브 메서드에서 throw된 예외의 세부 정보를 클라이언트에 보내지 않습니다. 대신, 클라이언트는 오류가 발생했음을 나타내는 일반 메시지를 받게 됩니다. EnableDetailedErrors를 사용하여 예외 메시지를 클라이언트에 전달하는 옵션을 재정의할 수 있습니다(예: 개발 또는 테스트). 프로덕션 앱에서는 예외 메시지를 클라이언트에 공개하면 안 됩니다.

버퍼 관리

SignalR은 연결당 버퍼를 사용하여 들어오고 나가는 메시지를 관리합니다. 기본적으로 SignalR은 이러한 버퍼를 32KB로 제한합니다. 클라이언트 또는 서버에서 보낼 수 있는 가장 큰 메시지는 32KB입니다. 메시지에 대한 연결에서 사용하는 최대 메모리는 32KB입니다. 메시지가 항상 32KB보다 작은 경우 제한을 줄일 수 있습니다. 이 제한은 다음과 같습니다.

  • 클라이언트가 더 큰 메시지를 보낼 수 없도록 합니다.
  • 서버는 메시지를 수락하기 위해 큰 버퍼를 할당할 필요가 없습니다.

메시지가 32KB보다 큰 경우 제한을 늘릴 수 있습니다. 이 제한을 늘리면 다음을 의미합니다.

  • 클라이언트로 인해 서버에서 큰 메모리 버퍼를 할당할 수 있습니다.
  • 서버에서 큰 버퍼를 할당하면 동시 연결 수가 줄어들 수 있습니다.

들어오고 나가는 메시지에 대한 제한이 있으며, 둘 다 MapHub에 구성된 HttpConnectionDispatcherOptions 개체에서 구성될 수 있습니다.

  • ApplicationMaxBufferSize는 서버가 버퍼링하는 클라이언트의 최대 바이트 수를 나타냅니다. 클라이언트가 이 제한보다 큰 메시지를 보내려고 하면 연결이 닫혀 있을 수 있습니다.
  • TransportMaxBufferSize는 서버에서 보낼 수 있는 최대 바이트 수를 나타냅니다. 서버가 이 제한보다 큰 메시지(허브 메서드의 반환 값 포함)를 보내려고 하면 예외가 throw됩니다.

제한을 0으로 설정하면 제한이 비활성화됩니다. 제한을 제거하면 클라이언트가 모든 크기의 메시지를 보낼 수 있습니다. 악의적인 클라이언트가 큰 메시지를 보내면 과도한 메모리가 할당될 수 있습니다. 과도한 메모리 사용량은 동시 연결 수를 크게 줄일 수 있습니다.

이 문서에서는 SignalR 보안에 대한 정보를 제공합니다.

원본 간 리소스 공유

CORS(원본 간 리소스 공유) 를 사용하여 브라우저에서 원본 SignalR 간 연결을 허용할 수 있습니다. JavaScript 코드가 SignalR 앱과 다른 도메인에서 호스트되는 경우 JavaScript가 SignalR 앱에 연결될 수 있도록 CORS 미들웨어를 활성화해야 합니다. 신뢰하거나 제어하는 도메인의 원본 간 요청만 허용합니다. 예시:

  • 사이트가 http://www.example.com에 호스트됩니다.
  • SignalR 앱이 http://signalr.example.com에 호스트됩니다.

원본 www.example.com만 허용하도록 SignalR 앱에서 CORS를 구성해야 합니다.

CORS 구성에 대한 자세한 내용은 CORS(원본 간 요청) 사용을 참조하세요. 다음 CORS 정책이 SignalR필요합니다.

  • 특정 예상 원본을 허용합니다. 원본 허용은 가능하지만 안전하지 않거나 권장되지 않습니다.
  • HTTP 메서드 GETPOST가 허용되어야 합니다.
  • cookie에 대한 기반 고정 세션이 제대로 작동하려면 자격 증명을 허용해야 합니다. 인증을 사용하지 않는 경우에도 활성화해야 합니다.

그러나 5.0에서는 TypeScript 클라이언트에서 자격 증명을 사용하지 않는 옵션을 제공했습니다. 자격 증명을 사용하지 않는 옵션은 앱에서 Cookie와 같은 자격 증명이 필요하지 않다는 것을 100% 알고 있는 경우에만 사용해야 합니다(cookie는 고정 세션에 여러 서버를 사용하는 경우 Azure App Service에서 사용됨).

예를 들어 다음 CORS 정책을 사용하면 https://example.com에서 호스트된 SignalR 브라우저 클라이언트가 https://signalr.example.com에서 호스트된 SignalR 앱에 액세스할 수 있습니다.

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    // ... other middleware ...

    // Make sure the CORS middleware is ahead of SignalR.
    app.UseCors(builder =>
    {
        builder.WithOrigins("https://example.com")
            .AllowAnyHeader()
            .WithMethods("GET", "POST")
            .AllowCredentials();
    });

    // ... other middleware ...
    app.UseRouting();

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapHub<ChatHub>("/chathub");
    });

    // ... other middleware ...
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    // ... other middleware ...

    // Make sure the CORS middleware is ahead of SignalR.
    app.UseCors(builder =>
    {
        builder.WithOrigins("https://example.com")
            .AllowAnyHeader()
            .WithMethods("GET", "POST")
            .AllowCredentials();
    });

    // ... other middleware ...

    app.UseSignalR(routes =>
    {
        routes.MapHub<ChatHub>("/chathub");
    });

    // ... other middleware ...
}

WebSocket 원본 제한

CORS에서 제공하는 보호 기능은 WebSocket에 적용되지 않습니다. WebSocket에 대한 원본 제한은 WebSocket 원본 제한을 참조하세요.

CORS에서 제공하는 보호 기능은 WebSocket에 적용되지 않습니다. 브라우저는 다음을 수행하지 않습니다.

  • CORS pre-flight 요청을 수행합니다.
  • WebSocket 요청을 생성할 때 Access-Control 헤더에 지정된 제한 사항을 준수합니다.

그러나 브라우저는 WebSocket 요청을 발급할 때 Origin 헤더를 보냅니다. 애플리케이션은 예상된 원본에서 제공하는 WebSocket만 허용되도록 이러한 헤더의 유효성을 검사하도록 구성되어야 합니다.

ASP.NET Core 2.1 이상에서는 ConfigureUseSignalR 및 인증 미들웨어 앞에 배치된 사용자 지정 미들웨어를 사용하여 헤더 유효성 검사를 수행할 수 있습니다.


// In Startup, add a static field listing the allowed Origin values:
private static readonly HashSet<string> _allowedOrigins = new HashSet<string>()
{
    // Add allowed origins here. For example:
    "https://www.mysite.com",
    "https://mysite.com",
};

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    // ... other middleware ...

    // Validate Origin header on WebSocket requests to prevent unexpected cross-site 
    // WebSocket requests.
    app.Use((context, next) =>
    {
        // Check for a WebSocket request.
        if (string.Equals(context.Request.Headers["Upgrade"], "websocket"))
        {
            var origin = context.Request.Headers["Origin"];

            // If there is an origin header, and the origin header doesn't match 
            // an allowed value:
            if (!string.IsNullOrEmpty(origin) && !_allowedOrigins.Contains(origin))
            {
                // The origin is not allowed, reject the request
                context.Response.StatusCode = (int) HttpStatusCode.Forbidden;
                return Task.CompletedTask;
            }
        }

        // The request is a valid Origin or not a WebSocket request, so continue.
        return next();
    });

    // ... other middleware ...

    app.UseSignalR(routes =>
    {
        routes.MapHub<ChatHub>("/chathub");
    });

    // ... other middleware ...
}

참고 항목

Origin 헤더는 클라이언트에 의해 제어되며 Referer 헤더와 마찬가지로 위조될 수 있습니다. 이러한 헤더를 인증 메커니즘으로 사용하면 안됩니다.

ConnectionId

SignalR 서버 또는 클라이언트 버전이 ASP.NET Core 2.2 이하인 경우 ConnectionId를 노출하면 악의적인 가장이 발생할 수 있습니다. SignalR 서버 및 클라이언트 버전이 ASP.NET Core 3.0 이상인 경우 ConnectionId 대신 ConnectionToken을 비밀로 유지해야 합니다. ConnectionToken은 의도적으로 API에 노출되지 않습니다. 이전 SignalR 클라이언트가 서버에 연결되지 않는지 확인하기 어려울 수 있으므로 SignalR 서버 버전이 ASP.NET Core 3.0 이상인 경우에도 ConnectionId가 노출되지 않아야 합니다.

액세스 토큰 로깅

WebSocket 또는 Server-Sent 이벤트를 사용하는 경우 브라우저 클라이언트는 쿼리 문자열에 액세스 토큰을 보냅니다. 쿼리 문자열을 통해 액세스 토큰을 수신하는 것은 일반적으로 표준 Authorization 헤더를 사용하는 것처럼 안전합니다. 항상 HTTPS를 사용하여 클라이언트와 서버 간의 안전한 엔드투엔드 연결을 보장합니다. 많은 웹 서버는 쿼리 문자열을 포함하여 각 요청에 대한 URL을 기록합니다. URL을 로깅하면 액세스 토큰을 기록할 수 있습니다. ASP.NET Core는 기본적으로 쿼리 문자열을 포함하는 각 요청에 대한 URL을 기록합니다. 예시:

info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1]
      Request starting HTTP/1.1 GET http://localhost:5000/chathub?access_token=1234

서버 로그를 사용하여 이 데이터를 로깅하는 데 문제가 있는 경우 Microsoft.AspNetCore.Hosting 로거를 Warning 수준 이상으로 구성하여 이 로깅을 완전히 비활성화할 수 있습니다(이러한 메시지는 Info 수준에서 작성됨). 자세한 내용은 코드에서 로그 필터 규칙 적용을 참조하세요. 여전히 특정 요청 정보를 기록하려는 경우 미들웨어를 작성하여 필요한 데이터를 기록하고 access_token 쿼리 문자열 값(있는 경우)을 필터링할 수 있습니다.

예외

일반적으로 예외 메시지는 클라이언트에 공개되지 않아야 하는 중요한 데이터로 간주됩니다. 기본적으로 SignalR은 허브 메서드에서 throw된 예외의 세부 정보를 클라이언트에 보내지 않습니다. 대신, 클라이언트는 오류가 발생했음을 나타내는 일반 메시지를 받게 됩니다. EnableDetailedErrors를 사용하여 예외 메시지를 클라이언트에 전달하는 옵션을 재정의할 수 있습니다(예: 개발 또는 테스트). 프로덕션 앱에서는 예외 메시지를 클라이언트에 공개하면 안 됩니다.

버퍼 관리

SignalR은 연결당 버퍼를 사용하여 들어오고 나가는 메시지를 관리합니다. 기본적으로 SignalR은 이러한 버퍼를 32KB로 제한합니다. 클라이언트 또는 서버에서 보낼 수 있는 가장 큰 메시지는 32KB입니다. 메시지에 대한 연결에서 사용하는 최대 메모리는 32KB입니다. 메시지가 항상 32KB보다 작은 경우 제한을 줄일 수 있습니다. 이 제한은 다음과 같습니다.

  • 클라이언트가 더 큰 메시지를 보낼 수 없도록 합니다.
  • 서버는 메시지를 수락하기 위해 큰 버퍼를 할당할 필요가 없습니다.

메시지가 32KB보다 큰 경우 제한을 늘릴 수 있습니다. 이 제한을 늘리면 다음을 의미합니다.

  • 클라이언트로 인해 서버에서 큰 메모리 버퍼를 할당할 수 있습니다.
  • 서버에서 큰 버퍼를 할당하면 동시 연결 수가 줄어들 수 있습니다.

들어오고 나가는 메시지에 대한 제한이 있으며, 둘 다 MapHub에 구성된 HttpConnectionDispatcherOptions 개체에서 구성될 수 있습니다.

  • ApplicationMaxBufferSize는 서버가 버퍼링하는 클라이언트의 최대 바이트 수를 나타냅니다. 클라이언트가 이 제한보다 큰 메시지를 보내려고 하면 연결이 닫혀 있을 수 있습니다.
  • TransportMaxBufferSize는 서버에서 보낼 수 있는 최대 바이트 수를 나타냅니다. 서버가 이 제한보다 큰 메시지(허브 메서드의 반환 값 포함)를 보내려고 하면 예외가 throw됩니다.

제한을 0으로 설정하면 제한이 비활성화됩니다. 제한을 제거하면 클라이언트가 모든 크기의 메시지를 보낼 수 있습니다. 악의적인 클라이언트가 큰 메시지를 보내면 과도한 메모리가 할당될 수 있습니다. 과도한 메모리 사용량은 동시 연결 수를 크게 줄일 수 있습니다.