서명 보증 ASP를 사용하는 방법.NET MVC OR WEBAPI의 인터페이스 보안

우리가 앱을 개발할 때, 앱은 백그라운드 서비스와 통신하여 데이터를 얻거나 제출해야 한다.만약 우리가 완벽한 안전 메커니즘이 없다면, 다른 사람의 요청에 의해 데이터를 조작하기 쉽다.
그래서 우리는 요청의 합법성을 확보하기 위해 어떤 안전 메커니즘을 사용해야 한다.현재 가장 자주 사용하는 방법은 모든 http 요청에 서명을 추가하고 서버에서 서명의 합법성을 검증하는 것이다. 서명이 합법적이면 응답하는 동작을 실행하고 서명이 불법이면 직접 요청을 거부한다.

서명 알고리즘


서명 알고리즘은 일반적으로 Hash 산열 알고리즘을 사용하는데 자주 사용하는 것은 MD5, SHA 시리즈 알고리즘이다.이런 알고리즘은 서로 다른 입력에 따라 서로 다른 결과를 계산할 수 있을 뿐만 아니라 충돌 확률도 매우 낮다.
서명 알고리즘은 암호화 알고리즘과 같은 것이 아니다.많은 학우들이 MD5를 사용하여 암호화한다고 말하지만, 사실 이것은 잘못된 것이다.서명 알고리즘은 원래 데이터의 정보를 포함하지 않기 때문에 원래의 데이터를 복구할 수 없습니다.
암호화 방법에 따라 암호화 방법은 암호화 결과에 따라 원래의 데이터를 다시 추산할 수 있다.
HMAC SHA는 더욱 안전한 서명 알고리즘으로서 키를 사용하여 서명 결과에 영향을 미친다.이렇게 하면 같은 입력과 서로 다른 키를 결합하면 서로 다른 서명을 얻을 수 있어 더욱 안전하다.

 public static string HmacSHA256(string secretKey,string plain)
        {
            var keyBytes = Encoding.UTF8.GetBytes(secretKey);
            var plainBytes = Encoding.UTF8.GetBytes(plain);

            using (var hmacsha256 = new HMACSHA256(keyBytes))
            {
                var sb = new StringBuilder();
                var hashValue = hmacsha256.ComputeHash(plainBytes);
                foreach (byte x in hashValue)
                {
                    sb.Append(String.Format("{0:x2}", x));
                }
                return sb.ToString();
            }
        }

서명된 매개변수


서명 알고리즘이 생겼는데 우리 서명한 내용은 어디에서 왔을까?
일반적으로 우리는 http가 요청하는queryString을 사용하고 시간 스탬프와 무작위 수를 붙여서 서명하는 매개 변수로 합니다.

 public static string MakeSignPlain(SortedDictionary<string,string> queryString,string time,string random )
        {
            var sb = new StringBuilder();
            foreach (var keyValue in queryString)
            {
                sb.AppendFormat("{0}={1}&", keyValue.Key, keyValue.Value);
            }
            if (sb.Length>1)
            {
                sb.Remove(sb.Length - 1, 1);
            }
            sb.Append(time);
            sb.Append(random);

            return sb.ToString().ToUpper();
        }

서명 확인


서명을 검증하는 것은 서비스 측에서 생산한 서명과 클라이언트가 생산한 서명이 계속 있는지 간단하게 비교하는 것이다.
주의해야 할 점은 타임 스탬프를 검증하는 것이 가장 좋고 서버 시간과 비교하면 앞뒤가 5분 차이가 나지 않는다는 것이다.이것도 간단한 Replay Attack 방지 수단이다.

 public static bool Valid(string requestSign,string signPlain,string time, string secretKey)
        {
            if (string.IsNullOrEmpty(time)||string.IsNullOrEmpty(requestSign)||string.IsNullOrEmpty(signPlain))
            {
                return false;
            }
            //is in range
            var now = DateTime.Now;
            long requestTime =0;
            if (long.TryParse(time,out requestTime))
            {
                var max = now.AddMinutes(5).ToString("yyyyMMddHHmmss");
                var min = now.AddMinutes(-5).ToString("yyyyMMddHHmmss");
                if (!(long.Parse(max) >= requestTime && long.Parse(min) <= requestTime))
                {
                    return false;
                }
              
            }
            else
            {
                return false;
            }

            //hashmac
            var sign = Encryption.HmacSHA256(secretKey, signPlain);

            return requestSign.Equals(sign, StringComparison.CurrentCultureIgnoreCase);
        }

ApiController 기본 클래스


위의 이 깔개가 있으면 우리는 기류에서 서명의 검증을 완성할 수 있다.클라이언트는 위에서 언급한 시간 스탬프, 무작위 수, 서명과 클라이언트의 ID를 http가 요청한 헤더에 넣어야 합니다.
우리는 기본 클래스의 OnActionExecuting에서 이 데이터를 조합하여 서명하는 매개 변수를 추출한 다음 클라이언트 ID에 따라 서명의 키를 가져와 같은 서명 알고리즘을 사용하여 서명을 계산합니다.클라이언트의 서명과 서비스 측의 서명이 일치하는지 비교합니다.
여기는 시범을 보이지 않겠습니다.

Replay Attack 예방


리셋 공격을 예방하는 데는 주로 두 가지가 있다.
  • 검사 시간 스탬프의 범위
  • 시간 스탬프와 서버 시간의 차이는 합리적인 범위 내에서 합법으로 간주된다.
  • 캐시 서명
  • 요청할 때마다 사인이 나왔는지 아닌지 판단해 보세요.나타나면 불법 요청으로 간주한다.
    시간 스탬프와 무작위 수의 존재가 있기 때문에 이론적으로 매번 요청한 서명은 중복될 수 없다.

    클라이언트 호출


    여기 C# 서명하고 http 인터페이스를 호출하는 코드를 보여 드리겠습니다.
    
     [TestMethod()]
            public void GetUserTest()
            {
                string url = "http://localhost:8090/api/test/GetUser";
                string userId = "A39891D4-6CEF-4538-A562-3A422CA9C17A";
                string appId = "100001";
                string secretKey = "M/vkPOWXgBa7GnRd73t7j+jsKfbZtb+f";
                string rumdon = Guid.NewGuid().ToString();
                string time = DateTime.Now.ToString("yyyyMMddHHmmss");
    
                //make signture plain text
                var sortDict = new SortedDictionary<string, string>()
                {
                    {"userId",userId }
                };
                var signPlain = new StringBuilder();
                foreach (var keyValue in sortDict)
                {
                    signPlain.AppendFormat("{0}={1}&", keyValue.Key, keyValue.Value);
                }
                if (signPlain.Length > 1)
                {
                    //remove last &
                    signPlain.Remove(signPlain.Length - 1, 1);
                }
                signPlain.Append(time);
                signPlain.Append(random);
    
                Console.WriteLine("sign plain:{0}", signPlain.ToString().ToUpper());
                //make sign
                var sign = Encryption.HmacSHA256(secretKey, signPlain.ToString().ToUpper());
                Console.WriteLine("sign:{0}", sign);
    
                string requestUrl = string.Format("{0}?{1}={2}", url, "userId", userId);
                HttpWebRequest request = (HttpWebRequest)WebRequest.Create(requestUrl);
                request.Method = "GET";
                //add headers
                request.Headers.Add("time", time);
                request.Headers.Add("appId", appId);
                request.Headers.Add("random", random);
                request.Headers.Add("sign", sign);
                //
                //start request
                try
                {
                    using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
                    {
                        var responseStream = response.GetResponseStream();
                        if (responseStream != null)
                        {
                            using (StreamReader reader = new StreamReader(responseStream))
                            {
                                var content = reader.ReadToEnd();
    
                                Console.WriteLine(content);
                            }
                        }
                    }
                }
                catch (WebException ex)
                {
                    using (HttpWebResponse response = (HttpWebResponse)ex.Response)
                    {
                        var responseStream = response.GetResponseStream();
                        if (responseStream != null)
                        {
                            using (StreamReader reader = new StreamReader(responseStream))
                            {
                                var content = reader.ReadToEnd();
                                Console.WriteLine(content);
                            }
                        }
                    }
                }
            }
    지금까지 ASP에 서명 보증을 사용하는 방법입니다.NET MVC OR WEBAPI의 인터페이스 보안에 대한 자세한 내용은 서명으로 ASP를 보증하는 것에 관한 것입니다.NET MVC OR WEBAPI의 인터페이스 보안 자료

    좋은 웹페이지 즐겨찾기