BACK/Spring

Hmac를 사용하여 url 암호화 구현해 보기

dazz6 2024. 11. 25. 18:00

링크를 공유할 때 단순히 링크를 공유하게 되면 보안에 문제*가 있다고 하여, Hmac을 사용해 url 암호화를 구현해 보았다. 

 

* 링크를 단순히 공유할 경우의 보안 문제

1. 링크 탈취 가능성

-> 예를 들어, 사용자 아이디나 세션 정보가 포함된 url이 노출될 경우 악용하여 다른 사용자의 데이터를 조작할 수 있음

2. Replay 공격

-> 링크를 통해 인증 등의 작업이 수행될 경우, 동일한 링크를 재사용하여 원치않는 작업을 반복 실행할 수 있음

3. URL 변조

->  파라미터를 조작하여 허가되지 않은 데이터를 요청할 수 있음

 

HMAC은 key와 message를 조합해서 고정된 크기의 해시 값을 생성하는 방식으로, 데이터의 무결성을 확인하고 인증을 보장하기 위해 사용된다.

 

@Value("${komawatsir-pwd}")
private String secretKey;
// 보안을 위해 application.properties에 key를 입력해 두고 주입해 주는 방식을 사용

private static String SECRET_KEY = "";

@PostConstruct
public void init() {
	SECRET_KEY = this.secretKey;
}
// application.properties에 저장된 값을 불러옴과 동시에
// key를 static으로 지정하면서, @PostConstruct annotation을 사용하여 실행될 때 key 값을 주입

/* 암호화하기 */
public String getUrl(Integer userId) {
        try {
            SecretKeySpec secretKey = new SecretKeySpec(SECRET_KEY.getBytes(), "HmacSHA256");
            Mac mac = Mac.getInstance("HmacSHA256");
            mac.init(secretKey);

            byte[] hmacBytes = mac.doFinal(String.valueOf(userId).getBytes());

            return Base64.getUrlEncoder().withoutPadding().encodeToString(hmacBytes);
        } catch (Exception e) {
            System.out.println("getUrl ERROR : " + e.getMessage());
            return null;
        }
    }

/* 검증하기 */
public Boolean validateUrl(Integer userId, String url) {
        String userIdHmac = getUrl(userId);

        /*
        equals 메소드는 문자열 길이에 따라 수행 시간이 달라질 수 있으므로 타이밍 공격(실행 시간 차이로 비밀 키 추측)에 취약.
        타임 인디펜던트 비교(입력 데이터의 길이와 내용에 관계없이 일정한 실행 시간을 유지하여 비교)를 구현함 (아래 코드)
         */
        if (userIdHmac == null || url == null || userIdHmac.length() != url.length()) {
            return false;
        }
        int result = 0;
        for (int i = 0; i < userIdHmac.length(); i++) {
            result |= userIdHmac.charAt(i) ^ url.charAt(i);
        }

        return result == 0;
    }