fix: 보안·안정성·동시성 개선 3차
All checks were successful
Server CI/CD / deploy (push) Successful in 1m31s

- 입력 검증 강화 (로그인/체인 핸들러 전체)
- boss raid 비관적 잠금으로 동시성 문제 해결
- SSAFY 사용자명 sanitize + 트랜잭션 처리
- constant-time API 키 비교, 보안 헤더, graceful shutdown
- 안전하지 않은 기본값 경고 추가

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-13 21:40:06 +09:00
parent cc751653c4
commit d597ef2d46
11 changed files with 247 additions and 97 deletions

View File

@@ -313,21 +313,36 @@ func (s *Service) SSAFYLogin(code string) (accessToken, refreshToken string, use
}
ssafyID := userInfo.UserID
username := "ssafy_" + ssafyID
user = &User{
Username: username,
PasswordHash: string(hash),
Role: RoleUser,
SsafyID: &ssafyID,
// SSAFY ID에서 영문 소문자+숫자만 추출하여 안전한 username 생성
safeID := sanitizeForUsername(ssafyID)
if safeID == "" {
safeID = hex.EncodeToString(randomBytes[:8])
}
if err := s.repo.Create(user); err != nil {
username := "ssafy_" + safeID
if len(username) > 50 {
username = username[:50]
}
var newUserID uint
err = s.repo.Transaction(func(txRepo *Repository) error {
user = &User{
Username: username,
PasswordHash: string(hash),
Role: RoleUser,
SsafyID: &ssafyID,
}
return txRepo.Create(user)
})
if err != nil {
return "", "", nil, fmt.Errorf("계정 생성 실패: %v", err)
}
newUserID = user.ID
if s.walletCreator != nil {
if err := s.walletCreator(user.ID); err != nil {
log.Printf("wallet creation failed for SSAFY user %d: %v — rolling back", user.ID, err)
if delErr := s.repo.Delete(user.ID); delErr != nil {
log.Printf("WARNING: rollback delete also failed for SSAFY user %d: %v", user.ID, delErr)
if err := s.walletCreator(newUserID); err != nil {
log.Printf("wallet creation failed for SSAFY user %d: %v — rolling back", newUserID, err)
if delErr := s.repo.Delete(newUserID); delErr != nil {
log.Printf("WARNING: rollback delete also failed for SSAFY user %d: %v", newUserID, delErr)
}
return "", "", nil, fmt.Errorf("계정 초기화에 실패했습니다. 잠시 후 다시 시도해주세요")
}
@@ -373,6 +388,17 @@ func (s *Service) VerifyToken(tokenStr string) (string, error) {
return claims.Username, nil
}
// sanitizeForUsername strips characters that are not [a-z0-9_-].
func sanitizeForUsername(s string) string {
var b strings.Builder
for _, c := range strings.ToLower(s) {
if (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || c == '_' || c == '-' {
b.WriteRune(c)
}
}
return b.String()
}
func (s *Service) EnsureAdmin(username, password string) error {
if _, err := s.repo.FindByUsername(username); err == nil {
return nil