버그 설명
DiscourseConnect Provider (SSO Provider)로 Discourse를 사용할 때, 사용자가 의존하는 당사(Relying Party)로 보내는 302 리다이렉트 URL에 이메일 주소가 노출됩니다. 이는 lib/second_factor/actions/discourse_connect_provider.rb의 populate_user_data 메서드가 항상 이메일을 설정하기 때문입니다:
def populate_user_data(sso)
sso.name = current_user.name
sso.username = current_user.username
sso.email = current_user.email # <-- 항상 포함됨
sso.external_id = current_user.id.to_s
# ...
end
이 이메일은 Base64로 인코딩되어 리다이렉트 URL에 포함됩니다:
https://target-site.com/callback?sso=<base64_payload>&sig=<hmac_signature>
Base64 페이로드를 디코딩하면 이메일 주소가 평문으로 확인됩니다.
영향
- 브라우저 기록: 이메일이 브라우저 기록에 남습니다.
- Nginx 로그: 전체 URL이 nginx 액세스 로그에 기록됩니다.
- 대상 사이트 로그: 의존하는 당사가 사용자의 명시적 동의 없이 이메일을 수신합니다.
- 사용자 기대: 사용자는 일반적으로 "나는 합법적인 사용자입니다"라는 것을 증명하기 위해 인증을 허용할 뿐, 이메일이 공유될 것이라고 기대하지 않습니다.
기대되는 동작
사용자는 의존하는 당사에게 이메일을 공유할지 여부를 제어할 수 있어야 합니다. discourse_connect_overrides_groups, discourse_connect_overrides_avatar 등과 유사한 구성 옵션이 있어야 합니다.
현재 이 동작을 비활성화할 사이트 설정이 없습니다. 이 동작을 오버라이드하는 플러그인을 작성하는 것은 가능하지만 이상적이지 않습니다.
재현 단계
enable_discourse_connect_provider를 활성화합니다.discourse_connect_provider_secrets를 구성합니다 (예: *.example.com|secret123).- 사용자가 DiscourseConnect Provider를 통해 인증하도록 합니다.
- 302 리다이렉트 URL을 확인합니다 - URL 매개변수에 이메일이 보입니다.
리다이렉트 URL은 다음과 같습니다:
https://relying-party.com/sso?sso=bm9uY2U9xxx&sig=xxx
sso 매개변수를 디코딩하면 다음이 나타납니다:
nonce=xxx&return_sso_url=xxx&email=user@example.com&external_id=123
환경
- Discourse 버전: (최신)
- 셀프 호스팅
가능한 수정 제안
- 응답에 이메일을 포함할지 여부를 제어하기 위해
discourse_connect_provider_includes_email(기본값: 하위 호환성을 위해 true)와 같은 사이트 설정을 추가합니다. - 또는 URL 로깅을 피하기 위해 GET 302 리다이렉트 대신 POST 기반 콜백을 구현합니다.