ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [암호화] 암호화에 대하여
    기타 2023. 7. 9. 23:29

    개요

    • 기능 추가 중 그동안 암호화에 대해 기본적인 세팅에 의존하여 설정한부분이 많아 이번 기회에 기본암호화의 방식과 사용중인 암호화 방식에 대해 정리를 진행하며
    • Preview 에 적용할 암호화 방식에 대해 설명함

    암호화 분류표

    암호화 종류


    주요 사용중인 암호화

    양방향 암호화

    AES : 블록 암호화를 이용한 대칭키 암호 알고리즘

    • CBC
      • CBC 에서 IV(initialization vector)는 첫 번째 블록의 암호화에 사용되며, 그 이후의 블록에서는 이전 블록의 암호문이 다음 블록의 암호화에 사용됩니다. 그렇기에 IV 는 보통 랜덤값이 들어가며 IV 자체는 보안을 위해 숨겨야하는 값이 아니므로 암호문과 함께 저장되거나 전송되어도 안전합니다.

    CBC

    //랜덤값을 이용한 Salt 생성
    SecureRandom random = new SecureRandom();
    byte bytes[] = new byte[20];
                random.nextBytes(bytes);
    byte[] saltBytes = bytes;
    
    SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
    
    PBEKeySpec spec = new PBEKeySpec(key.toCharArray(), saltBytes, hashcount, 256); // key, hashcount
    
    SecretKey secretKey = factory.generateSecret(spec);
    SecretKeySpec secret = new SecretKeySpec(secretKey.getEncoded(), "AES");
    
    // 알고리즘/모드/패딩
    // CBC : Cipher Block Chaining Mode
    Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
                cipher.init(Cipher.ENCRYPT_MODE, secret);
    AlgorithmParameters params = cipher.getParameters();
    // Initial Vector(1단계 암호화 블록용)
    byte[] ivBytes = params.getParameterSpec(IvParameterSpec.class).getIV();
    
    byte[] encryptedTextBytes = cipher.doFinal(msg.getBytes("UTF-8"));
    
    byte[] buffer = new byte[saltBytes.length + ivBytes.length + encryptedTextBytes.length];
    
    //            sRet = Base64.getEncoder().encodeToString(buffer);
    sRet = URLEncoder.encode(Base64.getEncoder().encodeToString(buffer), "UTF-8");

    Salt 설정

    • Salt 는 레인보우 테이블 공격에 대한 방어책으로 Password 뒤에 salt 값을 붙여서 암호화 하도록 설정한다
      • 예를들어 유저가 가장 많이 사용하는 Password 인 1234 로 암호화 했을때 암호화 결과값이 "e10adc3949ba59abbe56e057f20f883e" 라면
      • 1234 + !Fv5678@#!(Salt) 값을 붙이게 된다면 암호화 되는 값은 1234!Fv5678@#! 일태니 암호화 결과값은 달라지게 된다.
      • Salt 는 전송할때 함께 전송하거나 데이터베이스에 함께 저장하게 된다. 
    • Billing 에서는 IV 값을 생성 후 암호화할때 사용하는 방식이 아닌 cipher.init(Cipher.ENCRYPT_MODE, secret); 를 통해 랜덤한 IV 를 생성해 암호화를 진행
    • byte[] ivBytes = params.getParameterSpec(IvParameterSpec.class).getIV(); 를 통해 위 코드에서 생성한 IV 값을 가져오고있습니다.

    Kakao 인증

    CTR 암호화
    CTR 복호화

    • 암호화 종류는 AES 의 CTR
    • CTR 작동 순서
      • 초기 벡터(IV)를 설정, IV 는 암호화 과정에서 사용되는 카운터의 초기값
      • 이 초기 벡터(카운터)를 암호화
      • 암호화된 카운터와 평문 블록을 XOR 연산하여 암호문 블록을 생성
      • 카운터를 증가시키고, 다음 평문 블록에 대해 이 과정을 반복
    • CBC 와의 차이점으로는 CBC 는 이전 블록에 대한 결과값이 필요하기 때문에 병렬수행이 불가능 그러나 CTR 은 count 값에 따라 암호화하기 때문에 병렬수행이 가능함
    • CBC 는 블록단위 이지만 CTR 은 스트림 단위로 암호화
    • 코드상 "AES/CTR/NoPadding" 으로 설정되어있는데 스트림단위로 암호화를 하다보니 패딩할 필요가 없음(NoPadding)
    • KaKao 의 벡터(IV) 는 고정값 (CTR 의 IV 가 카운터로 사용됨) → 통신시 IV 값을 따로 전달하지 않는것으로 보임 
    • kakaoSecretKey 를 이용한 암호화키 사용

    단방향 암호화

    단방향 암호화의 예시
    단방향 암호화의 비교

    • Spring boot Security 에서 PasswordEncoder 를 상속받은 암호화 Class 설명 
      • Pbkdf2PasswordEncoder
        • PBKDF2 (Password-Based Key Derivation Function 2)를 사용하여 비밀번호를 암호화합니다. 이 방법은 salt를 추가하고 해시를 여러 번 적용함으로써 레인보우 테이블 공격 등에 대한 보안 기능
      • BCryptPasswordEncoder
        • 1999년에 발표
        • BCrypt는 Blowfish 암호를 기반으로 한 비밀번호 해시 함수,주요한 특징 중 하나는 해시를 생성할 때 사용되는 작업 요소(work factor)를 조정할 수 있다는게 큰 차이점. 이 작업 요소를 늘리면 해시를 생성하는 데 걸리는 시간이 늘어나므로, 암호를 무작위로 대입하는 공격(브루트포스 공격)에 대한 보안능력이 높아지지만 이로 인해 레거시 시스템에서는 성능 문제가 발생할 수 있음
        • Backoffice 의 관리자 로그인 비밀번호 관련 단방향 암호화는 BCryptPasswordEncoder 를 사용함
          • 예시($2a$10$CrT7s5mqbxHPYCX2DwMTseHSCWmsDcthYdeccg6M65Vb7VBDR8yjW) 가있을때 해당 암호문에 대한 설명으로
            • $2a$ : 이 부분은 암호화에 사용된 BCrypt의 버전
            • 10$ : 이 부분은 스트레칭(iteration) 횟수를 나타내는 로그 라운드(log rounds), 여기서는 2^10, 즉 1024회의 해싱이 발생했음을 의미함
              • 로그 라운드(log rounds)는 BCrypt 해싱에서 사용되는 특정 개념으로, 원래의 비밀번호 해싱 과정을 반복하는 횟수를 의미함, 이는 2의 승수로 표현되며, 즉 로그 라운드가 10이라면 비밀번호 해싱 과정은 2^10, 즉 1024번 반복됨
            • CrT7s5mqbxHPYCX2DwMTse : 이 부분이 솔트(salt)를 나타냅니다. Base64로 인코딩된 22자리 문자열이며, 암호화 시 무작위로 생성됩니다.
              • 원래의 데이터(주로 사용자의 비밀번호)에 추가되는 임의의 데이터를 의미 ex. password123 + 456(salt) = password123456
            • HSCWmsDcthYdeccg6M65Vb7VBDR8yjW : 이 부분이 실제 비밀번호의 해시값(hash), 이 역시 Base64로 인코딩된 문자열이며, 솔트와 원본 비밀번호를 사용해 생성
      • SCryptPasswordEncoder
        • 2009년에 발표 
        • SCrypt는 비밀번호 기반 키 유도 함수, BCrypt와 마찬가지로 SCrypt는 작업 요소를 조정할 수 있지만, 메모리 사용량도 함께 조정할 수 있다는 점이 다르고, 이로 인해 대량의 병렬 하드웨어(예: GPU)를 이용한 공격에 대해 보안능력이 더 높다. SCrypt는 이런 특성 때문에 암호화된 통신의 키 유도 뿐만 아니라, 암호화폐의 작업증명 알고리즘으로 사용됨
        • BCrypt는 웹 애플리케이션에서 널리 사용되는 반면, SCrypt는 주로 암호화폐(예: Litecoin)에서 채굴 알고리즘으로 사용됨
      •  Argon2PasswordEncoder
        • 2015년에 비밀번호 해싱 경쟁에서 우승한 Argon2 해싱 함수를 사용하여 비밀번호를 암호화 하며, 최신의 해시 공격에 대해 보안이 뛰어남
        • 기존 암호화방식과 동일하지만 대량의 메모리를 사용해서 GPU나 ASIC 같은 특수 하드웨어를 사용한 대량 계산 공격에 대한 보안 기능
        • CPU 코어를 여러개 사용하는 병렬처리를 이용해서 계산을 동시에 수행함
        • Argon2는 side-channel 공격에 대한 보안 능력 있는데, 이는 공격자가 비밀번호 해싱 과정 중 발생하는 물리적 신호(예: 전력 소비, 소음 등)를 분석하여 비밀번호를 추정하는 공격에 대한 보안 기능이 있음
      • spring boot security 에서 단방향 암호화할 경우 양방향 암호화처럼 따로 SecretKey 값 같은게 필요하지않음
      • SecretKey 가 없기에 암호화한 결과값(ex.($2a$10$CrT7s5mqbxHPYCX2DwMTseHSCWmsDcthYdeccg6M65Vb7VBDR8yjW)만 있다면 해당 암호가 맞는지 다른서버에서도 비교가 가능함
    •  
      •  

    결론

    비밀번호 관련 단방향 암호화 알고리즘은 Argon2 를 사용하는것이 좋을것으로 보임

    이유

    • 양방향 암호화의 경우 사용자의 비밀번호를 운영자가 알 수 있기에 거부감을 가질수 있음
    • 단방향 암호화중 가장 대중적으로 사용중인 BCrypt 와 최근 떠오르는 Argon2 가 적당해보임
    • BCrypt와 Argon2 둘다 무차별 공격(brute-force attacks), 사전 공격(dictionary attacks), 그리고 레인보우 테이블 공격(rainbow table attacks) 등에 보안능력을 가짐 
    • 하지만 Argon2 은 병렬처리 및 대량의 메모리를 이용한 계산을 하기에 대량 계산 공격에 대한 보안 기능도 있으며 side-channel 공격에 대한 보안능력도 있음
    • 최신알고리즘이며 최근 가장 떠오르고 있는 비밀번호와 관련된 단방향 암호화 알고리즘인 Argon2 를 사용하는것이 미래를 생각해서 개발하는데 큰 도움이 될것으로 판단됨
    • 아직 단방향암호화에 대해 적용된곳이 없기에 앞으로 단방향 비밀번호는 Argon2 알고리즘을 사용하여 안전성을 더욱 강화하며 메모리 등등 여러가지 커스텀을 하며 관리하는데 더 편할것으로 생각됨
    • 다만 Argon2 은 검증기간이 BCrypt 에 비해 오래된것이 아니기에 치명적인 결함이 나올 가능성이 존재함

    사용법

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new Argon2PasswordEncoder(DEFAULT_SALT_LENGTH, DEFAULT_HASH_LENGTH, DEFAULT_PARALLELISM, DEFAULT_MEMORY, DEFAULT_ITERATIONS);
    }
    • DEFAULT_SALT_LENGTH : 솔트(salt)의 기본 길이 (솔트의 길이는 바이트 단위 기준)
    • DEFAULT_HASH_LENGTH : 해싱된 비밀번호의 기본 길이 (해시의 길이는 바이트 단위 기준)
    • DEFAULT_PARALLELISM : Argon2 알고리즘에서 병렬 연산의 수준을 설정하는 값(CPU 코어를 사용하는 데 있어 얼마나 많은 병렬성을 허용할 것인지를 설정)
    • DEFAULT_MEMORY : Argon2 알고리즘에서 사용되는 메모리의 양을 설정하는 값(메모리의 양을 킬로바이트 단위로 설정)
    • DEFAULT_ITERATIONS : Argon2 알고리즘에서 암호화를 반복하는 횟수를 설정 (해당값이 클수록 암호화하는데 오래걸리지만, 무차별 공격을 방어하는데 효과가 더 커짐)

    번외

    • 페퍼
      • 페퍼(pepper)는 솔트(salt)와 비슷한 개념으로, 원본 데이터에 추가하는 또 다른 랜덤 문자열
      • 솔트는 데이터베이스에 함께 저장되므로, 만약 데이터베이스가 해킹당하면 공격자는 솔트 값을 알 수 있다.
      • DB를 해킹당했을때 위 예시를 보자면 CrT7s5mqbxHPYCX2DwMTse 이부분이 솔트인지 바로 알 수 있다.
      • 그러나 페퍼는 데이터베이스에 보관하지 않고 따로 어플리케이션같은곳에 보관하여 사용하기에 데이터베이스가 해킹당해도 페퍼값은 알 수 없기에 복호화하기 더욱 어렵다.
    • 레인보우 테이블
      • 미리 만들어진 테이블에 일반적인 비밀번호 패턴이나 사용 가능한 모든 문자열에 대한 해시를 저장한 후 가져온 해시값이 테이블에 있는지 확인하고, 있으면 원래 비밀번호를 알아낼 수 있는 행위

    • 위 테이블은 가장 많이 사용하는 암호 순위
      • 위 레인보우 테이블을 통한 예시 : 사용자의 비밀번호를 해킹해서 "e10adc3949ba59abbe56e057f20f883e" 값을 알고있을때 사용자의 해쉬값(e10adc3949ba59abbe56e057f20f883e)이 위 테이블 중 동일한 값이 있는지 체크하는 방법
    • 블록 암호화
    • 예시)0x01020304050607080910111213141516 가. 이를 4byte를 한 블록으로 하는 블록으로 나누는경우블록 암호화는 대칭키 암호화의 한 형태로, 데이터를 고정된 크기의 '블록'으로 분할하고 각 블록을 암호화하는 방법입니다. 가장 일반적으로 사용되는 블록 크기는 64비트, 128비트, 256비트 등입니다.

     

    • 패딩
      • 암호화에서 패딩(padding)은 주어진 데이터 블록이 암호화 알고리즘이 요구하는 정해진 크기에 맞지 않을 때 그 차이를 메우는 것을 의미합니다. 이 과정은 일반적으로 데이터를 암호화하거나 디지털 서명을 생성할 때 필요합니다.
      • 예를들어 오라클패딩에서는 PKCS7이라는 패딩방식을 사용하는데 PKCS7 패딩방식은 필요한 패딩의 값을 필요한 패딩의 바이트수로 정의합니다.

    블록의 값이 0x01020304050607080910111213 일때
    해당 블록을 패딩한다면
    블록이 정확하게 딱 들어맞을 경우 다음 블록을 생성

    '기타' 카테고리의 다른 글

    개발자 단어 정리  (0) 2020.10.29
    web push 전에 쓰던 알람을 받아오는 통신방법  (0) 2020.09.12

    댓글

Designed by Tistory.