본문 바로가기
소프트웨어(SW) 보안약점 진단원/구현단계 보안약점 제거 기준

구현단계 보안약점 기준 - 반복된 인증시도 제한 기능 부재

by 브루노W 2025. 6. 4.
유형 보안기능
보안약점 반복된 인증시도 제한 기능 부재
개요
일정 시간 내에 여러 번의 인증을 시도하여도 계정잠금 또는 추가 인증 방법 등의 충분한 조치가 수행되지 않는 경우, 공격자는 예상 ID와 비밀번호들을 사전(Dictionary)으로 만들고 무차별 대입(bruteforce)하여 로그인 성공 및 권한획득이 가능하다.
보안대책
인증시도 횟수를 적절한 횟수로 제한하고 설정된 인증실패 횟수를 초과했을 경우 계정을 잠금하거나 추가적인 인증과정을 거쳐서 시스템에 접근이 가능하도록 한다.
진단방법
인증을 위한 함수를 호출하는 경우, 이 함수의 호출 횟수를 확인하고 함수의 호출을 제한하는 코드가 존재하는지 확인한다.
그렇지 않은 경우는 취약하다고 판단한다.
연관된 설계단계 기준 인증 수행 제한

 

코드예제

 

● 안전하지 않은 코드 예 (Java)

private static final String SERVER_IP = "127.0.0.1";
private static final int SERVER_PORT = 8080;
private static final int FAIL = -1;

public void login() {
    String username = null;
    String password = null;
    Socket socket = null;
    in t result = FAIL;
    try {
        socket = new Socket(SERVER_IP, SERVER_PORT);
        // 인증 실패에 대해 제한을 두지 않아 안전하지 않다.
        while (result == FAIL) {
        	...
        result = verifyUser(username, password);
        }
    }

 

● 안전한 코드 예 (Java)

private static final String SERVER_IP = "127.0.0.1";
private static final int SERVER_PORT = 8080;
private static final int FAIL = -1;
private static final int MAX_ATTEMPTS = 5;

public void login() {
    String username = null;
    String password = null;
    Socket socket = null;
    int result = FAIL;
    int count = 0;
    try {
        socket = new Socket(SERVER_IP, SERVER_PORT);
        // 인증 실패 및 시도 횟수에 제한을 두어 안전하다.
        while (result == FAIL && count < MAX_ATTEMPTS) {
            ...
            result = verifyUser(username, password);
            count++;
        }
    }

 

 

● 안전하지 않은 코드 예 (C#)

// 로그인 실패 시 아무런 제약이 없음
override protected void OnLoginError(EventArgs e) {
	//do nothing
}

 

● 안전한 코드 예 (C#)

override protected void OnLoginError(EventArgs e) {
    // 연속적인 사용자 인증 시도에 대한 횟수를 제한
    if(ViewState["LoginErrors"] == null)
    	ViewState["LoginErrors"] = 0;
    int ErrorCount = (int)ViewState["LoginErrors"] + 1;
    ViewState["LoginErrors"] = ErrorCount;
    if((ErrorCount > 3) && Login1.PasswordRecoveryUrl != string.Empty)
    	Response.Redirect(Login1.PasswordRecoveryUrl);
}

 

 

● 안전하지 않은 코드 예 (C)

int validateUser(char *host, int port) {
    int socket = openSocketConnection(host, port);
    if (socket < 0) {
        printf("Unable to open socket connection");
        return(FAIL);
    }
    int isValidUser = 0;
    char nm[NAME_SIZE];
    char pw[PSWD_SIZE];
    // 인증시도 횟수를 제한하고 있지 않음
    while (isValidUser==0) {
        if (getNextMsg(socket, nm, NAME_SIZE) > 0) {
            if (getNextMsg(socket, pw, PSWD_SIZE) > 0) {
            	isValidUser = AuthenticateUser(nm, pw);
            }
        }
    }
    return(SUCCESS);
}

 

● 안전한 코드 예 (C)

#define MAX_ATTEMPTS 5
int validateUser(char *host, int port) {
    ......
    // 연속적인 사용자 인증 시도에 대한 횟수를 제한
    int count = 0;
    while ((isValidUser==0) && (count<MAX_ATTEMPTS)) {
        if (getNextMsg(socket, nm, NAME_SIZE) > 0) {
            if (getNextMsg(socket, pw, PSWD_SIZE) > 0) {
            	isValidUser = AuthenticateUser(nm, pw);
            }
        }
        count++;
    }
    if (isValidUser) {
    	return(SUCCESS);
    } else {
    	return(FAIL);
    }
}

 

 

진단방법

 
 
인증을 위한 함수를 호출하는 경우, 이 함수의 호출 횟수를 확인하고 함수의 호출을 제한하는 코드가 존재하는지 확인한다.
그렇지 않은 경우는 취약하다고 판단한다.

 

 

정탐코드

int validateUser(char *host, int port) {
    int socket = openSocketConnection(host, port);
    if (socket < 0) {
        printf(“Unable to open socket connection”);
        return(FAIL);
    }

    int isValidUser = 0;
    char username[USERNAME_SIZE];
    char password[PASSWORD_SIZE];
    while (isValidUser == 0) {
        if (getNextMessage(socket, username, USERNAME_SIZE) > 0) {
            if (getNextMessage(socket, password, PASSWORD_SIZE) > 0) {
            	isValidUser = AuthenticateUser(username, password);
            }
        }
    }
    return(SUCCESS);
}

인증을 시도를 할 때, 인증 시도에 대한 제한 없이 반복문 안에서 계속 인증 시도를 하는 코드이다. 인증 시도 회수에 대한 검사 루틴이 존재하지 않으므로 보안약점이 존재하는 코드라고 진단할 수 있다.