Skip to content

[feature] 관리자 계정의 비밀번호 변경 및 초기화한다#722

Merged
alsdddk merged 6 commits intodevelop/befrom
feature/#719-password-MOA-214
Sep 5, 2025
Merged

[feature] 관리자 계정의 비밀번호 변경 및 초기화한다#722
alsdddk merged 6 commits intodevelop/befrom
feature/#719-password-MOA-214

Conversation

@alsdddk
Copy link
Collaborator

@alsdddk alsdddk commented Sep 4, 2025

#️⃣연관된 이슈

#719

📝작업 내용

  • 관리자 계정에서 변경할 필요 없는 userId 필드 제거
  • 비밀번호 초기화 api
  • 랜덤 임시 비밀번호 발급 -> 응답으로 전달 -> 암호화해서 db 저장

중점적으로 리뷰받고 싶은 부분(선택)

임시 비밀번호 발급을 위해 SecurePasswordGenerator를 추가했는데, length는 현재 임의로 설정해둔 상태입니다

논의하고 싶은 부분(선택)

논의하고 싶은 부분이 있다면 작성해주세요.

🫡 참고사항

  • 현재는 응답으로 임시 비밀번호를 직접 전달하지만, 추후에는 메일 또는 문자로 전송 방식으로 리팩토링할 계획입니다

Summary by CodeRabbit

  • 신기능

    • 인증된 사용자가 비밀번호를 초기화하면 임시 비밀번호가 발급되고 응답으로 제공됩니다.
    • 초기화 시 자동로그인/세션용 쿠키가 삭제되어 보안이 강화됩니다.
  • 변경

    • 프로필 수정 요청에서 사용자 ID는 더 이상 전송/수정 대상이 아니며, 비밀번호만 업데이트됩니다.
    • 비밀번호 초기화 흐름에 임시 비밀번호 반환용 응답 형식이 추가되었습니다.

- 변경할 필요 없는 userId 필드 제거
- updateUserProfile 메서드에서 userId 제거
- /auth/user/reset
- SecurePasswordGenerator 로 랜덤 비밀번호 생성
- 비밀번호는 TempPasswordResponse를 통해 응답
- user.resetPassword로 암호화된 비밀번로 저장
- 임시 비밀번호에 무조건 영어, 숫자, 특수문자가 포함되도록 보장
- db에 저장된 refresh 토큰 제거
- 클라이언트의 쿠키 삭제
@alsdddk alsdddk self-assigned this Sep 4, 2025
@alsdddk alsdddk added 📬 API 서버 API 통신 작업 💾 BE Backend labels Sep 4, 2025
@vercel
Copy link

vercel bot commented Sep 4, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Preview Comments Updated (UTC)
moadong Ready Ready Preview Comment Sep 4, 2025 1:37pm

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Sep 4, 2025

Warning

.coderabbit.yaml has a parsing error

The CodeRabbit configuration file in this repository has a parsing error and default settings were used instead. Please fix the error(s) in the configuration file. You can initialize chat with CodeRabbit to get help with the configuration file.

💥 Parsing errors (1)
Validation error: Invalid regex pattern for base branch. Received: "**" at "reviews.auto_review.base_branches[0]"
⚙️ Configuration instructions
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Walkthrough

관리자 비밀번호 초기화를 위한 SecurePasswordGenerator 유틸, TempPasswordResponse DTO, UserCommandService.reset 서비스, POST /auth/user/reset 컨트롤러를 추가했고 User 엔터티와 UserUpdateRequest를 비밀번호 중심으로 수정하며 리프레시 토큰을 무효화(쿠키 삭제 및 사용자 필드 null)합니다.

Changes

Cohort / File(s) Summary
비밀번호 생성 유틸 추가
backend/src/main/java/moadong/global/util/SecurePasswordGenerator.java
보안 비밀번호 생성 컴포넌트 추가. 대소문자/숫자/특수문자 최소 1자 보장 후 셔플. generate(int length) 공개 메서드 도입.
비밀번호 초기화 API 추가
backend/src/main/java/moadong/user/controller/UserController.java
POST /auth/user/reset 엔드포인트 추가. 서비스 호출로 임시 비밀번호 발급, 응답 반환. refresh_token 쿠키 즉시 만료 처리(삭제).
유저 도메인 갱신
backend/src/main/java/moadong/user/entity/User.java
프로필 업데이트에서 userId 갱신 제거. resetPassword(String encodedPassword) 메서드 추가.
요청 DTO 정리
backend/src/main/java/moadong/user/payload/request/UserUpdateRequest.java
userId 필드 제거, password만 유지. encryptPassword는 인코딩된 비밀번호만 포함해 반환하도록 변경.
응답 DTO 추가
backend/src/main/java/moadong/user/payload/response/TempPasswordResponse.java
임시 비밀번호 반환용 record TempPasswordResponse(String tempPassword) 추가(유효성 애노테이션 포함).
비밀번호 초기화 서비스 로직
backend/src/main/java/moadong/user/service/UserCommandService.java
reset(String userId) 추가: 사용자 조회, 8자 임시 비밀번호 생성/인코딩/적용, 리프레시 토큰 null 처리, 사용자 저장, TempPasswordResponse 반환. SecurePasswordGenerator 의존성 주입.

