Skip to content

[의사결정] FE-BE 비 로그인 사용자 식별 전략 #85

@wlgns12370

Description

@wlgns12370

요구사항

  • 비로그인 사용자는 동아리에 좋아요를 1초마다 클릭할 수 있다.

문제 정의

비로그인 사용자는 서버가 신원을 확인할 수단이 없다. 이 상태에서 좋아요 기능을 제공하면 다음 문제가 발생합니다.

  • 동일 사용자가 무제한 좋아요 가능 → 경쟁 의미 없음
  • 스크립트로 자동화된 매크로 공격 → 특정 부스 순위 조작

식별 전략 선택 과정

식별 전략: IP + Device ID 조합을 선택했습니다.

식별자 사용 시 문제 조합 시 역할
IP 학교 WiFi는 수천 명이 같은 IP → 정상 사용자 차단 매크로/VPN 없는 일반 공격 차단
Device Id 값을 바꿔서 전송하면 서버가 검증 불가 기기 단위 식별
IP + Device Id 값을 바꿔서 전송하면 서버가 검증 불가 둘 다 바꿔야 우회 가능 → 공격 비용 상승

Device ID 발급 방식

id 발급을 클라이언트에서 할지, 서버에서 할지 고민을 해보았습니다.

[기각] X-Device-Id 헤더 (클라이언트 생성)
→ curl -H "X-Device-Id: 아무값" 으로 위조 가능

[선택] HttpOnly Cookie (서버 발급)
→ JS 접근 불가, 브라우저가 자동 전송, 위조 불가

클라이언트가 id를 생성해서 헤더로 보내는 방식은 헤더 값을 임의로 바꿀 수 있어 서버 검증이 불가능하다고 생각했습니다. 그래서 서버가 직접 id를 생성하고 HttpOnly Cookie로 발급하면 JS에서 접근이 불가능 해 식별할 수 있다고 생각했습니다.

논의1 reCAPTCHA v3 도입 여부

구글에서 제공하는 reCAPTCHA v3라는 기술은 사용자가 아무것도 하지 않아도 구글이 브라우저 행동 패턴을 분석해 봇 여부를 0~1점으로 채점합니다. 해당 기술을 도입하면 매크로나 해커에 대한 공격을 방어할 수 있을 것 같습니다.

프론트엔드 구현 비용

작업 설명
스크립트 로드 <script src="https://www.google.com/recaptcha/api.js?render=SITE_KEY"></script> 추가
토큰 발급 좋아요 버튼 클릭 시 grecaptcha.execute(SITE_KEY, {action: 'like'}) 호출
토큰 전송 좋아요 API 요청 시 X-Recaptcha-Token 헤더에 토큰 포함

도입 시 달라지는 흐름
좋아요 버튼 클릭
→ grecaptcha.execute() 호출 (토큰 발급, 약 100~300ms 소요)
→ POST /api/v1/booths/{id}/likes (X-Recaptcha-Token 헤더 포함)
→ 서버에서 Google 검증 후 처리

해당 부분에 의견 부탁드립니다.

논의2 프론트와 상호작용하는 서버 파이프라인

제가 생각해본 API 작동 절차를 시퀀스다이어그램으로 표현해 보았습니다. 의견이 필요합니다!

sequenceDiagram
    participant B as Browser
    participant F as DeviceIdCookieFilter
    participant S as Spring Server
    participant R as Redis

    Note over B,S: 좋아요 클릭
    B->>F: POST /api/v1/booths/{booth-id}/likes (credentials: include)
    alt 쿠키 없음 (최초 요청)
        F-->>B: Set-Cookie: deviceId=UUID&#59; HttpOnly&#59; Path=/&#59; MaxAge=1년
    else 쿠키 있음
        Note over F: 기존 deviceId 사용
    end
    F->>S: deviceId를 request attribute로 전달
    S->>R: SET like:rate:{ip}:{deviceId}:{boothId} 1 EX 1 NX
    alt 1초 이내 재요청
        R-->>S: nil (key 존재)
        S-->>B: 429 Too Many Requests
    else 정상 요청
        R-->>S: OK
        S->>R: ZINCRBY like:ranking 1 {boothId}
        R-->>S: 증가된 score
        S-->>B: 200 OK (누적 좋아요 수)
    end

    Note over B,S: 좋아요 수 조회
    B->>S: GET /api/v1/booths/{booth-id}/likes
    S->>R: ZSCORE like:ranking {boothId}
    R-->>S: score 값
    S-->>B: 200 OK (좋아요 수)
Loading
순서 시점 행동 비고
1 모든 API 요청 credentials: 'include' 옵션 설정 브라우저가 쿠키 자동 전송
2 좋아요 버튼 클릭 POST /api/v1/booths/{booth-id}/likes 호출 쿠키 없으면 서버 필터가 자동 발급
3 좋아요 수 표시 GET /api/v1/booths/{booth-id}/likes 호출 쿠키 불필요 (공개 데이터)
4 1초 내 재클릭 서버에서 429 Too Many Requests 응답 프론트에서 버튼 비활성화 처리 권장

해당 로직에서 프론트 분들은 낙관적 업데이트나 조회를 하지않고 내부적으로 카운트하는 상태 관리 등을 고민해보시면 될 것 같습니다.

해당 부분에 의견 부탁드립니다.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions