-
Http Module을 이용한 Custom Authentication(커스텀인증) 구현Development/ASP.NET 2020. 6. 6. 20:57
HTTP 인증은 Authorization 헤더 안의 Basic(기본인증), Bearer(OAuth) 등의 스킴에 해당하는 정보를 기반으로 이루어지게 됩니다.
IIS에서는 폼인증, 가장인증, 기본인증 등 다양한 인증 방식을 제공하고 있는데, 기본적으로 지원하는 인증방식 외에 HTTP Module을 이용하여 커스텀 인증을 구현하는 예제를 소개시켜 드리려고 합니다.먼저 HttpModule 인터페이스를 구현하는 모듈을 만들어야 합니다.
아래 예제는, 기본 인증을 통과한 이후에 추가적으로 IP 및 API Key정보의 유효성을 검증하도록 구현되어 있습니다.123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120using System;using System.Web;using System.Data;using System.Text;using System.Web.UI;using System.Collections.Generic;using System.Linq;using System.Net.Http;using System.Net.Http.Headers;using System.Security.Principal;using System.Threading;using System.Collections;namespace MyAuth{public class CustomAuthentication : IHttpModule{private const string Realm = "AuthKey";public static Hashtable _ApiKey = Hashtable.Synchronized(new Hashtable());public void Init(HttpApplication context){context.AuthenticateRequest += OnApplicationAuthenticateRequest;context.EndRequest += OnApplicationEndRequest;}private static void SetPrincipal(IPrincipal principal){Thread.CurrentPrincipal = principal;if (HttpContext.Current != null){HttpContext.Current.User = principal;}}//Credential Validateprivate static bool CheckCredentials(string credentials, string clientIP){bool check = false;string sAPIKey = ConfigurationManager.AppSettings["CustomAuthenticationKey"].ToString();string[] arrAcessIP = ConfigurationManager.AppSettings["AcessIP"].ToString().Split(',');//IP 체크 및 API Key 체크if(arrAccessIP.Contains(clientIP) && credentials.Equals(sAPIKEY))check = true;return check;}//인증에 필요한 추가 자원 추출private static void Authenticate(string credentials){try{string decodedCredentials = Encoding.UTF8.GetString(Convert.FromBase64String(credentials));HttpContext context = HttpContext.Current;string clientIP = context.Request.ServerVariables["HTTP_X_FORWARDED_FOR"]; // Proxy 등을 타고올때, 원본 IP 정보를 헤더에 넘겨줄 경우if (string.IsNullOrEmpty(clientIP)){clientIP = context.Request.ServerVariables["REMOTE_ADDR"];}else{// L4 및 여러 네트워크 장비를 통해서 유입 될 경우 , 형태로 IP가 들어 올 수 있음string[] addresses = clientIP.Split(',');if (addresses.Length != 0){clientIP = addresses[0];}}if (string.IsNullOrEmpty(clientIP))clientIP = context.Request.UserHostAddress;if (CheckCredentials(decodedCredentials, clientIP)){var identity = new GenericIdentity(decodedCredentials);SetPrincipal(new GenericPrincipal(identity, null));}else{HttpContext.Current.Response.StatusCode = 401;}}catch (FormatException e){HttpContext.Current.Response.StatusCode = 401;}}private void OnApplicationAuthenticateRequest(object sender, EventArgs e){var request = HttpContext.Current.Request;var authHeader = request.Headers["Authorization"];if (authHeader != null){var authHeaderVal = AuthenticationHeaderValue.Parse(authHeader);if (authHeaderVal.Scheme.Equals("basic", StringComparison.OrdinalIgnoreCase) && authHeaderVal.Parameter != null){Authenticate(authHeaderVal.Parameter);}else{HttpContext.Current.Response.StatusCode = 401;}}else{HttpContext.Current.Response.StatusCode = 401;}}private 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(){}public CustomAuthentication(){}}}cs 저는 Soap WebService 형태로 API를 구현하였기 때문에, SoapHeader를 상속받는 APIKey에 해당하는 인증 클래스를 별도로 추가 구현 하였습니다.
1234567891011121314151617181920212223using System;using System.Web;using System.Data;using System.Text;using System.Web.UI;using System.Collections.Generic;using System.Linq;using System.Net.Http;using System.Net.Http.Headers;using System.Security.Principal;using System.Threading;using System.Collections;namespace OrgWebAPI{public class Authentication : System.Web.Services.Protocols.SoapHeader{private string _APIKey = string.Empty;public string APIKey { get { return _APIKey; } set { _APIKey = value; } }public Authentication(){}}}cs 커스텀모듈을 적용하기 위하여, Web.config에 아래와 같이 설정을 해줍니다.
123456789101112131415161718192021222324252627282930313233<?xml version="1.0" encoding="utf-8"?><configuration><appSettings><!-- Custom Authentication--><add key="CustomAuthenticationKey" value="2F9F15BF-2A63-EC0E-EA22-8EAF55E5BD4B" /><add key="AcessIP" value="127.0.0.1,123.111.111.2,121.111.1.2" /></appSettings><system.web><webServices><protocols><add name="HttpPost" /></protocols></webServices><compilation debug="true" targetFramework="4.5.2"/><httpRuntime targetFramework="4.5.2"/><httpModules><add name="ApplicationInsightsWebTracking" type="Microsoft.ApplicationInsights.Web.ApplicationInsightsHttpModule, Microsoft.AI.Web"/></httpModules></system.web><system.webServer><handlers><add name="ScriptHandlerFactory" verb="*" path="*.asmx"type="System.Web.Script.Services.ScriptHandlerFactory, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"resourceType="Unspecified"/></handlers><!-- 커스팀 인증 모듈 추가 --><modules><add name="CustomAuthentication" type="MyAuth.CustomAuthentication"/></modules><validation validateIntegratedModeConfiguration="false"/></system.webServer></configuration>cs 기본인증의 경우 Basic 스킴에 아이디/패스워드 정보를 인코딩 하여 전달하기 때문에 보안에 취약 할 수 있습니다.
커스텀 인증 모듈을 통해 API Key 와 IP 등을 추가체크하여 보안을 좀더 강화해보는것은 어떨까요?'Development > ASP.NET' 카테고리의 다른 글
사파리에서 한글 첨부파일명이 깨질 경우 (0) 2020.05.09 ClientSide <-> ServerSide간 호환되는 비대칭키 알고리즘 로직 (0) 2020.03.21 IIS Web.config에 설정해주어야 하는 보안 서버 설정 (0) 2020.03.07 페이지 수명 주기 (0) 2019.07.07 댓글