diff --git a/docs/backends/facebook_limited_login.rst b/docs/backends/facebook_limited_login.rst index c50f0c00..3a73a3d1 100644 --- a/docs/backends/facebook_limited_login.rst +++ b/docs/backends/facebook_limited_login.rst @@ -57,6 +57,21 @@ Enable the auth backend: ... ) +Security requirements +--------------------- + +The client supplies the signed ID token in the ``access_token`` parameter. +The backend verifies the provider signature and applicable issuer, audience, +subject, and time claims. It intentionally does not enforce the token's nonce, +because the backend did not generate it server-side. + +Signature validation alone does not bind this token to an authentication +initiated by the receiving browser or make it single-use. Applications requiring +login-CSRF or account-linking replay protection must add an application-level +initiation and single-use binding. Preserve the initiating authenticated local +user through linking; do not treat an arbitrary client nonce as proof of that +binding. See :doc:`../security` for shared deployment requirements. + .. _Facebook App Creation: https://developers.facebook.com/apps/creation/ .. _Facebook Limited Login: https://developers.facebook.com/docs/facebook-login/limited-login/ .. _hosts file: https://en.wikipedia.org/wiki/Hosts_(file) diff --git a/docs/backends/openid.rst b/docs/backends/openid.rst index 221a56aa..3139795b 100644 --- a/docs/backends/openid.rst +++ b/docs/backends/openid.rst @@ -28,9 +28,15 @@ does not apply. Session and provider binding ---------------------------- -All OpenID backends require a positive assertion to match backend-scoped -discovery state from the browser session that initiated authentication. When -the framework supports restoring sessions, only a session identifier carried +All OpenID backends require a positive assertion whose provider-signed return +URL carries a session-held, one-use transaction value. The verified response +must also match backend-scoped discovery state from the browser session that +initiated authentication. A second OpenID start in the same session replaces +the first transaction; the earlier callback is rejected. Unsolicited assertions +are rejected, including for anonymous users. Always use the normal backend +initiation path. + +When the framework supports restoring sessions, only a session identifier carried by the provider-signed ``openid.return_to`` can select that session. Missing or mismatched state requires the user to restart authentication. @@ -49,6 +55,28 @@ setting to disable this validation. Custom strategies that implement session restoration must restore the complete OpenID session state and update session-derived pipeline arguments, including the authenticated user. +Custom backends overriding authorization URL or HTML generation must include +the transaction value in the provider-signed return URL, for example by calling +``_return_to_for_login()``. + +Upgrading binding requirements +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Authentication attempts initiated before upgrading to session, transaction, or +fixed-provider binding must restart. Deployments upgrading from a release +without fixed-provider binding should review associations created or migrated +from unexpected identity URLs, especially Ubuntu, openSUSE, and Yandex rows +migrated from nickname or email identifiers. Confirm ownership through recovery +or authenticated relinking; those records cannot be repaired automatically. + +Setting ``SOCIAL_AUTH_ALLOW_UNVERIFIED_LEGACY_UID_MIGRATION = False`` limits +migration risk but does not replace provider binding. Disable affected backends +until the fixed release is installed. See +:doc:`../configuration/identifier-migration` for evidence and recovery policy. + +Extra data +---------- + OpenID_ backends can store extra data in ``UserSocialAuth.extra_data`` field by defining a set of values names to retrieve from any of the used schemas, AttributeExchange and SimpleRegistration. As their keywords differ we need diff --git a/docs/backends/saml.rst b/docs/backends/saml.rst index dc5830ea..a51ec838 100644 --- a/docs/backends/saml.rst +++ b/docs/backends/saml.rst @@ -308,6 +308,8 @@ being sent with that request. ``social-auth-app-django`` preserves the server-side session identifier in SAML ``RelayState`` and restores the session before validating the response. +The restored session key is rotated before continued use; do not override +restoration in a way that retains the old key or changes the initiating user. Use a current ``social-auth-app-django`` release and a server-side Django session engine. In multi-instance deployments, every instance that can handle the login or callback must use the same session storage. Database and cached diff --git a/docs/backends/telegram.rst b/docs/backends/telegram.rst index 06d8fb4d..ed245443 100644 --- a/docs/backends/telegram.rst +++ b/docs/backends/telegram.rst @@ -58,4 +58,17 @@ Telegram uses a widget-based authentication method for login. - Last name - Photo URL (if available) +Security requirements +--------------------- + +The callback HMAC authenticates the Telegram data, and the backend rejects data +more than 24 hours old. It does not bind the callback to a login initiated by +the receiving browser or make it single-use. A valid signed callback can be +replayed within its accepted age. + +Applications requiring login-CSRF or account-linking replay protection must add +an application-controlled initiation and single-use binding before completing +the login or association. Preserve the initiating authenticated local user +through account linking. Serve the application and widget callback over HTTPS. + .. _BotFather: https://t.me/botfather diff --git a/docs/configuration/django.rst b/docs/configuration/django.rst index 1b9f7a10..d71250aa 100644 --- a/docs/configuration/django.rst +++ b/docs/configuration/django.rst @@ -542,7 +542,23 @@ Starting Login -------------- The ``social:begin`` view requires a ``POST`` request. Use a form and include -the CSRF token in templates that start authentication. +the CSRF token in templates that start authentication. The disconnection view +likewise requires an authenticated, CSRF-protected POST. + +Provider completion callbacks are intentionally exempt from Django's CSRF token +check and rely on the selected core backend's protocol validation. Legacy +email and username login forms have no provider callback proof: submit them to +``social:complete`` using a CSRF-protected POST instead. Do not exempt these +forms or expose an alternate completion route bypassing validation. + +During authenticated account linking, preserve the initiating local user across +the callback; reject or restart a flow if the principal changes. For externally +resumed partials, preserve the explicit confirmation and nonce checks described +under :ref:`partial-pipeline`. + +Restorable cross-site flows require shared server-side sessions and the same +browser context. See :doc:`../backends/saml` for supported session arrangements, +restoration, and upgrade troubleshooting. Templates @@ -1206,8 +1222,12 @@ from ``social_django.constants`` or equivalent string literals: Endpoint Behaviors and Security Architecture ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Both endpoints employ a layered defense model to protect users and your -application: +Both endpoints preserve issuer, redirect, framing, and CSRF checks. Fetch +Metadata headers influence navigation checks and automatic submission; they +are optional browser context signals, not credentials. Missing headers do not +remove the remaining checks. Keep these controls when overriding templates. +Bridge target validation remains active even if general +``SOCIAL_AUTH_SANITIZE_REDIRECTS`` validation is disabled. IdP Launch (``idp_launch``) ~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -1215,7 +1235,7 @@ IdP Launch (``idp_launch``) Route: ``/idp-launch//`` (URL name: ``social:idp_launch``) Designed to handle `OIDC IdP-initiated login per the OpenID Connect Core -specification `_. Enforces 8 security layers: +specification `_. Its behavior is: 1. **Opt-In Gate**: Raises ``Http404`` if ``LaunchBridge.IDP`` (or ``'idp_launch'``) is not present in ``SOCIAL_AUTH_ENABLE_LAUNCH_BRIDGES``. @@ -1247,7 +1267,8 @@ App Launch (``app_launch``) Route: ``/app-launch//`` (URL name: ``social:app_launch``) -Designed for same-origin frontend or SPA redirects with OpenID Connect (OIDC) backends. Enforces 10 security layers: +Designed for same-origin frontend or SPA redirects with OpenID Connect (OIDC) +backends. Its behavior is: 1. **Opt-In Gate**: Raises ``Http404`` if ``LaunchBridge.APP`` (or ``'app_launch'``) is not present in ``SOCIAL_AUTH_ENABLE_LAUNCH_BRIDGES``. @@ -1333,10 +1354,13 @@ For example, to allow a frontend served on port 8443, include Customizing the Launch Template (``launch.html``) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Both bridge views render ``social_django/launch.html``. To customize the -appearance, loading animation, or branding during the transition, create a -file named ``social_django/launch.html`` within your project's ``templates`` -directory (ensuring your template loader prioritizes project templates). +Both bridge views render ``social_django/launch.html``. Overrides must retain +the CSRF token, forward only the supplied parameters, and honor ``auto_submit``: +when false, show a manual confirmation button instead of submitting the form +automatically. To customize the appearance, loading animation, or branding +during the transition, create ``social_django/launch.html`` within your +project's ``templates`` directory (ensuring your template loader prioritizes +project templates). Template Context Variables ~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/docs/configuration/settings.rst b/docs/configuration/settings.rst index 7c8c1b19..e8b2efe9 100644 --- a/docs/configuration/settings.rst +++ b/docs/configuration/settings.rst @@ -362,6 +362,8 @@ For OAuth backends:: ] +.. _request-redirect-settings: + Processing requests and redirects --------------------------------- @@ -384,6 +386,16 @@ allow some tweaks to the behavior of these. ``next`` GET argument if the hostname is on the list, otherwise it defaults to the value of ``SOCIAL_AUTH_LOGIN_REDIRECT_URL``. +``SOCIAL_AUTH_ALLOWED_REDIRECT_SCHEMES = ['http', 'https']`` + Select the schemes accepted for request-derived completion redirects. + HTTP(S) URLs must also pass the host allowlist. For a native application, + explicitly add its private-use scheme, for example + ``['http', 'https', 'com.example.app']``. Private-use destinations are + authorized by scheme alone; their host-like component is not checked against + ``ALLOWED_REDIRECT_HOSTS``. Approve only schemes controlled by the intended + application. These checks do not provide general URL safety or SSRF + protection. Backend-specific settings take precedence over global values. + ``SOCIAL_AUTH_REDIRECT_IS_HTTPS = False`` On projects behind a reverse proxy that uses HTTPS, the redirect URIs can have the wrong schema (``http://`` instead of ``https://``) if @@ -470,14 +482,21 @@ Miscellaneous settings This setting does not rename arguments passed by ``create_user``; required creation fields must be supplied separately. The default mapping is empty. -``SOCIAL_AUTH_PROTECTED_USER_FIELDS = ['email',]`` - During the pipeline process a ``dict`` named ``details`` will be populated - with the needed values to create the user instance, but it's also used to - update the user instance. Any value in it will be checked as an attribute - in the user instance (first by doing ``hasattr(user, name)``). Usually - there are attributes that cannot be updated (like ``username``, ``id``, - ``email``, etc.), those fields need to be *protect*. Set any field name that - requires *protection* in this setting, and it won't be updated. +``SOCIAL_AUTH_PROTECTED_USER_FIELDS = []`` + Add local field names that ``user_details`` must not update from provider + profile data. By default, ``username``, ``id``, ``pk``, ``email``, + ``password``, ``is_active``, ``is_staff``, and ``is_superuser`` are already + protected. Configured fields supplement that list. Protection applies to + the destination field after ``USER_FIELD_MAPPING`` is resolved; it governs + updates, not which fields are passed to initial user creation. + +``SOCIAL_AUTH_NO_DEFAULT_PROTECTED_USER_FIELDS = False`` + Explicit ``True`` removes the default protected fields; any fields listed + in ``PROTECTED_USER_FIELDS`` remain protected. This transfers responsibility + for identity and authorization-sensitive updates to the application. + Preserve equivalent protection before allowing provider data to update + local account identifiers, credentials, activity, or privilege fields. + Both settings support backend-specific overrides. ``SOCIAL_AUTH_IMMUTABLE_USER_FIELDS = ['email',]`` diff --git a/docs/pipeline.rst b/docs/pipeline.rst index 30b73ea5..2fa315c7 100644 --- a/docs/pipeline.rst +++ b/docs/pipeline.rst @@ -81,6 +81,30 @@ arguments that may be passed from other pipeline functions or future versions:: pass +.. _pipeline-security: + +Pipeline security +----------------- + +Custom pipeline steps execute with application authority. Preserve protocol +validation, identity selection, policy checks, and association ordering. Keep +``auth_allowed`` before user creation or linking. Request and provider values +retain their provenance when saved in sessions, associations, or partials; +revalidate them before using them for a new security decision. Confirmed resume +parameters are effective request data, not independently trusted identity claims. + +Provider email and profile fields do not establish local authorization or +account ownership by themselves. ``associate_by_email`` is disabled by default; +enable it only with an explicit verified-email and account-recovery policy. +Review protected local fields in :doc:`configuration/settings` and partial +ownership and confirmation requirements under :ref:`partial-pipeline`. + +The optional ``social_core.pipeline.debug.debug`` step prints the provider +response, details, positional arguments, and pipeline keyword arguments to +standard output. These may include tokens, validation codes, personal data, +and other secrets. Use it only with controlled development data; do not enable +it in production. Custom diagnostics must avoid logging raw authentication data. + Authentication Pipeline ----------------------- @@ -312,6 +336,8 @@ Backend specific disconnection pipelines can also be defined with a setting such ``SOCIAL_AUTH_TWITTER_DISCONNECT_PIPELINE``. +.. _partial-pipeline: + Partial Pipeline ---------------- diff --git a/docs/security.rst b/docs/security.rst index 1a0b1008..688a75a6 100644 --- a/docs/security.rst +++ b/docs/security.rst @@ -2,70 +2,71 @@ Security considerations ======================= The canonical `social-auth-core threat model`_ and -`social-auth-app-django threat model`_ document the packages' respective trust -boundaries, security properties, downstream responsibilities, and vulnerability -triage rules. This page provides complementary deployment and configuration -guidance. +`social-auth-app-django threat model`_ define each package's trust boundaries, +security guarantees, exceptions, and vulnerability triage rules. This page +provides deployment guidance; detailed configuration and upgrade instructions +live on the linked topic pages. + +Trusted configuration and extensions +------------------------------------ + +Settings, provider endpoints, issuers, signing keys, certificates, algorithms, +proxies, and redirect destinations must come from trusted operator +configuration. Do not let request parameters choose these security policies. +Keep required TLS and protocol validation enabled and use intended HTTPS +provider endpoints. Certificate verification does not protect a cleartext HTTP +endpoint. Retain finite outbound request timeouts. + +Custom backends, pipelines, strategies, storage, middleware, and templates run +with application authority. Preserve validation, policy, and association +ordering, and review extensions as security-sensitive code. See +:doc:`backends/implementation`, :doc:`pipeline`, :doc:`strategies`, and +:doc:`storage`. + +Provider profile fields are not automatically verified ownership or local +authorization facts. Decide which claims may influence account creation, +updates, groups, email association, and recovery. Escape profile data at output +sinks. Do not enable ``associate_by_email`` without an explicit verified-email +and recovery policy. See :ref:`pipeline-security` and :doc:`groups`. Stable social account identifiers --------------------------------- -Social authentication associations are authorization bindings. Their ``uid`` -must therefore come from a provider identifier that is immutable and cannot be -reassigned, rather than a display name, email address, UPN, or other -human-readable login name. - -Bundled backends use stable provider identifiers where available, including -OIDC ``sub`` claims, provider account IDs, UUIDs, and verified OpenID identity -URLs. Applications overriding a backend's ``ID_KEY`` are responsible for -ensuring the selected claim has the same stability properties. Backends whose -provider responses expose no stable identifier, including Drip, Last.fm, and -Mixcloud, are association-only and require an authenticated local user. - -When upgrading an existing deployment, read -:doc:`configuration/identifier-migration` before authenticating users. Candidate -lookup uses historical keys and values; authorization to migrate requires -matching stored current-identifier evidence. Conflicting evidence always blocks -migration. - -Only audited historical transitions that lacked evidence retain default -unverified migration. This compatibility allowance accepts a first-login race -through mutable identifiers. Security-sensitive deployments should set -``SOCIAL_AUTH_ALLOW_UNVERIFIED_LEGACY_UID_MIGRATION = False`` and use recovery -or authenticated linking where evidence is unavailable. Explicit ``True`` is an -unsafe escape hatch, not a verification mechanism. Configuration-driven changes -and custom backends require evidence by default. - - -Legacy OpenID session and provider binding ------------------------------------------- - -OpenID backends accept positive assertions only when their provider-signed -return URL carries a session-held, one-use transaction value and the verified -response matches backend-scoped discovery state from the initiating browser -session or a session selected by the signed return URL. A second OpenID -start in the same session replaces the first transaction. The first callback -is rejected, and the user must begin authentication again. Unsolicited assertions -are rejected, including for anonymous users. Do not expose an -alternate callback path that bypasses the backend's normal authentication -start. The generic OpenID backend still accepts a provider selected by the -application user and should be exposed only when that behavior and its outbound -discovery traffic are appropriate for the deployment. Fixed-provider backends -additionally require the exact HTTPS provider endpoint discovered during -initiation. Custom OpenID backends overriding authorization URL or HTML -generation must include the transaction value in the provider-signed return -URL, for example by calling ``_return_to_for_login()``. - -Deployments upgrading from a release without fixed-provider binding should -review OpenID associations created or migrated from unexpected identity URLs, -especially Ubuntu, openSUSE, and Yandex associations migrated from historical -nickname or email identifiers. Such records cannot be repaired automatically; -confirm ownership through account recovery or authenticated relinking. Setting -``SOCIAL_AUTH_ALLOW_UNVERIFIED_LEGACY_UID_MIGRATION`` to ``False`` limits -migration risk but -does not replace provider binding, so affected backends should be disabled -until the fixed release is installed. Authentication attempts initiated before -upgrading to either binding requirement must be restarted. +Social associations are authorization bindings. Use stable, provider-scoped +identifiers rather than mutable email addresses, usernames, UPNs, or display +names. Applications overriding ``ID_KEY`` must preserve those properties. +Drip, Last.fm, and Mixcloud expose no suitable stable identifier and are +association-only; they require an authenticated local user. + +Read :doc:`configuration/identifier-migration` before upgrading or changing +identifier configuration. It documents evidence requirements, audited +compatibility exceptions, their first-login race, configuration provenance, +and recovery. A missing-evidence compatibility allowance does not establish +continuous ownership. For transferable EVE characters, also read +:doc:`backends/eveonline`. + +Sessions and protected transitions +---------------------------------- + +Use a framework integration supplying isolated sessions, authenticated-user +state, and CSRF protection for local state changes. Preserve the initiating +user through account-linking callbacks; reject or restart a flow if that user +changes. Keep tokens, sessions, associations, codes, nonces, partials, and +backups confidential and protected from modification. The packages do not +provide encryption at rest. + +Provider callbacks rely on protocol validation. State, nonce, PKCE, and request +correlation solve different problems; none replaces the others. Some signed +flows do not supply initiation, session binding, or single-use protection. +Review the selected backend's limits, especially :doc:`backends/telegram`, +:doc:`backends/facebook_limited_login`, and legacy iframe flows in +:doc:`backends/vk` and :doc:`backends/odnoklassnikiru`. + +For Django, see :doc:`configuration/django` for protected routes and launch +bridges, :doc:`backends/saml` for cross-site session restoration, and +:ref:`partial-pipeline` for confirmation and template requirements. + +.. _oauth2-transaction-state: OAuth2 transaction state ------------------------ @@ -78,88 +79,36 @@ OAuth2 authorizations begun before upgrading to this behavior also must restart. ``SOCIAL_AUTH_STATE_LIFETIME`` sets the maximum age in seconds, defaulting to 1800. A provider-specific ``SOCIAL_AUTH__STATE_LIFETIME`` takes -precedence. The value must be a positive integer. Framework storage integrations -must implement ``NonceMixin.consume()`` as an atomic conditional claim shared -by all application workers; a session read followed by a write cannot prevent -two simultaneous callbacks from using the same state. - - -Host header validation ----------------------- - -The library may use the incoming HTTP ``Host`` header when generating absolute URLs -or redirects during the authentication and authorization flow. If the ``Host`` -header is not validated by the deployment stack, it may allow host header -injection attacks. - -This is a deployment and configuration concern rather than a defect in the -library itself. The behavior is intentional, as the library needs to construct -absolute URLs for OAuth callbacks and redirects. Proper upstream validation is -required to ensure that only legitimate ``Host`` header values are accepted by your -application. - - -Reverse proxy configuration ----------------------------- - -When deploying behind a reverse proxy (such as nginx, Apache, HAProxy, or a -cloud load balancer), the proxy must validate the Host header before forwarding -requests to the application. - -Key requirements: - -* **Validate the Host header**: Only expected hostnames should be forwarded - upstream to the application. Requests with unexpected or malicious Host - values should be rejected by the proxy. - -* **Forwarded headers**: If your deployment uses forwarded headers such as - ``X-Forwarded-Host`` or the standard ``Forwarded`` header: - - * These headers must be accepted **only from trusted proxies**. - * They must **not** be blindly trusted from direct client requests. - * They must be configured explicitly in the proxy configuration. - -.. note:: - - Configuration syntax varies across reverse - proxy implementations. Consult your proxy's documentation for Host header - validation and forwarded header handling. - - -Django configuration --------------------- - -When using Python Social Auth with Django, proper Host header validation must -be configured at the application level using Django's built-in security -features. - -Key requirements: - -* **Configure ALLOWED_HOSTS**: The ``ALLOWED_HOSTS`` setting must be explicitly - configured with the canonical hostname(s) for your application. For example:: - - ALLOWED_HOSTS = ['example.com', 'www.example.com'] - -* **Never use wildcard in production**: The wildcard value ``"*"`` must not be - used in production environments, as it disables Host header validation - entirely. - -* **Host validation behind proxies**: Host validation must remain enabled even - when the application is deployed behind a reverse proxy. Do not disable - ``ALLOWED_HOSTS`` validation based on the assumption that the proxy will - handle it. - -* **Forwarded header settings**: If your deployment uses forwarded headers, - configure Django's ``USE_X_FORWARDED_HOST`` setting carefully. This setting - should only be enabled when: - - * The application is behind a trusted reverse proxy. - * The proxy is properly configured to set or strip forwarded headers. - * The proxy prevents clients from sending malicious forwarded headers - directly. - -For more information on Django security settings, refer to the `Django security -documentation`_. +precedence. The value must be a positive integer. Storage integrations must +provide an atomic claim shared across workers; see :ref:`nonce-storage` for +``NonceMixin.consume()`` and :ref:`authentication-storage-cleanup` for Django +cleanup and coordinated upgrade requirements. + +For legacy OpenID transaction, discovery, and provider binding, including +upgrade precautions, see :doc:`backends/openid`. + +Host headers, redirects, and outbound access +-------------------------------------------- + +Callback and absolute redirect URLs can be derived from the request host. +Validate ``Host`` against intended application hosts, and accept forwarded +headers only from trusted proxies that set or strip client-supplied values. +Use HTTPS and canonical callback URLs. With Django, configure restrictive +``ALLOWED_HOSTS`` and enable ``USE_X_FORWARDED_HOST`` only with an appropriately +configured trusted proxy. See `Django security documentation`_. + +Keep redirect sanitization enabled and restrict approved hosts and schemes. +Explicit private-use schemes authorize by scheme, without host validation. +Redirect checks are not SSRF protection or a general egress policy; see +:ref:`request-redirect-settings`. Generic OpenID deliberately supports +user-selected provider discovery; expose it only if appropriate for the +application and its network policy. + +Apply request-size, rate, concurrency, capacity, and retention controls suitable +for the deployment. The packages supply no quantitative resource guarantee. +Constrain provider/discovery egress where required and review access to any +configured credential files. Avoid production debug-pipeline output and +unsafe logging of authentication data; see :ref:`pipeline-security`. .. _Django security documentation: https://docs.djangoproject.com/en/stable/topics/security/ .. _social-auth-app-django threat model: https://github.com/python-social-auth/social-app-django/blob/master/THREAT_MODEL.md diff --git a/docs/storage.rst b/docs/storage.rst index 2d66253a..6a8f637a 100644 --- a/docs/storage.rst +++ b/docs/storage.rst @@ -116,6 +116,8 @@ must accept the new keyword-only argument before upgrading social-core. raise NotImplementedError('Implement in subclass') +.. _nonce-storage: + Nonce ----- @@ -149,6 +151,8 @@ the needed methods:: ``consume()`` must be a single atomic operation across application workers. OAuth2 uses it to reject simultaneous callbacks for the same transaction. +A session read followed by a write is insufficient across workers. See +:ref:`oauth2-transaction-state` for transaction behavior and lifetime settings. Persistent stores should periodically remove abandoned OAuth2 claims; the Django integration uses :ref:`authentication-storage-cleanup` for this.