소프트웨어 개발보안 가이드 분석(2021) : 취약한 비밀번호 허용

사용자에게 강한 패스워드 조합규칙을 요구하지 않으면, 사용자 계정이 취약하게 되며, 사전 공격(Dictionary Attack), 무차별 대입공격(Brute-Force Attack) 등에 취약할 수 있다.

소프트웨어 개발보안가이드(2021), 한국인터넷진흥원

 

취약한 비밀번호로 인한 사고 사례

2012년에는 한 소셜 미디어 플랫폼에서 대규모 데이터 유출 사고가 발생했습니다. 이 사고의 주요 원인 중 하나는 사용자들이 약한 비밀번호(예: "123456", "password" 등)를 사용함으로써 해커들이 브루트포스(무차별 대입 공격) 방법으로 쉽게 접근할 수 있었기 때문입니다. 이러한 유출로 인해 수백만 사용자의 개인정보가 위험에 노출되었고, 이는 해당 기업의 명성과 신뢰도에 치명적인 타격을 주었습니다.

취약한 비밀번호 정책으로 LinkedIn이 2012년에 발생한 데이터 유출 사건이 발생한 바 있고, 이 사고로 인해 대략 1억 명의 사용자의 개인정보가 유출된 것으로 확인되었습니다(CBS NEWS,2016)

위 사례와 같은 데이터 유출을 방지하기 위해서는 사용자에게 강력한 비밀번호를 설정하도록 권장하는 것이 중요합니다. 강력한 비밀번호는 다음과 같은 조합을 포함하여 사용되어야 합니다.

패스워드 선택 및 이용 안내서(한국인터넷진흥원, 2019)

한국인터넷진흥원에서는 가이드를 통해 "안전한 비밀번호"와 "안전하지 않은 비밀번호"에 대한 예시들을 통해 패스워드 보안정책의 기준을 제시하고 있습니다.

* 안전한 비밀번호에 대한 기준

  • 다양한 유형의 문자를 조합한 비밀번호: 숫자, 대소문자, 특수문자 등을 조합
  • 8자 이상의 길이
  • 10자리 이상의 길이를 권장하며 최대 4자리까지 동일한 문자 반복 가능
  • 숫자나 특수문자를 전체 길이의 절반까지 사용 가능

* 안전하지 않은 비밀번호

  • 통상적인 패턴을 갖는 비밀번호: 예를 들어 연속된 숫자 ('123123')
  • 키보드 상의 연속된 위치에 있는 문자들의 조합 ('qwerty')
  • 사용자 개인 정보에 기반한 단어나 숫자: 예를 들어 사용자의 이름, 생년월일, 주소, 전화번호 등
  • 사용자 ID를 포함한 비밀번호
  • 사용자 ID가 'kisa'인 경우에 'kisa1' 등으로 쉽게 유추할 수 있는 비밀번호
  • 특정 문화나 사회에서 자주 사용되는 단어나 구절: 예를 들어 '안녕하세요', 'love12' 등
  • 웹사이트 이름이나 업체 이름을 포함하는 비밀번호
  • 사이트, 기업명, 마켓 이름 등 특정 회사와 연관이 있는 단어
  • 특정 영화나 캐릭터 이름을 포함한 비밀번호: 예를 들어 '스타워즈', 'ironman1' 등
  • 사전에 정의된 순서대로의 디폴트 비밀번호: 예를 들어 '0000', 'password'
  • 화살표 키 배열과 같은, 오른쪽이나 왼쪽으로 순차적으로 나열된 키보드 패턴

 

취약한 코드 및 안전한 코드 예제

* 취약한 코드 예제

가입자가 입력한 비밀번호에 대한 복잡도 검증 없이 가입 승인 처리를 수행하고 있습니다.

String id = request.getParameter("id");
String pass = request.getParameter("pass");
UserVo userVO = new UserVo(id, pass);
// 비밀번호의 자릿수, 특수문자 포함 여부 등 복잡도를 체크하지 않고 등록
String result = registerDAO.register(userVO);

 

* 안전한 코드 예제

사용자 계정을 보호하기 위해 가입 시, 비밀번호 복잡도 검증 후 가입 승인처리를 수행합니다.

String id = request.getParameter("id");
String pass = request.getParameter("pass");
// 비밀번호에 자릿수, 특수문자 포함 여부 등의 복잡도를 체크하고 등록하게 한다.
Pattern pattern = Pattern.compile("((?=.*[a-zA-Z])(?=.*[0-9@#$%]).{8,})");
Matcher matcher = pattern.matcher(pass);
if (!matcher.matches()) {
    return "비밀번호 조합규칙 오류";
}
UserVo userVO = new UserVo(id, pass);
String result = registerDAO.register(userVO);

 

SW개발보안가이드의 예제 코드를 응용하여, 회원 등록 과정에서 패스워드 정책을 검증하기 위한 Java 코드는 다음과 같습니다. 이 코드는 패스워드 정책을 준수하는지 확인하는 로직을 포함하고 있습니다.

import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class PasswordPolicyValidator {

    private static final Pattern PASSWORD_PATTERN =
            Pattern.compile("^(?=.*[0-9])(?=.*[a-z])(?=.*[A-Z])(?=.*[@#$%^&+=])(?=\\S+$).{8,}$");

    public static void main(String[] args) {
        String password = "UserPassword123!";

        if (validatePassword(password)) {
            System.out.println("Password is valid and meets the policy requirements.");
            // 회원 등록 로직 수행
        } else {
            System.out.println("Password does not meet the policy requirements.");
        }
    }

    public static boolean validatePassword(String password) {
        Matcher matcher = PASSWORD_PATTERN.matcher(password);
        return matcher.matches();
    }
}

이 패스워드 정책 검증 로직은 다음 규칙을 따릅니다:

최소 한 개의 숫자 포함 ((?=.*[0-9]))

최소 한 개의 소문자 포함 ((?=.*[a-z]))

최소 한 개의 대문자 포함 ((?=.*[A-Z]))

최소 한 개의 특수 문자 포함 ((?=.*[@#$%^&+=]))

공백이 없어야 함 ((?=\\S+$))

최소 8자 이상 (.{8,})

  • 이전 소프트웨어 개발보안 가이드 분석(2021) : 적절하지 않은 난수값 사용
  • 다음 소프트웨어 개발보안 가이드 분석(2021) : 부적절한 전자서명 확인