사용자 인증을 위한 JavaScript 페이로드 암호화의 안전한 방법

Discourse 사용자의 user_api_key를 가져오기 위해 프론트엔드 애플리케이션(Vue.js로 작성됨)을 인증하는 로그인 플로우를 만들었습니다. @sam 님이 여기에 작성한 가이드라인을 따랐습니다.

인증은 정상적으로 작동하지만, 현재 사설 키를 안전하게 저장하는 방법에 대해 우려가 있습니다. 현재 사설 키는 .env 파일에 로컬로 저장되어 있으며, 페이로드의 암호화/복호화에 사용됩니다. 이것이 페이로드를 암호화하는 안전한 방법인지, 만약 아니라면 프론트엔드 자바스크립트 애플리케이션을 위한 대안은 무엇인지 궁금합니다.

감사합니다!

Electron/Tauri 앱에서처럼? 뭘 선택해야 할지 전혀 모르겠어요. 아마도 안전한 저장소를 위한 어떤 메커니즘이 있을 거라고 추측하는데, 운이 좋으면 Mac에서는 키체인(keychain), Windows에서는 비슷한 기능을 사용할 수 있을지도 모릅니다.

이것은 서버에 호스팅된 웹 애플리케이션입니다. 사설 키가 노출될 경우 페이로드가 가로채질 수 있다는 점이 우려됩니다. 이는 자바스크립트 암호화와 관련된 보다 일반적인 문제임을 알고 있지만, Discourse에서 웹 앱을 인증하는 데 더 안전한 관행이 있는지 확인하고자 질문합니다.

사용자 API 키 시스템은 이 사용 사례를 위해 설계된 것이 아닙니다. 개인 키를 생성하고 나서 서버에 사용자 대신 저장하는 일련의 과정을 거친다면, 굳이 이런 복잡한 절차를 따를 필요가 있나요? 그냥 서버 측에서 키를 생성하면 됩니다.

실제 문제가 무엇인지 훨씬 더 자세히 이해하지 못해 구체적인 조언을 드리기 어렵습니다. 웹 앱에 프록시 엔드포인트를 제공하고 모든 인증을 앱에서 처리하는 방식으로 단순화하는 것은 어떨까요?

동료 @owengot와(과) 이야기를 나눈 후, 문제에 대한 보다 자세한 설명을 드리겠습니다.

해당 웹 애플리케이션은 Vue.js 기반이며 자체적인 서버 사이드 컴포넌트가 없습니다. 대신 Discourse API를 사용하는 독립형 웹 애플리케이션으로, 일종의 서버 역할을 Discourse가 수행합니다. 사용 사례로는 독립형 투표나 양식이 있으며, 콘텐츠는 사용자의 계정 하에 있는 Discourse 토픽에 게시됩니다. 이러한 용도에서 커스텀 서버 사이드 컴포넌트 없이 이러한 소프트웨어를 만들 수 있다는 것은 좋은 아키텍처로 보였기 때문에, 이상적으로는 “서버 사이드” 작업을 피하는 것이 좋습니다.

현재 사용자 API 키를 얻는 과정은 의도대로 작동하지만, @owengot는 JavaScript에서 개인 키를 생성하는 단계가 오래 걸린다는 것을 발견했습니다(최대 10초까지 걸리며, JavaScript에서 비효율적인 탓입니다…). 그 후 그는 동일한 개인 키를 여러 사용자에게 사용할 수 있다는 해킹 기법을 발견했고, 웹 애플리케이션의 다른 정적 파일(JS, CSS 등)을 서빙하는 웹 호스트에 있는 파일에 이를 저장했습니다. 이렇게 하면 빠르지만 … 이제 그 개인 키는 (1) 공유 키가 되고 (2) 공개적으로 접근 가능한 URL로 외부에 노출되어 저장됩니다. 이건, 음, 무섭습니다 :fearful:

그래서 저는 이 해킹 기법의 보안 영향에 대해 Discourse 팀에게 질문해야 한다고 말했습니다. 두 가지 기본적인 대안을 보고 있습니다:

  • 만약 그 개인 키가 “단순히” 페이로드 암호화에 사용된다면, 통신이 SSL / HTTPS를 통해 암호화되어 있으므로 이를 수용할 수 있을 것입니다.

  • 그러나 그 개인 키를 알면 나중에 누군가의 Discourse 계정으로 게시하는 데 사용되는 최종 사용자 API 키를 계산할 수 있게 된다면, 이 해킹 기법은 당연히 허용할 수 없게 됩니다.

@owengot이 개발한 소프트웨어를 사용하기 시작하면서 이 부분에도 관심이 생겼습니다. @tanius, @sam, 이것이 안전한 방법이라고 생각하시나요?

네, 그렇게 하면 안 됩니다. 개인 키는 클라이언트별로 비공개로 유지되어야 합니다.

10초의 키 생성 시간은 감수해야 할 비용입니다. 더 빠르게 처리할 수 있는 JS 암호화 API가 몇 가지 있습니다.