다음을 통해 공유


ASP.NET Web API 기본 인증

작성자: Mike Wasson

기본 인증은 RFC 2617, HTTP 인증: 기본 및 다이제스트 액세스 인증에 정의되어 있습니다.

단점

  • 사용자 자격 증명은 요청에서 전송됩니다.
  • 자격 증명은 일반 텍스트로 전송됩니다.
  • 자격 증명은 모든 요청과 함께 전송됩니다.
  • 브라우저 세션을 종료하는 것 외에는 로그아웃할 방법이 없습니다.
  • CSRF(교차 사이트 요청 위조)에 취약함 에는 CSRF 방지 측정값이 필요합니다.

장점

  • 인터넷 표준.
  • 모든 주요 브라우저에서 지원됩니다.
  • 비교적 간단한 프로토콜입니다.

기본 인증은 다음과 같이 작동합니다.

  1. 요청에 인증이 필요한 경우 서버는 401(권한 없음)을 반환합니다. 응답에는 서버가 기본 인증을 지원함을 나타내는 WWW-Authenticate 헤더가 포함됩니다.
  2. 클라이언트는 권한 부여 헤더에 클라이언트 자격 증명을 사용하여 다른 요청을 보냅니다. 자격 증명은 base64로 인코딩된 문자열 "name:password"로 형식이 지정됩니다. 자격 증명은 암호화되지 않습니다.

기본 인증은 "영역"의 컨텍스트 내에서 수행됩니다. 서버에는 WWW-Authenticate 헤더에 영역 이름이 포함됩니다. 사용자의 자격 증명은 해당 영역 내에서 유효합니다. 영역의 정확한 scope 서버에 의해 정의됩니다. 예를 들어 리소스를 분할하기 위해 여러 영역을 정의할 수 있습니다.

기본 인증 다이어그램

자격 증명은 암호화되지 않은 상태로 전송되므로 기본 인증은 HTTPS를 통해서만 안전합니다. Web API에서 SSL 작업을 참조하세요.

기본 인증은 CSRF 공격에도 취약합니다. 사용자가 자격 증명을 입력하면 브라우저는 세션 기간 동안 동일한 도메인에 대한 후속 요청에 따라 자격 증명을 자동으로 보냅니다. 여기에는 AJAX 요청이 포함됩니다. CSRF(교차 사이트 요청 위조) 공격 방지를 참조하세요.

IIS를 사용한 기본 인증

IIS는 기본 인증을 지원하지만 주의할 점은 사용자가 Windows 자격 증명에 대해 인증됩니다. 즉, 사용자에게 서버 도메인에 계정이 있어야 합니다. 공용 웹 사이트의 경우 일반적으로 ASP.NET 멤버 자격 공급자에 대해 인증하려고 합니다.

IIS를 사용하여 기본 인증을 사용하도록 설정하려면 ASP.NET 프로젝트의 Web.config 인증 모드를 "Windows"로 설정합니다.

<system.web>
    <authentication mode="Windows" />
</system.web>

이 모드에서 IIS는 Windows 자격 증명을 사용하여 인증합니다. 또한 IIS에서 기본 인증을 사용하도록 설정해야 합니다. IIS 관리자에서 기능 보기로 이동하여 인증을 선택하고 기본 인증을 사용하도록 설정합니다.

I S Manager dashboard 이미지

Web API 프로젝트에서 인증이 [Authorize] 필요한 컨트롤러 작업에 대한 특성을 추가합니다.

클라이언트는 요청에서 권한 부여 헤더를 설정하여 자체 인증합니다. 브라우저 클라이언트는 이 단계를 자동으로 수행합니다. 비브로우저 클라이언트는 헤더를 설정해야 합니다.

사용자 지정 멤버 자격을 사용한 기본 인증

언급했듯이 IIS에 기본 제공된 기본 인증은 Windows 자격 증명을 사용합니다. 즉, 호스팅 서버에서 사용자에 대한 계정을 만들어야 합니다. 그러나 인터넷 애플리케이션의 경우 사용자 계정은 일반적으로 외부 데이터베이스에 저장됩니다.

다음 코드는 기본 인증을 수행하는 HTTP 모듈입니다. 이 예제의 더미 메서드인 메서드를 대체 CheckPassword 하여 ASP.NET 멤버 자격 공급자를 쉽게 연결할 수 있습니다.

Web API 2에서는 HTTP 모듈 대신 인증 필터 또는 OWIN 미들웨어를 작성하는 것이 좋습니다.

namespace WebHostBasicAuth.Modules
{
    public class BasicAuthHttpModule : IHttpModule
    {
        private const string Realm = "My Realm";

        public void Init(HttpApplication context)
        {
            // Register event handlers
            context.AuthenticateRequest += OnApplicationAuthenticateRequest;
            context.EndRequest += OnApplicationEndRequest;
        }

        private static void SetPrincipal(IPrincipal principal)
        {
            Thread.CurrentPrincipal = principal;
            if (HttpContext.Current != null)
            {
                HttpContext.Current.User = principal;
            }
        }

        // TODO: Here is where you would validate the username and password.
        private static bool CheckPassword(string username, string password)
        {
            return username == "user" && password == "password";
        }

        private static void AuthenticateUser(string credentials)
        {
            try
            {
                var encoding = Encoding.GetEncoding("iso-8859-1");
                credentials = encoding.GetString(Convert.FromBase64String(credentials));

                int separator = credentials.IndexOf(':');
                string name = credentials.Substring(0, separator);
                string password = credentials.Substring(separator + 1);

                if (CheckPassword(name, password))
                {
                    var identity = new GenericIdentity(name);
                    SetPrincipal(new GenericPrincipal(identity, null));
                }
                else
                {
                    // Invalid username or password.
                    HttpContext.Current.Response.StatusCode = 401;
                }
            }
            catch (FormatException)
            {
                // Credentials were not formatted correctly.
                HttpContext.Current.Response.StatusCode = 401;
            }
        }

        private static void OnApplicationAuthenticateRequest(object sender, EventArgs e)
        {
            var request = HttpContext.Current.Request;
            var authHeader = request.Headers["Authorization"];
            if (authHeader != null)
            {
                var authHeaderVal = AuthenticationHeaderValue.Parse(authHeader);

                // RFC 2617 sec 1.2, "scheme" name is case-insensitive
                if (authHeaderVal.Scheme.Equals("basic",
                        StringComparison.OrdinalIgnoreCase) &&
                    authHeaderVal.Parameter != null)
                {
                    AuthenticateUser(authHeaderVal.Parameter);
                }
            }
        }

        // If the request was unauthorized, add the WWW-Authenticate header 
        // to the response.
        private static void OnApplicationEndRequest(object sender, EventArgs e)
        {
            var response = HttpContext.Current.Response;
            if (response.StatusCode == 401)
            {
                response.Headers.Add("WWW-Authenticate",
                    string.Format("Basic realm=\"{0}\"", Realm));
            }
        }

        public void Dispose() 
        {
        }
    }
}

HTTP 모듈을 사용하도록 설정하려면 system.webServer 섹션의 web.config 파일에 다음을 추가합니다.

<system.webServer>
    <modules>
      <add name="BasicAuthHttpModule" 
        type="WebHostBasicAuth.Modules.BasicAuthHttpModule, YourAssemblyName"/>
    </modules>

"YourAssemblyName"을 어셈블리 이름("dll" 확장 포함 안 함)으로 바꿉니다.

Forms 또는 Windows 인증과 같은 다른 인증 체계를 사용하지 않도록 설정해야 합니다.