public class AuthorizationAttribute : FilterAttribute, IAuthorizationFilter    

    {


        void IAuthorizationFilter.OnAuthorization(AuthorizationContext filterContext)

        {

            HttpCookie cookie = filterContext.RequestContext.HttpContext.Request.Cookies.Get("AccessToken");

            Controller controller = filterContext.Controller as Controller;


            if (cookie == null || string.IsNullOrEmpty(WebConfig.SessionUserID))

            {                

                controller.HttpContext.Response.Redirect(redirectUrl);

                

                //filterContext.Result = new RedirectToRouteResult( new RouteValueDictionary { 

                //    { "controller", "Home" }

                //    , { "action", "Index" } 

                //});

            }


            if (!this.dataEncryption.PBKDF2Compare(cookie.Value, this.dataEncryption.PBKDF2(this.dataEncryption.SHA1Decrypt(WebConfig.SessionUserID, WebConfig.Salt), WebConfig.Salt, int.Parse(WebConfig.Iteration))))

            {

                controller.HttpContext.Response.Redirect(redirectUrl);

            }

        }


        private DataEncryption dataEncryption = new DataEncryption();

        private string redirectUrl = WebConfig.SignInUrl;

        public string RedirectUrl

        {

            get { return redirectUrl; }

            set { redirectUrl = value; }

        }


    }


Posted by James jangjeonghun

class ImpDataEncryption : IDataEncryption

    {        


        string IDataEncryption.SHA1Encrypt(string key, string textToEncrypt)

        {

            byte[] keyArray = Encoding.UTF8.GetBytes(key);

            byte[] toEncryptArray = Encoding.UTF8.GetBytes(textToEncrypt);


            RijndaelManaged rijndaelManaged = new RijndaelManaged();

            rijndaelManaged.Mode = CipherMode.CBC;

            rijndaelManaged.Padding = PaddingMode.PKCS7;

            rijndaelManaged.KeySize = 128;

            rijndaelManaged.BlockSize = 128;


            byte[] keyBytes = new byte[16];

            int len = keyArray.Length;

            if (len > keyBytes.Length)

            {

                len = keyBytes.Length;

            }


            Array.Copy(keyArray, keyBytes, len);

            rijndaelManaged.Key = keyBytes;

            rijndaelManaged.IV = keyBytes;


            ICryptoTransform iCryptoTransform = rijndaelManaged.CreateEncryptor();

            byte[] resultArray = iCryptoTransform.TransformFinalBlock(toEncryptArray, 0, toEncryptArray.Length);


            return Convert.ToBase64String(resultArray, 0, resultArray.Length);

        }


        string IDataEncryption.SHA1Decrypt(string key, string encryptText)

        {

            byte[] keyArray = Encoding.UTF8.GetBytes(key);

            byte[] toEncryptArray = Convert.FromBase64String(encryptText);


            RijndaelManaged rijndaelManaged = new RijndaelManaged();

            rijndaelManaged.Mode = CipherMode.CBC;

            rijndaelManaged.Padding = PaddingMode.PKCS7;

            rijndaelManaged.KeySize = 128;

            rijndaelManaged.BlockSize = 128;


            byte[] keyBytes = new byte[16];

            int len = keyArray.Length;

            if (len > keyBytes.Length)

            {

                len = keyBytes.Length;

            }


            Array.Copy(keyArray, keyBytes, len);

            rijndaelManaged.Key = keyBytes;

            rijndaelManaged.IV = keyBytes;


            ICryptoTransform iCryptoTransform = rijndaelManaged.CreateDecryptor();

            byte[] resultArray = iCryptoTransform.TransformFinalBlock(toEncryptArray, 0, toEncryptArray.Length);


            return UTF8Encoding.UTF8.GetString(resultArray);

        }


        string IDataEncryption.PBKDF2(string password, string salt, int iteration)

        {

            byte[] saltBytes = Encoding.UTF8.GetBytes(salt);


            var pbkdf2 = new Rfc2898DeriveBytes(password, saltBytes, iteration);

            var key = pbkdf2.GetBytes(64);


            return Convert.ToBase64String(key);

        }


        bool IDataEncryption.PBKDF2Compare(string passwordHash1, string passwordHash2)

        {

            if (passwordHash1 == null || passwordHash2 == null) { return false; }


            int minLength = Math.Min(passwordHash1.Length, passwordHash2.Length);

            int result = 0;


            for (int i = 0; i < minLength; i++)

            {

                result |= passwordHash1[i] ^ passwordHash2[i];

            }


            return 0 == result;

        }

        

    }


