나누고 싶은 개발 이야기

Data Engineer로서 기록하고 공유하고 싶은 기술들. 책과 함께 이야기합니다.

Language/C#

HTTP 인증(1) - Basic

devidea 2013. 9. 4. 14:50

HTTP 1.1에서 규정하고 있는 인증의 종류에는 Basic 인증과 Digest 인증이 있습니다.

http://www.ietf.org/rfc/rfc2617.txt


HTTP 요청에 대해서 인증 처리가 되지 않은 경우, 스테이터스 코드 401을 통해 클라이언트에 리소스 접근에 필요한 인증정보를 통지하게 된다.


[요청]

POST /api/employees/12345 HTTP/1.1

Host: basic.auth.com


[응답]

HTTP/1.1 401 Unauthorized

WWW-Authenticate: Basic realm="basic.auth.com"


WWW-Authenticate 헤더에 의해서 클라이언트는 서버가 제공하는 인증 방식을 이해할 수 있고, 그 방식에 따른 인증 정보를 보낼 수 있다.


Basic 인증

Basic 인증은 유저 이름과 패스워드로 인증을 합니다. 유저 이름과 패스워드는 Authorization 헤더에 넣어 요청마다 전송한다. Authorization 헤더의 내용은 유저이름:패스워드 로 구성하고 Base64 인코딩한 문자열이 된다. 문자열은 암호처럼 보이지만 실상은 간단히 디코딩이 가능하다.

따라서 네트워크 상에서 암호가 흘러다닌다고 생각할 수 있다. 보다 높은 정도의 보안 강도를 위해서는 SSL와 TLS를 사용해 HTTPS 통신을 사용해야 한다.


다음 그림으로 Basic 인증의 흐름을 볼 수 있다.


아래 코드는 Basic 인증을 처리하기 위한 예제입니다.

[Authorize] 특성을 Controller에 추가해 준다.

public class AuthenticationHandler : DelegatingHandler
{
private const string SCHEME = "Basic";
private const string REALM = "basic.auth.com";
protected async override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
try
{
var headers = request.Headers;
if (headers.Authorization != null && SCHEME.Equals(headers.Authorization.Scheme))
{
Encoding encoding = Encoding.GetEncoding("iso-8859-1");
string credentials = encoding.GetString(Convert.FromBase64String(
headers.Authorization.Parameter));
string[] parts = credentials.Split(':');
string userId = parts[0].Trim();
string password = parts[1].Trim();
// TODO - Do authentication of userId and password against your credentials store here
if (true)
{
var claims = new List<Claim>
{
new Claim(ClaimTypes.Name, userId),
new Claim(ClaimTypes.AuthenticationMethod, AuthenticationMethods.Password)
};
var principal = new ClaimsPrincipal(new[] { new ClaimsIdentity(claims, SCHEME) });
Thread.CurrentPrincipal = principal;
if (HttpContext.Current != null)
HttpContext.Current.User = principal;
}
}
var response = await base.SendAsync(request, cancellationToken);
if (response.StatusCode == HttpStatusCode.Unauthorized)
{
response.Headers.WwwAuthenticate.Add(new AuthenticationHeaderValue(SCHEME, "realm=\"" + REALM + "\""));
}
return response;
}
catch (Exception)
{
var response = request.CreateResponse(HttpStatusCode.Unauthorized);
response.Headers.WwwAuthenticate.Add(new AuthenticationHeaderValue(SCHEME));
return response;
}
}
}

[Authorize] 특성이 동작하도록 하기 위해서 아래와 같이 구현한 Handler를 추가해 준다.

public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
// Basic 인증과 관련된 Handler를 추가한다.
config.MessageHandlers.Add(new AuthenticationHandler());
}
}
view raw WebApiConfig.cs hosted with ❤ by GitHub



아래 코드는 Basic 인증으로 요청하는 Clinet 예제이다.

AuthenticationHeaderValue를 통해서 헤더값에 인증을 위한 값을 할당하는 것을 볼 수 있다.

class Program
{
static void Main(string[] args)
{
// Testing Basic Authentication
using (HttpClient client = new HttpClient())
{
// Basic 인증을 위한 유저이름:패스워드를 Base64 인코딩해서 헤더에 넣는다.
string creds = String.Format("{0}:{1}", "john", "john");
byte[] bytes = Encoding.ASCII.GetBytes(creds);
var header = new AuthenticationHeaderValue("Basic", Convert.ToBase64String(bytes));
client.DefaultRequestHeaders.Authorization = header;
var postData = new List<KeyValuePair<string, string>>();
postData.Add(new KeyValuePair<string, string>("Name", "John Q Human"));
HttpContent content = new FormUrlEncodedContent(postData);
string response = String.Empty;
var responseMessage = client.PostAsync("http://basic.auth.com/api/employees/12345", content).Result;
if(responseMessage.IsSuccessStatusCode)
response = responseMessage.Content.ReadAsStringAsync().Result;
Console.WriteLine(response);
}
}
}
view raw authmain.cs hosted with ❤ by GitHub



아래 그림은 Fiddler로 요청을 살펴본 내용이다. Authorization으로 헤더에 값이 들어 있는 것을 볼 수 있다.


리소드에 대한 인증처리가 잘 되었을 경우 200 코드.



[참고]

Pro ASP.NET Web API Security

웹 개발자를 위한 웹을 지탱하는 기술



반응형

'Language > C#' 카테고리의 다른 글

[ASP.NET MVC] Complie-Time에 View 에러 확인  (0) 2013.12.20
HttpClient  (0) 2013.06.29
JsonResult Serializer 변경  (0) 2013.05.10
날짜 형식 찾기 위한 정규식  (1) 2010.04.13