Javascript

[Javascript] 정규표현식(Regular Expression)으로 패스워드 유효성을 검증하는 방법 - 최소길이, 최대길이, 숫자, 알파벳 대/소문자, 특수기호 사용 체크

apost 2023. 1. 19. 16:39

로그인 화면, 또는 사용자 인증 화면에서 자바스크립트로 패스워드, 또는 입력한 인증키를 체크하는 방법을 알아보겠습니다.

 

패스워드를 체크한 결과를 사용자에게 어떻게 표현하느냐에 따라 친절하게 메시지를 표시해 줄 수도 있고, 보안상 결과만 뭉뚱그려서 패스워드를 유추할 수 있는 힌트?를 주지 않는 방법도 있습니다.

 

그리고 이런 여러 가지 순차적인 체크를 할 필요 없이, 원하는 패스워드 입력 조건을 만족하는지 여부만을 체크할 수 있는 짧은 정규표현식 처리 방법도 있습니다.

 

하나씩 확인해봅니다.

 

 

 

1. 친절한 메시지 버전

함수를 실행한 결과 값으로 패스워드의 어떤 부분이 패스워드 조건을 충족하지 않는지 친절한 문자열 에러를 반환해 주는 방식입니다.

 

/*
-- 파라메터 - 순서대로 패스워드, 최소길이, 최대길이, 체크 레벨
-- 체크레벨(조합형)
1:숫자or소문자or대문자or특수문자 중 1가지 조건 충족
2:숫자or소문자or대문자or특수문자 중 2가지 조건 충족
3:숫자or소문자or대문자or특수문자 중 3가지 조건 충족
4:숫자+소문자+대문자+특수문자 모두 충족
*/
function checkPassword(pwd,min,max,checkLevel){
    let errorMsg = '';
    //빈 패스워드류 체크
    if(!pwd){//"", null, undefined, 0, NaN 체크
        errorMsg = "패스워드를 입력해 주십시오.";  
        return errorMsg;  
    }

    //파라메터 체크, 기본값 설정
    min = min ?? 8;
    max = max ?? 16;
    checkLevel = checkLevel ?? 3;
    const lenRegExp = new RegExp('^.{'+min+','+max+'}$');

    //길이 체크
    if(pwd.match(lenRegExp)==null){
        errorMsg = min==max ? `패스워드 길이가 ${min}이 아닙니다.`:`패스워드 길이가 최소:${min}, 최대:${max}가 아닙니다.`;
        return errorMsg;
    }

    //조합 체크
    let checked = 0;
    checked+=(pwd.match(/[0-9]/)!=null?1:0); // 숫자
    checked+=(pwd.match(/[a-z]/)!=null?1:0); // 알파벳 소문자
    checked+=(pwd.match(/[A-Z]/)!=null?1:0); // 알파벳 대문자
    checked+=(pwd.match(/[^0-9a-zA-Z]/)!=null?1:0); //특수문자
    if(checked<checkLevel){
      errorMsg = `숫자, 알파벳 소문자, 알파벳 대문자, 특수문자 중 ${checkLevel}가지 이상 조합되야 합니다.`;
      return errorMsg;
    }
    return "";
}

let checkMsg = checkPassword("testtest1A",8,16,3);
console.log(checkMsg);

 

패스워드 조건을 충족하지 못했을 때 함수에서 반환되는 결과 메시지 표시

 

문자열 형태로 에러 메시지를 반환받는 경우 가장 많이 하는 실수가 맨 끝의 성공했을 때(에러 없이 함수 끝까지 도달했을 때) 빈 문자열을 반환하는 것입니다.

마지막에 빈 문자열 반환이 없으면 패스워드 체크 결과가 성공 시 undefined 가 반환됩니다. 주의해야 합니다.

함수 맨 끝의 빈 문자열 반환이 없을 경우, 다음처럼 결괏값에 대한 문자열 체크를 하면 항상 조건 미충족이 되게 됩니다.

 

if(checkPassword2("testtest1A#",8,16,3)!=""){
    console.log('패스워드 조건 미충족!');
}else{
    console.log('정상 패스워드');
}

 

문자열 결과 반환을 받을 때는 다음처럼 널리시(Nullish) 조건 체크를 해서 undefined 결괏값에 대한 처리를 확실하게 해 주던가, 아니면 함수 맨 끝에 반드시 맨 끝에 성공 시 빈 문자열을 반환하도록 해야 합니다.

 

if(!checkPassword2("testtest1A#",8,16,3)){
    console.log('정상 패스워드');
}else{
    console.log('패스워드 조건 미충족!');
}

 

 

 

2. 결과만 불리언으로 확인하기

앞서 만든 함수에서 에러 메시지에 대한 친절한 안내가 필요 없을 경우 불리언 값만 반환하도록 해서 조금 더 간결하게 처리를 할 수 있습니다.

 

function checkPassword(pwd,min,max,checkLevel){
    //빈 패스워드류 체크
    if(!pwd){//"", null, undefined, 0, NaN 체크
        return false;  
    }

    //파라메터 체크, 기본값 설정
    min = min ?? 8;
    max = max ?? 16;
    checkLevel = checkLevel ?? 3;
    const lenRegExp = new RegExp('^.{'+min+','+max+'}$');

    //길이 체크
    if(pwd.match(lenRegExp)==null){
        return false;
    }

    //조합 체크
    let checked = 0;
    checked+=(pwd.match(/[0-9]/)!=null?1:0); // 숫자
    checked+=(pwd.match(/[a-z]/)!=null?1:0); // 알파벳 소문자
    checked+=(pwd.match(/[A-Z]/)!=null?1:0); // 알파벳 대문자
    checked+=(pwd.match(/[^0-9a-zA-Z]/)!=null?1:0); //특수문자
    if(checked<checkLevel){
      return false;
    }
    return true;
}

 

 

 

