[시큐어코딩 가이드] 2-2-9. 취약한 패스워드 허용

취약한 패스워드 허용

정의

사용자에게 강한 패스워드 조합규칙을 요구하지 않으면, 사용자 계정이 취약하게 된다. 안전한 패스워드를 생성하기 위해서는 '패스워드 선택 및 이용 안내서'에서 제시하는 패스워드 설정 규칙을 적용해야 한다.

 

KISA 한국인터넷진흥원

1. 안전한 패스워드 2. 이러한 패스워드 사용하지 마세요 3. 안전한 패스워드 생성 Tip 4. 패스워드 보안 지침(이용자 측면) 5. 패스워드 보안 지침(관리자 측면)

www.kisa.or.kr

 

안전한 코딩기법

패스워드 생성 시 강한 조건 검증을 수행한다. 패스워드(패스워드)는 숫자와 영문자, 특수문자 등을 혼합하여 사용하고, 주기적으로 변경하여 사용하도록 해야 한다.

 

코드예제

다음은 안전하지 않은 코드예제로, 사용자가 입력한 패스워드를 복잡도 검증 없이 가입 승인처리를 수행하고 있는 코드입니다.

<!--form.html-->
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <form method="post">
        {% csrf_token %}
        <label>
            userid: <input type="text" name="userid"/><br>
            pwd: <input type="text" name="pwd"/><br>
            check_pwd: <input type="text" name="check_pwd"/><br>
        </label>
        <input type="submit" value="패스워드변경"/>
    </form>
</body>
</html>
#취약한 패스워드 허용(안전X)
def register_bad(request):
    if request.method == 'GET':
        return render(request, 'form.html')

    userid = request.POST.get('userid')
    pwd = request.POST.get('pwd')
    check_pwd = request.POST.get('check_pwd')

    if pwd != check_pwd:
        return render(request, 'error.html', {'msg': '패스워드가 일치하지 않습니다.'})
    else:
        # 지금 테이블 없음 ㅎㅎ
        # usertable = User()
        # usertable.userid = userid
        # usertable.pwd = pwd
        # db.session.add(usertable)
        # db.session.commit()
        return render(request, 'success.html', {'msg': '회원가입 성공'})

 

pwd: 123 / check_pwd: 123aaa

 

pwd: abc123 / check_pwd: abc123

 

다음은 안전한 코드예제로, 패스워드 복잡도와 길이를 검증 후 가입 승인처리를 수행하고 있는 코드입니다. 

#취약한 패스워드 허용(안전O)
def register_good(request):
    if request.method == 'GET':
        return render(request, 'form.html')

    userid = request.POST.get('userid')
    pwd = request.POST.get('pwd')
    check_pwd = request.POST.get('check_pwd')

    if pwd != check_pwd:
        return render(request, 'error.html', {'msg': '패스워드가 일치하지 않습니다.'})

    if not check_password(pwd):
        return render(request, 'error.html', {'msg': '패스워드 규칙에 맞지 않습니다.'})
    else:
        # 지금 테이블 없음 ㅎㅎ
        # usertable = User()
        # usertable.userid = userid
        # usertable.pwd = pwd
        # db.session.add(usertable)
        # db.session.commit()
        return render(request, 'success.html', {'msg': '회원가입 성공'})

def check_password(password):
    #3종이상 문자로 구성된 8자리 이상 패스워드 검사 정규식
    PT1 = re.compile('^(?=.*[A-Z])(?=.*[a-z])[A-Za-z\d!@#$%^&*]{8,}$')
    PT2 = re.compile('^(?=.*[A-Z])(?=.*\d)[A-Za-z\d!@#$%^&*]{8,}$')
    PT3 = re.compile('^(?=.*[A-Z])(?=.*[!@#$%^&*])[A-Za-z\d!@#$%^&*]{8,}$')
    PT4 = re.compile('^(?=.*[a-z])(?=.*\d)[A-Za-z\d!@#$%^&*]{8,}$')
    PT5 = re.compile('^(?=.*[a-z])(?=.*[!@#$%^&*])[A-Za-z\d!@#$%^&*]{8,}$')
    PT6 = re.compile('^(?=.*\d)(?=.*[!@#$%^&*])[A-Za-z\d!@#$%^&*]{8,}$')

    # 문자 구성 상관없이 10자리 이상 패스워드 검사 정규식
    PT7 = re.compile('^[A-Za-z\d!@#$%^&*]{10,}$')

    for i in [PT1, PT2, PT3, PT4, PT5, PT6, PT7]:
        if i.match(password):
            return True
    return False

 

pwd: 123abc / check_pwd: 123abc

 

pwd: 33cheese!! / check_pwd: 33cheese!!

 

또 다른 안전한 코드예제로, Django 프레임워크의 VALIDATORS를 사용할 수 있습니다.

#Django 프레임워크 사용
#취약한 패스워드 허용(안전O)
import re
from django.core.exceptions import ValidationError
from django.utils.translation import ugettext as _

class CustomValidator(object):
    def Validate(self, password, user=None):
        PT1 = re.compile('^(?=.*[A-Z])(?=.*[a-z])[A-Za-z\d!@#$%^&*]{8,}$')
        PT2 = re.compile('^(?=.*[A-Z])(?=.*\d)[A-Za-z\d!@#$%^&*]{8,}$')
        PT3 = re.compile('^(?=.*[A-Z])(?=.*[!@#$%^&*])[A-Za-z\d!@#$%^&*]{8,}$')
        PT4 = re.compile('^(?=.*[a-z])(?=.*\d)[A-Za-z\d!@#$%^&*]{8,}$')
        PT5 = re.compile('^(?=.*[a-z])(?=.*[!@#$%^&*])[A-Za-z\d!@#$%^&*]{8,}$')
        PT6 = re.compile('^(?=.*\d)(?=.*[!@#$%^&*])[A-Za-z\d!@#$%^&*]{8,}$')
        PT7 = re.compile('^[A-Za-z\d!@#$%^&*]{10,}$')

        for i in [PT1, PT2, PT3, PT4, PT5, PT6, PT7]:
            if i.match(password):
                return None
            raise ValidationError(_("패스워드 조합 규칙에 적합하지 않습니다."),
                                code='improper_password',)

    def get_help_text(self):
        return _("패스워드는 영문 대소문자, 숫자, 특수문자 조합중 2가지 이상 8자리이거나 문자 상관없이 10자리 이상이어야 합니다.")

 

 

 

보안기능/취약한 패스워드 허용

[참고문헌] Python 시큐어코딩 가이드(2022) / KISA(한국인터넷진흥원)