메뉴 건너뛰기

app

멈추지 않는 공격자: CWE-307 반복된 인증 시도 제한 기능 부재

suritam92026.01.17 23:26조회 수 0댓글 0

    • 글자 크기

1. 반복된 인증 시도 제한 기능 부재의 위험성

CWE-307은 사용자가 로그인을 시도할 때, 실패 횟수에 상관없이 무한정 재시도를 허용할 때 발생합니다. 공격자는 이를 악용하여 **무차별 대입 공격(Brute-force Attack)**이나 **사전 공격(Dictionary Attack)**을 수행합니다. 자동화된 도구를 사용하여 초당 수백 번씩 비밀번호를 대입해 보다 보면, 결국 단순하거나 흔한 비밀번호를 사용하는 계정은 뚫릴 수밖에 없습니다. 이는 계정 탈취는 물론, 서버 리소스를 과도하게 점유하여 발생하는 서비스 거부(DoS)의 원인이 되기도 합니다.

2. 흔히 발생하는 취약한 패턴

주로 "편의성"과 "구현의 단순함" 때문에 보안 설정을 간과할 때 나타납니다.

무제한 로그인 시도: 아이디와 비밀번호가 틀려도 아무런 제약 없이 다시 입력창을 보여주는 경우.

임계치 설정 미비: 5회 또는 10회 이상 실패하더라도 계정을 잠그거나 지연 시간(Delay)을 주지 않는 경우.

캡차(CAPTCHA) 미도입: 사람이 아닌 봇(Bot)의 자동화된 요청을 구분하지 못하는 경우.

로그인 API 노출: 웹 페이지에는 제한이 있어도, 모바일용 API나 백엔드 엔드포인트에는 시도 제한 로직이 누락된 경우.

3. 실무적 대응: 계정 잠금 정책과 점진적 지연

공격자의 시도 횟수를 물리적으로 제한하고 경제성을 떨어뜨려야 합니다.

계정 잠금(Account Lockout): 일정 횟수(예: 5회) 이상 인증 실패 시 해당 계정을 일정 시간 동안 잠금 처리합니다.

계단식 시간 지연(Exponential Backoff): 실패 횟수가 늘어날수록 다음 시도까지 대기해야 하는 시간을 1초, 2초, 4초... 와 같이 기하급수적으로 늘립니다.

자동화 방지(CAPTCHA): 반복적인 실패가 감지되면 캡차를 노출하여 봇의 접근을 차단합니다.

알림 발송: 평소와 다른 환경에서 반복된 실패가 발생할 경우 사용자에게 이메일이나 SMS로 경고 알림을 보냅니다.

4. CWE-307 대응 및 안전한 인증 시도 관리 자바 코드 예시

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
 
public class LoginAttemptService {
    private static final Logger logger = LoggerFactory.getLogger(LoginAttemptService.class);
    
    // 실제 운영 환경에서는 Redis나 DB를 사용하여 관리하는 것이 좋습니다.
    private static final int MAX_ATTEMPT = 5;
    private static final Map<String, Integer> attemptsCache = new ConcurrentHashMap<>();
 
    // [CWE-307 조치] 인증 시도 전 제한 여부 확인
    public boolean isBlocked(String userId) {
        Integer attempts = attemptsCache.get(userId);
        if (attempts != null && attempts >= MAX_ATTEMPT) {
            logger.debug("Security Violation: User {} is blocked due to excessive attempts.", userId);
            return true;
        }
        return false;
    }
 
    // [CWE-307 조치] 인증 실패 시 횟수 증가
    public void loginFailed(String userId) {
        int attempts = attemptsCache.getOrDefault(userId, 0);
        attempts++;
        attemptsCache.put(userId, attempts);
        
        logger.debug("Login failed for user: {}. Current attempt: {}/{}", userId, attempts, MAX_ATTEMPT);
    }
 
    // 인증 성공 시 횟수 초기화
    public void loginSucceeded(String userId) {
        attemptsCache.remove(userId);
        logger.debug("Login successful for user: {}. Attempts reset.", userId);
    }
}

코멘트: 보안은 '문'을 튼튼하게 만드는 것만큼이나, '문고리'를 수만 번 돌려보는 행위를 제지하는 것이 중요합니다. 단순히 비밀번호 일치 여부만 체크할 것이 아니라, 시스템 전체를 보호하기 위한 속도 제한(Rate Limiting) 관점에서 접근하십시오. logger.debug()를 통해 비정상적인 접근 패턴을 모니터링하되, 공격자가 우리 시스템을 비밀번호 연습장으로 쓰지 못하도록 단호한 정책을 세우는 것이 시큐어 코딩의 핵심입니다.

 
    • 글자 크기
찰나의 순간을 노리는 공격: CWE-367 TOCTOU 경쟁 조건 (by suritam9) 외부 코드의 위험한 초대: CWE-494 무결성 검사 없는 코드 다운로드 (by suritam9)

댓글 달기

첨부 (0)
위로