Sequence Diagram(s)

sequenceDiagram
    autonumber
    participant C as Client
    participant UC as UserController
    participant S as UserCommandService
    participant UR as UserRepository
    participant G as SecurePasswordGenerator
    participant PE as PasswordEncoder

    C->>UC: POST /auth/user/reset (인증됨)
    UC->>S: reset(userId)
    S->>UR: findByUserId(userId)
    UR-->>S: User or not found
    alt 사용자 없음
        S-->>UC: throw USER_NOT_EXIST
        UC-->>C: 4xx 에러
    else 사용자 있음
        S->>G: generate(8)
        G-->>S: tempPassword
        S->>PE: encode(tempPassword)
        PE-->>S: encodedPassword
        S->>S: user.resetPassword(encodedPassword)\nuser.setRefreshToken(null)
        S->>UR: save(user)
        UR-->>S: saved
        S-->>UC: TempPasswordResponse(tempPassword)
        UC->>UC: refresh_token 쿠키 삭제 설정
        UC-->>C: 200 OK + TempPasswordResponse
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Assessment against linked issues

Objective Addressed Explanation
관리자 계정 비밀번호 초기화 구현 (MOA-214)
관리자 계정 비밀번호 변경 기능 구현 (MOA-214) 요청/서비스에서 명시적 "비밀번호 변경(사용자 주도)" 엔드포인트나 흐름 추가는 보이지 않음; 변경은 초기화(임시비밀번호) 중심임.

Possibly related issues

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feature/#719-password-MOA-214

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbitai ignore or @coderabbit ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@github-actions
Copy link

github-actions bot commented Sep 4, 2025

Test Results

75 tests   75 ✅  2s ⏱️
12 suites   0 💤
12 files     0 ❌

Results for commit 029435b.

♻️ This comment has been updated with latest results.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
backend/src/main/java/moadong/user/entity/User.java (1)

38-39: 해시 비밀번호에 부적합한 @SiZe 제약 — 저장/검증 실패 가능성

BCrypt/Argon2 해시는 길이가 60자+입니다. 엔티티의 @Size(min=8, max=20)는 인코딩된 값과 충돌합니다. DTO에서만 길이/정책을 검증하고, 엔티티 제약은 완화하세요.

적용 예시:

-    @NotNull
-    @Size(min = 8, max = 20)
-    private String password;
+    @NotNull
+    private String password; // encoded hash 저장; 길이/복잡도 검증은 요청 DTO(@Password)에서 수행

Also applies to: 80-82

🧹 Nitpick comments (6)
backend/src/main/java/moadong/user/payload/request/UserUpdateRequest.java (1)

14-15: 메서드 명칭 정합성(encryptencode)

대칭/비대칭 암호화가 아닌 해시 인코딩이므로 의미상 encodePassword가 더 적합합니다. 호출부 일괄 치환 권장.

