보안/프라이버시 문제: DiscourseConnect 제공자 리다이렉트 URL에 이메일 노출

Describe the bug

When using Discourse as a DiscourseConnect Provider (SSO Provider), the user’s email address is exposed in the 302 redirect URL to the relying party. This happens because the populate_user_data method in lib/second_factor/actions/discourse_connect_provider.rb always sets the email:

  def populate_user_data(sso)
    sso.name = current_user.name
    sso.username = current_user.username
    sso.email = current_user.email  # <-- Always included
    sso.external_id = current_user.id.to_s
    # ...
  end

This email is thenBase64-encoded and included in the redirect URL:
https://target-site.com/callback?sso=<base64_payload>&sig=<hmac_signature>

Decoding the base64 payload reveals the email address in plain text.

Impact

  1. Browser history: Email is recorded in browser history
  2. Nginx logs: The full URL is logged in nginx access logs
  3. Target site logs: The relying party receives the email without explicit user consent
  4. User expectation: Users typically authorize to prove “I am a legitimate user” - they don’t expect their email to be shared

Expected behavior

Users should be able to control whether their email is shared with the relying party. There should be a configuration option similar to discourse_connect_overrides_groups, discourse_connect_overrides_avatar, etc.

Currently there is no site setting to disable this behavior. Writing a plugin to override this behavior is possible but not ideal.

Reproduction steps

  1. Enable enable_discourse_connect_provider
  2. Configure discourse_connect_provider_secrets (e.g., *.example.com|secret123)
  3. Have a user authenticate via DiscourseConnect Provider
  4. Check the 302 redirect URL - email is visible in the URL parameters

The redirect URL looks like:
https://relying-party.com/sso?sso=bm9uY2U9xxx&sig=xxx

Decoding sso parameter reveals:
nonce=xxx&return_sso_url=xxx&email=user@example.com&external_id=123

Environment

  • Discourse version: (latest)
  • Self-hosted

Possible fix suggestions

  1. Add a site setting like discourse_connect_provider_includes_email (default: true for backward compatibility) to control whether email is included in the response
  2. Or implement POST-based callback instead of GET 302 redirect to avoid URL logging
1개의 좋아요

Isn’t that also encrypted with the shared secret?

1개의 좋아요

Hi Falco,

The HMAC signature only ensures the payload hasn’t been tampered with - it doesn’t encrypt the data. Base64 is encoding, not encryption, so anyone can decode it to read the contents.

The core issue is: we don’t want to expose unnecessary user information (like email) to “third-party sites using Discourse for SSO login”. Users authorize only to prove “I am a legitimate user” - they don’t expect their email to be shared.

Would a site setting to control whether email is returned be acceptable?

I’m curious, what is this downstream third party that has implemented our custom SSO protocol?

I’d say that is pr-welcome as long as it is not the default, so we don’t break existing sites.

Thanks for the feedback!

To give you some context on the use case: our forum is a “campus forum,” and a student built a free website for other students to use. They want to use our Discourse-based campus forum to authenticate whether a user is indeed a student at our school - essentially proving “I am a legitimate student here.”

In this scenario, the external website only needs to know “this is a valid user from our campus forum” - they don’t necessarily need the user’s email, which is sensitive information that shouldn’t be shared unnecessarily.

I’ll work on a PR that adds a site setting to control this behavior, with a default that maintains backward compatibility for existing installations.

4개의 좋아요

PR 링크입니다