[시큐어코딩 가이드] 취약한 비밀번호 허용

 

 

 

■ 보안기능

 

보안기능(인증, 접근제어, 기밀성, 암호화, 권한관리 등)을 부적절하게 구현 시 발생할 수 있는 보안약점으로 적절한 인증 없는 중요기능 허용, 부적절한 인가 등이 포함된다.

 

 

취약한 비밀번호 허용

 

■ 개요

 

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

 

 

■ 안전한 코딩 기법

 

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

 

 

코드예제

 

사용자가 입력한 패스워드에 대한 복잡도 검증 없이 가입 승인 처리를 수행하고 있다.

 

안전하지 않은 코드의 예
from flask import request, redirect
from Models import User
from Models import db

@app.route('/register', methods=['POST'])
def register():
userid = request.form.get('userid')
password = request.form.get('password')
confirm_password = request.form.get('confirm_password')

if password != confirm_password:
return make_response("비밀번호가 일치하지 않습니다", 200)
else:
usertable=User()
usertable.userid = userid
usertable.password = password
# 패스워드 생성 규칙을 확인하지 않고 회원 가입
db.session.add(usertable)
db.session.commit()
return make_response("회원가입 성공", 200)

 

사용자 계정을 보호하기 위해 가입 시, 패스워드 복잡도와 길이를 검증 후 가입 승인처리를 수행한다.

 

안전한 코드의 예
from flask import request, redirect
from Models import User
from Models import db
import re

@app.route('/register', methods=['POST'])
def register():
userid = request.form.get('userid')
password = request.form.get('password')
confirm_password = request.form.get('confirm_password')

if password != confirm_password:
return make_response("비밀번호가 일치하지 않습니다.", 200)

if not check_password(password):
return make_response("비밀번호 조합규칙에 맞지 않습니다.", 200)
else:
usertable=User()
usertable.userid = userid
usertable.password = password

db.session.add(usertable)
db.session.commit()
return make_response("회원가입 성공", 200)
def check_password(password):
# 2종 이상 문자로 구성된 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 pattern in [PT1, PT2, PT3, PT4, PT5, PT6, PT7]:
if pattern.match(password):
return True
return False

 

참고로, 위 코드의 특수문자(‘!@#$%^&*’)는 기업 내부 정책에 따라 변경하여 사용하면 되며, 패스워드를 숫자로만 10자리 구성할 경우 취약할 수 있으니 사용자에게 주의할 것을 안내하는 것이 필요하다.

 

 

■ Django 프레임워크의 VALIDATORS 사용

 

Django에서는 미들웨어의 AUTH_PASSWORD_VALIDATORS 설정에서 비밀번호에 대한 검증을 하고 있다. 기본적으로 아래와 같은 검증을 해준다.


⦁UserAttributeSimilarityValidator : user의 attributes(username, firstname, lastname, email)등과 유사한지 체크
⦁MinimumLengthValidator : 비밀번호 길이의 최소 값 (default 8)
⦁CommonPasswordValidator : 사람들이 가장 많이 사용하는 패스워드 20,000개에 해당하는지 검증.
⦁NumericPasswordValidator : 비밀번호가 완전히 숫자인지 검증.

 

위의 Validator외에 필요한 검증 기준은 사용자 Validator를 생성해서 AUTH_PASSWORD_VALIDATORS에 등록하여 사용가능하다. 아래는 사용자 Validator 예제이다.
(검증 통과 시 None 반환, 실패 시 ValidationError 발생하도록 구현 필요)

 

안전한 코드의 예
import re
from django.core.exceptions import ValidationError
from django.utils.translation import ugettext as _

class CustomValidator(object):
def validate(self, password, user=None):
# 2종 이상 문자로 구성된 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 pattern in [PT1, PT2, PT3, PT4, PT5, PT6, PT7]:
if pattern.match(password):
return None
raise ValidationError(
_("비밀번호 조합규칙에 적합하지 않습니다.."),
code='improper_password',
)

def get_help_text(self):
return _(
"비밀번호는 영문 대문자, 소문자, 숫자, 특수문자 조합 중 2가지 이상 8자리이거나 문자 구성 상
관없이 10자리 이상이어야 합니다."
)

 

 

댓글

Designed by JB FACTORY