feat(security): 비밀번호 정책 강화 — 8자+영문·숫자·특수 3종 + 안내문구

- 회원가입·비밀번호 변경·관리자 회원 등록/수정에 정책 적용
- 정규식 규칙을 배열 형식으로 적용(파이프 파싱 깨짐 → is_unique 등 검증 무력화 버그 수정)
- 비밀번호 입력란에 형식 안내문구 추가

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
taekyoungc
2026-06-24 18:01:18 +09:00
parent 8ba9c650e8
commit 3b3f83461a
7 changed files with 16 additions and 10 deletions

View File

@@ -19,14 +19,15 @@ class PasswordChange extends BaseController
helper('admin');
$rules = [
'current_password' => 'required',
'new_password' => 'required|min_length[4]|max_length[255]',
'new_password' => ['required', 'min_length[8]', 'max_length[255]', 'regex_match[/^(?=.*[A-Za-z])(?=.*\d)(?=.*[\W_]).+$/]'],
'new_password_confirm' => 'required|matches[new_password]',
];
$messages = [
'current_password' => ['required' => '현재 비밀번호를 입력해 주세요.'],
'new_password' => [
'required' => '새 비밀번호를 입력해 주세요.',
'min_length' => '비밀번호는 4자 이상이어야 합니다.',
'min_length' => '비밀번호는 8자 이상이어야 합니다.',
'regex_match' => '비밀번호는 영문·숫자·특수문자를 모두 포함해야 합니다.',
],
'new_password_confirm' => [
'required' => '비밀번호 확인을 입력해 주세요.',

View File

@@ -83,13 +83,13 @@ class User extends BaseController
{
$rules = [
'mb_id' => 'required|min_length[2]|max_length[50]|is_unique[member.mb_id]',
'mb_passwd' => 'required|min_length[4]|max_length[255]',
'mb_passwd' => ['required', 'min_length[8]', 'max_length[255]', 'regex_match[/^(?=.*[A-Za-z])(?=.*\d)(?=.*[\W_]).+$/]'],
'mb_name' => 'required|max_length[50]',
'mb_email' => 'permit_empty|valid_email|max_length[100]',
'mb_phone' => 'permit_empty|max_length[20]',
'mb_level' => 'required',
];
if (! $this->validate($rules)) {
if (! $this->validate($rules, ['mb_passwd' => ['min_length' => '비밀번호는 8자 이상이어야 합니다.', 'regex_match' => '비밀번호는 영문·숫자·특수문자를 모두 포함해야 합니다.']])) {
return redirect()->back()->withInput()->with('errors', $this->validator->getErrors());
}
$allowedLevels = array_keys($this->getAssignableLevels());
@@ -154,9 +154,9 @@ class User extends BaseController
];
$passwd = $this->request->getPost('mb_passwd');
if ($passwd !== '') {
$rules['mb_passwd'] = 'min_length[4]|max_length[255]';
$rules['mb_passwd'] = ['min_length[8]', 'max_length[255]', 'regex_match[/^(?=.*[A-Za-z])(?=.*\d)(?=.*[\W_]).+$/]'];
}
if (! $this->validate($rules)) {
if (! $this->validate($rules, ['mb_passwd' => ['min_length' => '비밀번호는 8자 이상이어야 합니다.', 'regex_match' => '비밀번호는 영문·숫자·특수문자를 모두 포함해야 합니다.']])) {
return redirect()->back()->withInput()->with('errors', $this->validator->getErrors());
}
$allowedLevels = array_keys($this->getAssignableLevels());

View File

@@ -357,7 +357,7 @@ class Auth extends BaseController
{
$rules = [
'mb_id' => 'required|min_length[4]|max_length[50]|is_unique[member.mb_id]',
'mb_passwd' => 'required|min_length[4]|max_length[255]',
'mb_passwd' => ['required', 'min_length[8]', 'max_length[255]', 'regex_match[/^(?=.*[A-Za-z])(?=.*\d)(?=.*[\W_]).+$/]'],
'mb_passwd_confirm' => 'required|matches[mb_passwd]',
'mb_name' => 'required|max_length[100]',
'mb_email' => 'permit_empty|valid_email|max_length[100]',
@@ -375,8 +375,9 @@ class Auth extends BaseController
],
'mb_passwd' => [
'required' => '비밀번호를 입력해 주세요.',
'min_length' => '비밀번호는 4자 이상이어야 합니다.',
'min_length' => '비밀번호는 8자 이상이어야 합니다.',
'max_length' => '비밀번호는 255자 이하여야 합니다.',
'regex_match' => '비밀번호는 영문·숫자·특수문자를 모두 포함해야 합니다.',
],
'mb_passwd_confirm' => [
'required' => '비밀번호 확인을 입력해 주세요.',

View File

@@ -13,6 +13,7 @@
<div class="flex flex-wrap items-center gap-2">
<label class="block text-sm font-bold text-gray-700 w-32">새 비밀번호 <span class="text-red-500">*</span></label>
<input class="border border-gray-300 rounded px-3 py-1.5 text-sm w-60" name="new_password" type="password" required autocomplete="new-password"/>
<p class="w-full text-xs text-gray-500 pl-32">8자 이상, 영문·숫자·특수문자를 모두 포함해야 합니다.</p>
</div>
<div class="flex flex-wrap items-center gap-2">

View File

@@ -11,6 +11,7 @@
<div class="flex flex-wrap items-center gap-2">
<label class="block text-sm font-bold text-gray-700 w-20">비밀번호 <span class="text-red-500">*</span></label>
<input class="border border-gray-300 rounded px-3 py-1.5 text-sm w-48" id="mb_passwd" name="mb_passwd" type="password" required/>
<p class="w-full text-xs text-gray-500 pl-20">8자 이상, 영문·숫자·특수문자를 모두 포함해야 합니다.</p>
</div>
<div class="flex flex-wrap items-center gap-2">
<label class="block text-sm font-bold text-gray-700 w-20">이름 <span class="text-red-500">*</span></label>

View File

@@ -24,6 +24,7 @@ $editLoginLocked = $editLockUntil !== null && $editLockUntil !== '' && strtotime
<div class="flex flex-wrap items-center gap-2">
<label class="block text-sm font-bold text-gray-700 w-20">비밀번호</label>
<input class="border border-gray-300 rounded px-3 py-1.5 text-sm w-48" id="mb_passwd" name="mb_passwd" type="password" placeholder="변경 시에만 입력"/>
<p class="w-full text-xs text-gray-500 pl-20">변경 시 8자 이상, 영문·숫자·특수문자를 모두 포함해야 합니다.</p>
</div>
<div class="flex flex-wrap items-center gap-2">
<label class="block text-sm font-bold text-gray-700 w-20">이름 <span class="text-red-500">*</span></label>

View File

@@ -13,6 +13,7 @@
<div>
<label class="block text-sm font-bold text-gray-700 mb-1" for="mb_passwd">비밀번호 <span class="text-red-500">*</span></label>
<input class="<?= $inputCls ?>" id="mb_passwd" name="mb_passwd" type="password" autocomplete="new-password"/>
<p class="mt-1 text-xs text-gray-500">8자 이상, 영문·숫자·특수문자를 모두 포함해야 합니다.</p>
</div>
<div>
<label class="block text-sm font-bold text-gray-700 mb-1" for="mb_passwd_confirm">비밀번호 확인 <span class="text-red-500">*</span></label>