3. 선택적 조합이 아닌 기본 제한 조건을 충족하는 패스워드 체크

앞서의 패스워드 패턴 체크는 조합 체크 방식입니다.

4가지 체크 조건 중에서 함수 파라미터로 넘겨받은 누적 조건 개수를 만족하면 성공한 것으로 처리합니다.

checkLevel 조건 체크가 2라면 알파벳 대문자+특수문자 조합이어도 패스워드 조건을 만족한 것이 됩니다.

만약 checkLevel 조건 체크가 3이고 숫자+알파벳 소문자는 무조건 만족해야 하고, 알파벳 대문자 or 특수문자 중 한 가지를 만족해야 한다면 어떻게 해야 할까?

 

먼저 checkLevel은 조합 체크 누적 카운터이므로 기존 충족 조건이 무조건 2개가 필요한 경우에는 필요가 없습니다. 카운터 값은 무시해도 됩니다.

정규표현식으로 체크한 결과는 "숫자 and 소문자 and (대문자 or 특수문자)" 조건을 충족해야 합니다.

앞서의 예와는 반대로 checked 결과 값이 0이면 패스워드 체크를 성공한 것으로 하고 조건에 부합하지 않으면 checked+1을 하는 방식으로 변경합니다.

그리고 둘 중 하나만 만족하면 되는 조건인 "대문자 or 특수문자" 체크는 +10을 해서 "숫자 and 소문자" 조건과 구분을 합니다.

 

최종적으로는 다음 두 조건 중 한 조건이라도 해당되면 패스워드 조건에 부합하지 않게 됩니다.

 

- 결괏값이 20보다 크거나 같으면 "대문자 or 특수문자" 둘 다 충족하지 못합니다.

- 결괏값(checked)을 /10을 해서 나머지 값을 구해 나머지가 0보다 크면 "숫자 and 소문자" 조건을 충족하지 못합니다.

 

따라서 조건문을 "checked>=20||checked%10!=0" 작성하면 "숫자 and 소문자 and (대문자 or 특수문자)" 조건을 충족하는 패스워드를 판단할 수 있습니다.

 

    //숫자 and 소문자 and (대문자 or 특수문자)
    let checked = 0;
    checked+=(pwd.match(/[0-9]/)!=null?0:1); // 숫자
    checked+=(pwd.match(/[a-z]/)!=null?0:1); // 알파벳 소문자
    checked+=(pwd.match(/[A-Z]/)!=null?0:10); // 알파벳 대문자
    checked+=(pwd.match(/[^0-9a-zA-Z]/)!=null?0:10); //특수문자
    if(checked>=20||checked%10!=0){
      return false;
    }

 

 

4. 친절하지 않은 더 빠른 정규표현식 패스워드 체크

친절하게 패스워드를 체크한 결과를 표시해 주는 긴 코드로 작성한 함수로 패스워드를 체크하는 대신, 간결하고 빠르게 원하는 조건에 맞는지만을 확인할 수 있습니다.

마찬가지로 정규표현식을 사용하지만, 1개의 정규표현식으로 패스워드가 원하는 조건에 부합하는지를 체크해서 결과를 불리언(true/false)으로 반환합니다.

 

 

- 8~16자리 패스워드면서 영문자 대/소문자, 그리고 숫자로만 이루어진 패스워드 체크. 영문 대소문자, 숫자만 허용하고 특수문자를 사용할 수 없도록 할 때 사용할 수 있습니다.

 

function checkPassword(pwd){
    const regExp = new RegExp(/^[0-9A-Za-z]\w{8,16}$/);
    return pwd.match(regExp);
}

 

 

- 6~12 자리 패스워드면서 영문 소문자, 영문 대문자, 숫자를 각각 최소한 1개 이상 가지고 있는 패스워드 체크. 숫자, 영문 대/소문자를 섞어서 패스워드를 생성하도록 해서 최소한의 패스워드 복잡성을 만족하도록 강제할 때 사용할 수 있습니다.

 

 

function checkPassword(pwd){
    const regExp = new RegExp(/^(?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{6,12}$/);
    return pwd.match(regExp)==null?false:true;
}

 

한 개 이상의 특수문자까지 더 해서 사용하도록 강제하려면 다음과 같이 조건을 추가합니다.

 

function checkPassword5(pwd){
    const regExp = new RegExp(/^(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[^0-9a-zA-Z]).{6,12}$/);
    return pwd.match(regExp)==null?false:true;
}

 

다만, 이 방법은 영문자, 숫자가 아닌 모든 문자를 특수문자로 인식하기 때문에 한글이나 기타 다른 언어 문자도 모두 특수문자로 인식합니다.

2바이트 문자는 특수문자로 인식하지 않고 ASCII 특수문자만 특수문자 입력으로 처리되도록 하려면 다음과 같이 변경해야 합니다.

 

function checkPassword(pwd){
    const regExp = new RegExp(/^(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[!@#$%^&*]).{6,12}$/);
    return pwd.match(regExp)==null?false:true;
}