You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
비로그인 사용자는 서버가 신원을 확인할 수단이 없다. 이 상태에서 좋아요 기능을 제공하면 다음 문제가 발생합니다.
동일 사용자가 무제한 좋아요 가능 → 경쟁 의미 없음
스크립트로 자동화된 매크로 공격 → 특정 부스 순위 조작
식별 전략 선택 과정
식별 전략: 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; HttpOnly; Path=/; 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 응답
프론트에서 버튼 비활성화 처리 권장
해당 로직에서 프론트 분들은 낙관적 업데이트나 조회를 하지않고 내부적으로 카운트하는 상태 관리 등을 고민해보시면 될 것 같습니다.
요구사항
문제 정의
비로그인 사용자는 서버가 신원을 확인할 수단이 없다. 이 상태에서 좋아요 기능을 제공하면 다음 문제가 발생합니다.
식별 전략 선택 과정
식별 전략: 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점으로 채점합니다. 해당 기술을 도입하면 매크로나 해커에 대한 공격을 방어할 수 있을 것 같습니다.
프론트엔드 구현 비용
도입 시 달라지는 흐름
좋아요 버튼 클릭
→ 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; HttpOnly; Path=/; 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 (좋아요 수)해당 로직에서 프론트 분들은 낙관적 업데이트나 조회를 하지않고 내부적으로 카운트하는 상태 관리 등을 고민해보시면 될 것 같습니다.
해당 부분에 의견 부탁드립니다.