Posted by James jangjeonghun
기본 정보

세션은 서버에서 생성되며 서버에서 관리가 된다.

쿠키는 서버에서도 생성할 수 있지만 클라이언트에서도 생성할 수있다.

쿠키는 클라이언트에 의해 관리된다.


쿠키가 클라이언트에 의해 관리 되다보니 보안상 취약하다.

쿠키의 보안상의 단점(쿠키 스니핑 or 스푸핑)을 보안하여 등장한 것이 바로 세션이다.


세션은 서버의 메모리에 저장되며 많은 양의 세션 정보들은 

메모리 풀 상태를 발생시킬 수 있다. 이는 서비스에 안 좋은 영향을 줄 수있다는 애기이다.


추가 정보

세션의 유지시간은 크게 두 곳에서 제어된다.


첫번째는 응용 프로그램에서 두번째는 웹 서버에서이다.


응용 프로그램에서 세션의 유지시간을 제어하는 방법은 다음과 같다.

<sessionState mode="InProc" cookieless="false" timeout="120"></sessionState>


서버에서 세션의 유지시간을 제어하는 방법은 다음과 같다.

IIS 관리자 -> 응용 프로그램 풀(사이트에 맞은 풀 선택) -> 고급 설정 -> 프로세서 모델 -> 유휴 시간 제한(분)


* 여기서 중요한 것은 세션의 유지시간은 결국 웹 서버에의해 결정이된다는 것이다.

왜냐하면 응용 프로그램을 서비스하는것은 결국엔 웹 서버이닌깐!


하고싶은 말

쿠키와 세션에 관한 내용을 언급한 이유는 웹 사이트에서 로그인 처리에 관한 이야기를 하고자 함이다.

웹에서 로그인 처리시 크게 2가지를 생각해야하는대 그 첫번째는 인증이고 두번째는 권한이다.


인증이라함은 사용자가 웹 사이트에 등록된 회원인지 확인하는 부분이고(아이디와 비밀번호를 이용한 일반적인 방법)

권한이라함은 사용자가 특정 페이지에 접근했을 했을때 해당 페이지에 대한 접근 가능 여부를 판단하는 것을 뜻한다.


인증 과정에서 생각해 볼만한 사항

1. 보안 통신(Secure Socket Layer)

2. 암화화된 비밀번호를 비교(ex : PBKDF2)


권한 과정에서 생각해 볼만한 사항

1. 엑센스 토큰(Access Token) 사용

* 쿠키를 사용하는 경우 여러가지 보안 이슈가 있는대 

대표적으로 쿠키 스푸핑, 암호화 되지 않은 회원 정보(회원ID)등 많은 보안상의 문제점들이 존재한다.

쿠키 자체가 클라이언트에 의해 관리되는 정보이기 때문에 절대적으로 신뢰해서는 않된다.


그래서 나는...

인증 부분을 크게 문제가 없다. 회사에서 인증서를 사주면 적용하면 되고 안사주면 할 수 없고

C#의 경우 PBKDF2 암호화 기능을 지원하기 때문에 회원 가입시 암호화된 비밀번호를 DB에 등록하고 있다.


문제는 권한을 위한 엑센스 토근인대...

처음에는 쿠키만을 이용하여 엑센스 토큰을 생성하려고 했다. 이유는 세션의 경우 단일 서버에서 문제가 없지만

다중 서버에서는 문제가 있다.(해결 방법이 없는것은 아님) 쿠키만을 이용하여 처리하기가 생각만큼 쉽지 않았다.

 

문제는... 

사용자만이 알고 있는 식별 가능한 클라이언트의 고유값(예를들면 사용자 ID 같은)을 서버에 저장해야한다는 것이다.

사실 이부분을 해결하고자 등장한 것이 Session 이다. 그러니 Session을 사용하지 않고 해결하기란 그리 쉽지 않았다.ㅜㅜ


결론는...

1. 로그인시 사용자의 아이디를 Session에 저장한다.

2. PBKDF2 방식으로 암호화된 엑센스 토큰을 발급한다.

3. 사용자가 페이지 접근시 엑센스 토큰을 체크한다.



Posted by James jangjeonghun

티스토리 툴바