Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(snackgame.biz): 게임 결과를 서명한다 #179

Merged
merged 6 commits into from
Oct 10, 2024

Conversation

0chil
Copy link
Collaborator

@0chil 0chil commented Oct 9, 2024

관련 이슈

변경 사항

AS-IS

스낵게임 Biz의 게임 결과가 평문이기 때문에 타 비즈니스에서 신뢰할 수 없었습니다

TO-BE

RS256 알고리즘을 통해 게임 결과를 서명합니다.
서명된 게임 결과는 스낵게임 Biz에서 제공하는 공개키를 사용해 검증할 수 있습니다.
자세한 문서는 블로그에 기록했습니다: https://biz.snackga.me

Rest 컨트롤러 메서드에 @Signed 어노테이션을 붙여 서명할 수 있습니다
image

class ResponseSigner(
val signer: Signer,
val objectMapper: ObjectMapper
) : ResponseBodyAdvice<Any> {
Copy link
Collaborator

Choose a reason for hiding this comment

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

ResponseBodyAdvice가 공통된 응답을 위해 쓰인다고 하는데 지금처럼 DTO를 만들어서 하던 방법과 차이점이나 ResponseBodyAdvice를 쓰게 된 이유가 있을까요??

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

선요약: 최소한의 접점만으로 문제를 가장 직접적으로 해결하는 방법이다

이것도 풀자면 긴 스토리긴 한데요.
우선 목적은 최대한 기존 코드를 건드리지 않고 반환값을 서명하는 것입니다. (서명에 대한 관심사 분리)
물론 멋있다는 점도 한 몫 합니다 ㅋ.ㅋ

원래 HandlerMethodReturnValueHandler 혹은 HandlerInterceptor를 사용하려고 했습니다. (둘 다 실패)

전자는 @ResponseBody에 대한 ReturnValueHandler가 이미 있기 때문에 제가 새로 추가한 핸들러가 실행되지 않아서 실패했구용

후자는 HttpServletResponse의 body를 조작해야 하는데, 이 body는 이미 String이 된 상태이므로 조작이 조금 복잡해지며 부작용의 우려가 있습니다.
이것보다 더 직접적인 방법(ResponseBodyAdvice)가 있어 이걸 선택했습니다.

ResponseBodyAdvice 인터페이스는 이름처럼 @ResponseBody를 처리하는데 참고자료의 역할을 할 수 있습니다. (Spring API 문서)
말씀해주신 목적으로도 사용하긴 하는 것 같은데,
특별히 '공통된 응답을 만드는데 사용하라' 같은 의도는 없는 것으로 보입니다.
그래서 앞서 말씀드린 목적으로 사용할 수 있다고 생각했구요,

이 친구의 작동방식을 고려해보면 - 실제로 MessageConverter가 바디를 변환하기 직전!에 작동하므로, 변환하고자 하는 값을 직접적으로(타입도 살아있는 상태로) 받고 제어할 수 있게 됩니다.
이 틈에 기존 객체에 서명하고 감쌈으로써 최소한의 접점만으로 원하는 목적을 달성하는 AOP를 할 수 있게 됩니다.

Copy link
Collaborator

Choose a reason for hiding this comment

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

역시 땡칠... 어노테이션만으로 처리할 수 있게 된게 인상적입니다..
오늘도 하나배워갑니다 ☺️


return Jwts.builder()
.header().keyId(privateKey.hashCode().toString())
Copy link
Collaborator

Choose a reason for hiding this comment

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

keyId를 추가하는 것은 무엇을 위한 작업인가요??

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

이 부분은 스낵게임 Biz 문서 하단을 보시면 이해가 쉬울 것 같은데요.

RSASHA로 서명된 JWT는 공개키인 JWK로 검증이 가능합니다.
이 JWK를 여러개 묶어놓은게 JWKS(JSON Web Key Set)인데요.
그 키들 중에서 해당 JWT에 맞는 공개키를 찾는데 kid를 사용할 수 있습니다.
이렇게 되면 '키 순환'이 가능해진다는 장점도 있습니다.
만일 기존 키페어의 유출/분실 사고가 발생한다면 키셋에서 해당 키를 제거하고 새 키페어를 등록해줄 수 있습니다.
그러면 기존 JWT는 검증에 실패하고, 새 키페어로 서명된 JWT는 검증이 잘 되겠죠?
이런 장점들 때문에 서명에 사용한 keyId를 적고, 키를 관리하는 것이 좋습니다.

Copy link
Collaborator

Choose a reason for hiding this comment

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

아하 이해했습니다! 자세한 설명 감사드립니다 🫢

Copy link
Collaborator

@Hwanvely Hwanvely left a comment

Choose a reason for hiding this comment

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

이번 작업도 너무 고생하셨습니다! 심사도 완료돼서 이제 다음단계로 나아갈 수 있을 것 같네요 ㅎㅎ

@0chil 0chil merged commit 3c71e28 into dev Oct 10, 2024
13 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
Status: Done
Development

Successfully merging this pull request may close these issues.

스낵게임 Biz 고객이 게임결과를 검증할 수 있다
2 participants