Problem
기존 오래된 코드의 리펙토링 때문에 시작된 작업이다. ASP.NET
의 Web API
1를 이용해서 구현이 되어있었고 다른 외부 서비스와(사전에 미리 프로토콜이 약속된 대상과) 통신을 하고 있었다. 보안의 이유로 각 웹 메소드는 파라미터와 추가로 파라미터를 검증할 수 있는 추가 파라미터(HMAC
1과 비슷한)를 입력하여 요청을 검증하고 있었다. 아래는 기존 나쁜 냄새가 났던 코드의 예시이다. 동작하는 실제 코드는 아니다.
public ActionResult Method1(String param1, String param1, String hmac)
{
// validation
if(hmac(param1, param2) != hmac) {
throw Error
}
return ...
}
public ActionResult Method2(String param1, String param1, String param2, String hmac)
{
// validation
if(hmac(param1, param2, param3) != hmac) {
throw Error
}
return ...
}
validation 블럭이 매번 반복되는 것을 볼 수 있었고, 개선해야할 필요가 있었다.
Solution
- C# 에서도 Java의 어노테이션 같은 Attribute를 가능한지 먼저 확인해봤다2.
- 어셈블리, 형식, 메서드, 속성에 적용할 수 있는 방법이 있음
- Attribute를 연결하면 프로그램 엔터티와 연결되면 리플렉션이라는 기법을 사용하여 런타임에 확인됨
- 즉, 아래처럼 사용할 수 있었다.
[HMACParamsValidator] public ActionResult Method1(String param1, String param1, String hmac) { return Json(new { Result = ChargeService.RollbackResultValue.SUCCESS.ToString() }, JsonRequestBehavior.DenyGet); }
- 원하는 동작을 하기 위한 HMACParamsValidator를 구현한다.
- OnActionExecuting 에서 입력받은 파라미터를 확인하고 hmac 파라미터는 별도 처리할 수 있다.
using System; using System.Collections.Generic; using System.Reflection; using System.Web.Mvc; namespace App_Code { [AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor)] internal class HMACParamsValidatorAttribute : ActionFilterAttribute { public HMACParamsValidatorAttribute() { } public override void OnActionExecuted(ActionExecutedContext filterContext) { base.OnActionExecuted(filterContext); } public override void OnActionExecuting(ActionExecutingContext filterContext) { base.OnActionExecuting(filterContext); List<String> list = new List<String>(); String hmac = ""; foreach (KeyValuePair<string, object> items in filterContext.ActionParameters) { if (items.Key.Equals("hmac", StringComparison.CurrentCultureIgnoreCase)) { hmac = items.Value.ToString(); } else { if (items.Value != null) { list.Add(items.Value.ToString()); } } } list.Add(Key); if(hmac != hmac(list)) // hmac 구현 필요 (예시) { filterContext.Result = new JsonResult() { Data = NOT_MATCH_HMAC.ToString(), // NOT_MATCH_HMAC 정의 필요 JsonRequestBehavior = JsonRequestBehavior.AllowGet }; } } public override void OnResultExecuted(ResultExecutedContext filterContext) { base.OnResultExecuted(filterContext); } public override void OnResultExecuting(ResultExecutingContext filterContext) { base.OnResultExecuting(filterContext); } } }