-    public UserUpdateRequest encryptPassword(PasswordEncoder passwordEncoder){
+    public UserUpdateRequest encodePassword(PasswordEncoder passwordEncoder){
         return new UserUpdateRequest(passwordEncoder.encode(this.password));
     }
backend/src/main/java/moadong/global/util/SecurePasswordGenerator.java (1)

10-16: 특수문자 집합·정책 외부화

하드코딩된 SPECIAL="!@#$%^"는 취약/불편할 수 있습니다. 구성값으로 외부화하고(예: application.yml) 필요 시 모호문자(O,0,l,1) 제외 옵션을 제공하세요.

backend/src/main/java/moadong/user/controller/UserController.java (1)

96-99: 권한 조건 강화 고려

isAuthenticated()만으로는 탈취 토큰에 의한 오남용 위험이 있습니다. 역할 기반 조건(예: 담당자/관리자 롤) 또는 재인증(비밀번호/2FA) 요구를 검토하세요.

backend/src/main/java/moadong/user/payload/response/TempPasswordResponse.java (1)

6-10: 응답 DTO에 @password 부착은 과도함

출력 값까지 Bean Validation을 걸면 예외/로그에 평문 정책이 노출될 수 있습니다. 응답 DTO에서는 제거하고, 입력 DTO에서만 정책 검증하세요.

 public record TempPasswordResponse(
         @NotNull
-        @Password
         String tempPassword
 ){ }
backend/src/main/java/moadong/user/service/UserCommandService.java (2)

41-41: SecurePasswordGenerator 주입: Bean 등록 여부 확인 필요
@AllArgsConstructor 주입을 쓰려면 SecurePasswordGenerator가 @Component/@bean으로 컨테이너에 등록돼 있어야 합니다. 유틸 성격이면 정적 메서드로도 충분할 수 있으니 선택지 검토 바랍니다.

추가가 필요하면 SecurePasswordGenerator에 아래와 같이 애노테이션만 붙이면 됩니다:

@Component
public class SecurePasswordGenerator { ... }

126-142: 감사 로그와 남용 방지

  • 누가(관리자 ID, IP) 언제 어떤 사용자에 대해 reset 했는지 감사 로그/이벤트를 남기세요.
  • 관리자 API에 rate limit(예: 사용자 단위/관리자 단위)와 알림(과다 시도) 추가를 권장합니다.

원하시면 도메인 이벤트 발행/감사 리포지토리/RateLimiter 인터셉터 예시를 제공하겠습니다.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between fb74867 and 20dad7e.

📒 Files selected for processing (6)
  • backend/src/main/java/moadong/global/util/SecurePasswordGenerator.java (1 hunks)
  • backend/src/main/java/moadong/user/controller/UserController.java (2 hunks)
  • backend/src/main/java/moadong/user/entity/User.java (1 hunks)
  • backend/src/main/java/moadong/user/payload/request/UserUpdateRequest.java (1 hunks)
  • backend/src/main/java/moadong/user/payload/response/TempPasswordResponse.java (1 hunks)
  • backend/src/main/java/moadong/user/service/UserCommandService.java (5 hunks)
🔇 Additional comments (2)
backend/src/main/java/moadong/user/service/UserCommandService.java (2)

12-12: LGTM – 보안용 유틸 import 추가 적절합니다.


21-21: LGTM – 응답 DTO import 추가 적절합니다.

Comment on lines +96 to +113
@PostMapping("/reset")
@Operation(summary = "사용자 비밀번호 초기화", description = "사용자 비밀번호를 초기화합니다.")
@PreAuthorize("isAuthenticated()")
@SecurityRequirement(name = "BearerAuth")
public ResponseEntity<?> reset(@CurrentUser CustomUserDetails user,
HttpServletResponse response) {
TempPasswordResponse tempPwdResponse = userCommandService.reset(user.getUserId());

ResponseCookie cookie = ResponseCookie.from("refresh_token", "")
.path("/")
.maxAge(0)
.httpOnly(true)
.sameSite("None")
.secure(true)
.build();
response.addHeader("Set-Cookie", cookie.toString());
return Response.ok(tempPwdResponse);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

⚠️ Potential issue

보안: 임시 비밀번호 응답 반환 시 캐시/로그 차단 필요

평문 비밀번호가 프록시/브라우저/서버 로그에 남을 수 있습니다. 최소한 응답 캐시 비활성화와 응답/요청 로깅 마스킹을 적용하세요. 장기적으로는 메일/SMS 전송으로 전환 권장.

패치 예시(캐시 금지 헤더):

     public ResponseEntity<?> reset(@CurrentUser CustomUserDetails user,
                                    HttpServletResponse response) {
         TempPasswordResponse tempPwdResponse = userCommandService.reset(user.getUserId());

         ResponseCookie cookie = ResponseCookie.from("refresh_token", "")
                 .path("/")
                 .maxAge(0)
                 .httpOnly(true)
                 .sameSite("None")
                 .secure(true)
                 .build();
         response.addHeader("Set-Cookie", cookie.toString());
+        response.setHeader("Cache-Control", "no-store, no-cache, must-revalidate, max-age=0");
+        response.setHeader("Pragma", "no-cache");
         return Response.ok(tempPwdResponse);
     }

또한, 액세스 토큰 무효화 전략(토큰 버전, 블랙리스트, 짧은 TTL)도 병행 검토 바랍니다.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
@PostMapping("/reset")
@Operation(summary = "사용자 비밀번호 초기화", description = "사용자 비밀번호를 초기화합니다.")
@PreAuthorize("isAuthenticated()")
@SecurityRequirement(name = "BearerAuth")
public ResponseEntity<?> reset(@CurrentUser CustomUserDetails user,
HttpServletResponse response) {
TempPasswordResponse tempPwdResponse = userCommandService.reset(user.getUserId());
ResponseCookie cookie = ResponseCookie.from("refresh_token", "")
.path("/")
.maxAge(0)
.httpOnly(true)
.sameSite("None")
.secure(true)
.build();
response.addHeader("Set-Cookie", cookie.toString());
return Response.ok(tempPwdResponse);
}
@PostMapping("/reset")
@Operation(summary = "사용자 비밀번호 초기화", description = "사용자 비밀번호를 초기화합니다.")
@PreAuthorize("isAuthenticated()")
@SecurityRequirement(name = "BearerAuth")
public ResponseEntity<?> reset(@CurrentUser CustomUserDetails user,
HttpServletResponse response) {
TempPasswordResponse tempPwdResponse = userCommandService.reset(user.getUserId());
ResponseCookie cookie = ResponseCookie.from("refresh_token", "")
.path("/")
.maxAge(0)
.httpOnly(true)
.sameSite("None")
.secure(true)
.build();
response.addHeader("Set-Cookie", cookie.toString());
response.setHeader("Cache-Control", "no-store, no-cache, must-revalidate, max-age=0");
response.setHeader("Pragma", "no-cache");
return Response.ok(tempPwdResponse);
}

Comment on lines +104 to +111
ResponseCookie cookie = ResponseCookie.from("refresh_token", "")
.path("/")
.maxAge(0)
.httpOnly(true)
.sameSite("None")
.secure(true)
.build();
response.addHeader("Set-Cookie", cookie.toString());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

쿠키 제거 시 Domain 정합성 확인

로그인 시 설정한 refresh_token의 Domain/Path와 동일해야 삭제됩니다. 현재 Domain 미설정 상태입니다. 발급부와 동일하게 맞추는지 확인 부탁드립니다.

아래 스크립트로 발급부 설정을 점검하세요:


🏁 Script executed:

#!/bin/bash
# refresh_token 쿠키 설정 위치/속성 확인
rg -nC2 "ResponseCookie\.from\(\s*\"refresh_token\"" --type=java

Length of output: 1706


🏁 Script executed:

#!/bin/bash
sed -n '1,200p' backend/src/main/java/moadong/user/util/CookieMaker.java

Length of output: 795


refresh_token 삭제 쿠키에 domain 설정 추가
UserController.java의 로그아웃(63–67행) 및 비밀번호 재설정(104–110행) 삭제 로직에서 ResponseCookie 빌더에 .domain(".moadong.com")을 추가해 주세요.

🤖 Prompt for AI Agents
In backend/src/main/java/moadong/user/controller/UserController.java around
lines 63–67 and 104–111, the ResponseCookie builders for clearing the
refresh_token do not set the cookie domain; update both builders to include
.domain(".moadong.com") in the fluent chain before build() so the Set-Cookie
includes the correct domain when removing the refresh_token.

Comment on lines +80 to +82
public void resetPassword(String encodedPassword) { //초기화된 비밀번호 업데이트
this.password = encodedPassword;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

비밀번호 초기화 시 보안 후속처리 누락

resetPassword 호출 시점 기록과 세션 토큰 무효화를 엔티티/도메인 이벤트로 일관되게 다루면 안전합니다(토큰 버전 증가 또는 passwordChangedAt 비교).

예시 스니펫(참고):

+    private Date passwordChangedAt;
...
     public void resetPassword(String encodedPassword) { //초기화된 비밀번호 업데이트
         this.password = encodedPassword;
+        this.passwordChangedAt = new Date();
+        this.refreshToken = null;
     }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
public void resetPassword(String encodedPassword) { //초기화된 비밀번호 업데이트
this.password = encodedPassword;
}
// in User.java, add at the top of the class alongside the other fields:
private Date passwordChangedAt;
...
// update the resetPassword method:
public void resetPassword(String encodedPassword) { // 초기화된 비밀번호 업데이트
this.password = encodedPassword;
this.passwordChangedAt = new Date();
this.refreshToken = null;
}
🤖 Prompt for AI Agents
In backend/src/main/java/moadong/user/entity/User.java around lines 80–82,
resetPassword currently only assigns the new encoded password; you must also
record the password-change moment and invalidate existing sessions/tokens.
Modify resetPassword to set a passwordChangedAt timestamp (e.g., Instant.now())
and/or increment a tokenVersion field, and publish a domain event (e.g.,
UserPasswordResetEvent) so downstream services can revoke sessions; ensure this
runs in the same transaction as the password update and that JWT/session
validation logic checks passwordChangedAt/tokenVersion to reject old tokens.

Comment on lines +126 to +142
public TempPasswordResponse reset(String userId) {
User user = userRepository.findUserByUserId(userId)
.orElseThrow(() -> new RestApiException(ErrorCode.USER_NOT_EXIST));

//랜덤 임시 비밀번호 생성
TempPasswordResponse tempPwdResponse = new TempPasswordResponse(
securePasswordGenerator.generate(8));

//암호화
user.resetPassword(passwordEncoder.encode(tempPwdResponse.tempPassword()));

user.updateRefreshToken(null);
userRepository.save(user);

return tempPwdResponse;
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

❓ Verification inconclusive

임시 비밀번호를 응답 본문으로 반환하는 것은 고위험 보안 이슈입니다.

  • 즉시: 응답/액세스 로그에서 본문 마스킹, 캐시 방지 헤더(no-store) 적용, APM/디버그 로거에 노출 금지.
  • 근본: API 응답으로 비밀번호 전달을 제거하고, 일회성 리셋 토큰+아웃오브밴드(이메일/SMS)로 전환 권장.

원하시면 컨트롤러/필터 레벨 마스킹 및 헤더 설정 패치를 제안하겠습니다.


임시 비밀번호 평문 반환 차단

  • 서비스(UserCommandService.reset)에서 TempPasswordResponse에 평문 비밀번호를 포함하지 않도록 삭제
  • 로그·액세스 기록 마스킹, Cache-Control: no-store 헤더 적용, APM/디버거 노출 방지
  • 근본 대책: API 응답 대신 이메일/SMS 일회성 리셋 토큰 방식으로 전환
🤖 Prompt for AI Agents
In backend/src/main/java/moadong/user/service/UserCommandService.java around
lines 126 to 142, the method currently generates a plaintext temporary password,
places it in TempPasswordResponse and returns it — remove returning or storing
the plaintext in any API response or logs; instead generate a one-time reset
token (or store only a hashed temporary credential) and send the reset
link/token via email/SMS, clear any temp secrets from memory immediately, ensure
no plaintext is written to logs or caches, add Cache-Control: no-store on the
endpoint response, and update callers/tests to expect a token-sent
acknowledgement (not the password) while persisting only hashed values or token
metadata server-side with expiry.

Comment on lines +131 to +133
TempPasswordResponse tempPwdResponse = new TempPasswordResponse(
securePasswordGenerator.generate(8));

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

❓ Verification inconclusive

임시 비밀번호 길이 8 → 최소 16자로 상수/설정화하세요.
매직넘버 제거 및 구성 가능하게 하여 보안 정책을 코드 수정 없이 바꿀 수 있게 하는 것이 좋습니다.

아래 diff로 생성 길이를 상수로 치환하세요:

-        TempPasswordResponse tempPwdResponse = new TempPasswordResponse(
-                securePasswordGenerator.generate(8));
+        TempPasswordResponse tempPwdResponse = new TempPasswordResponse(
+                securePasswordGenerator.generate(TEMP_PASSWORD_LENGTH));

그리고 클래스 상단에 상수 추가:

// 클래스 필드 영역
private static final int TEMP_PASSWORD_LENGTH = 16; // 정책에 따라 16~20 권장

추가로, 생성기가 대/소문자+숫자+기호를 모두 포함하도록 보장하는지 점검 바랍니다.


임시 비밀번호 길이 매직 넘버 제거 및 상수화
securePasswordGenerator.generate(8)의 8을 private static final int TEMP_PASSWORD_LENGTH 또는 외부 설정(application.yml 등)으로 분리하고 기본값을 16 이상으로 지정하세요.
생성 로직이 대문자·소문자·숫자·특수문자를 모두 포함하도록 구현되어 있는지도 검증 바랍니다.

Comment on lines +135 to +139
user.resetPassword(passwordEncoder.encode(tempPwdResponse.tempPassword()));

user.updateRefreshToken(null);
userRepository.save(user);

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

비밀번호 재설정 시 기존 Access Token 무효화 전략 필요
refresh 토큰만 null 처리하면 유출된 Access Token은 만료 시점까지 유효합니다. User에 passwordChangedAt(또는 tokenVersion)을 기록하고, JWT 검증 시 이전에 발급된 토큰을 거부하도록 하는 방식을 권장합니다.

원하시면 User 필드/필터/JWT 클레임 변경까지 포함한 패치안을 드리겠습니다.

Copy link
Member

@Zepelown Zepelown left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

고생하셨습니다.
버그 하나 있는 거 같은데 수정 부탁드릴게요

- 최소 길이 검증 추가
@alsdddk
Copy link
Collaborator Author

alsdddk commented Sep 4, 2025

수정 완료 됐슴다

@alsdddk alsdddk requested a review from Zepelown September 4, 2025 13:47
Copy link
Member

@Zepelown Zepelown left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

고생하셨습니다

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (1)
backend/src/main/java/moadong/global/util/SecurePasswordGenerator.java (1)

20-33: 이전 지적사항 해결됨: 길이(+3) 버그 수정 및 최소 길이 검증 추가 OK

for (int i = 3; i < length; i++)로 수정되어 요청 길이 그대로 생성되고, 최소 길이(8) 검증도 추가되었습니다.

🧹 Nitpick comments (3)
backend/src/main/java/moadong/global/util/SecurePasswordGenerator.java (3)

10-16: 특수문자 집합 확대 또는 정책화 권장

현 집합(!@#$%^)은 너무 좁습니다. 기본값을 확장하거나(메일/SMS 전송 이슈가 없다면) 구성값으로 관리하는 편이 좋습니다.

적용 예:

-    private static final String SPECIAL = "!@#$%^";
+    private static final String SPECIAL = "!@#$%^&*()_-+=[]{}:;,.?";

15-18: 길이 정책 상수화(12+ 권장) 및 상한 추가

임시 비밀번호 길이는 12~16을 권장합니다(NIST는 길이 중심 권고). 상수화해 호출부와 정책을 일관되게 관리하면 좋습니다. 상한도 두어 오용을 방지하세요.

+    private static final int MIN_LENGTH = 12;
+    private static final int MAX_LENGTH = 128;
@@
-        if (length < 8) {
-            throw new IllegalArgumentException("Length must be at least 8");}
+        if (length < MIN_LENGTH) {
+            throw new IllegalArgumentException("Length must be >= " + MIN_LENGTH);
+        }
+        if (length > MAX_LENGTH) {
+            throw new IllegalArgumentException("Length must be <= " + MAX_LENGTH);
+        }

호출부(UserCommandService 등)에서 현재 generate(8) 사용 시 함께 상향이 필요한지 확인 부탁드립니다.

Also applies to: 20-23


19-37: 임시 비밀번호 전달 방식 보안 가드레일 제안

API 응답으로 직접 전달하는 현재 방식은 위험도가 있습니다. 아래 가드레일을 고려해 주세요.

  • 단회 노출(one-time display) 및 즉시 만료(예: 5–10분) 토큰화 또는 최초 로그인 시 강제 변경.
  • 재발급 시 이전 임시 비밀번호 즉시 무효화 및 리프레시 토큰 전부 폐기.
  • 관리자·대상 사용자·요청 IP 등 감사 로그 남기기 + 레이트 리미트.
  • 문자/메일 전송을 도입할 때, 전송 매체에 안전한 문자셋(모호 문자 제외) 사용.
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 20dad7e and 029435b.

📒 Files selected for processing (1)
  • backend/src/main/java/moadong/global/util/SecurePasswordGenerator.java (1 hunks)
🔇 Additional comments (2)
backend/src/main/java/moadong/global/util/SecurePasswordGenerator.java (2)

39-48: 셔플 구현 적절

Fisher–Yates 방식으로 SecureRandom 사용하며 편향 없어 보입니다. 좋습니다.


7-9: SecureRandom 재사용 OK

인스턴스 필드로 재사용해 보안·성능 균형이 좋습니다. 상태가 없으므로 빈으로 두는 것도 타당합니다.

Also applies to: 17-18

@alsdddk alsdddk merged commit 838838c into develop/be Sep 5, 2025
5 checks passed
@alsdddk alsdddk deleted the feature/#719-password-MOA-214 branch September 26, 2025 06:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

📬 API 서버 API 통신 작업 💾 BE Backend

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

Comments