Przeglądaj źródła

fix(docs): update confirmation code security considerations

Peter Thomassen 5 lat temu
rodzic
commit
49e3a65ae7
1 zmienionych plików z 31 dodań i 27 usunięć
  1. 31 27
      docs/authentication.rst

+ 31 - 27
docs/authentication.rst

@@ -9,7 +9,7 @@ users only. Users can register an account free of charge through the API as
 described below.
 
 Obtain a Captcha
-```````````````````
+````````````````
 
 Before registering a user account, you need to solve a captcha. You will have
 to send the captcha ID and solution along with your registration request. To
@@ -303,35 +303,39 @@ Confirmation Codes
     user's email address. Although clients generally should consider these
     codes opaque, we would like to give some insights into how they work.
 
-    The code is a base64-encoded JSON representation of the user's intent.
-    The representation carries a timestamp of when the intent was expressed,
-    the user ID, and also any extra parameters that were submitted along with
-    the intent. An example of such a parameter is the new email address in the
-    context of a `change email address`_ operation. Parameters that are
-    unknown at the time when the code is generated are not included in the
-    code and must be provided via ``POST`` request payload when using the
-    code. A typical example of this is the new password in a `password reset`_
+    The code is a base64-encoded encrypted-then-signed JSON representation of
+    the user's intent. Encryption/decryption and authentication (sign/verify)
+    is handled by `pyca/cryptography's Fernet implementation
+    <https://cryptography.io/en/latest/fernet/>`_ which is uses AES-CBC and
+    HMAC-SHA256 with specifically derived key material. The HMAC also signs the
+    current time (i.e. when the intent was expressed). During verification,
+    codes are checked for freshness and rejected when older than allowed.
+
+    The encoded intent is composed of the user ID and any extra parameters that
+    were submitted along with the intent. An example of such a parameter is the
+    new email address in the context of a `change email address`_ operation.
+    Parameters that are unknown at code generation time are not included in the
+    code and must be provided via ``POST`` request payload when using the code.
+    A typical example of this is the new password in a `password reset`_
     operation, as it is only provided when the code is being used (and not at
     the time when the code is requested).
 
-    To ensure integrity, we also include a message authentication code (MAC)
-    using `Django's signature implementation
-    <https://docs.djangoproject.com/en/2.2/_modules/django/core/signing/#Signer>`_.
-    When a confirmation code is used, we recompute the MAC based on the data
-    incorporated in the code, and only perform the requested action if the MAC
-    is reproduced identically. Codes are also checked for freshness using the
-    timestamp, and rejected if older than allowed.
-
-    In order to prevent race conditions, we add additional data to the MAC
-    input such that codes are only valid as long as the user state is not
-    modified (e.g. by performing another sensitive account operation). This is
-    achieved by mixing a) the account operation type (e.g. password reset), b)
-    the account's activation status, c) the account's current email address,
-    and d) the user's password hash into the MAC input. If any of these
-    parameters happens to change before a code is applied, the MAC will be
+    In order to prevent race conditions, we augment the code with additional
+    data which we use to invalidate codes when the user state is modified (e.g.
+    by performing another sensitive account operation). This is achieved by
+    including the combined hash of a) the account operation type (e.g. password
+    reset), b) the account's activation status, c) the account's current email
+    address, and d) the user's password hash. When a confirmation code is used,
+    we recompute this hash based on the user's current state, and only perform
+    the requested action if the hash is reproduced identically. If any of these
+    parameters happens to change before a code is applied, the code will be
     rendered invalid, and the operation will fail. This measure blocks
     scenarios such as using an old email address change code after a more
-    recent password change.
+    recent password change. (Note that it is sometimes possible to revert the
+    state so that an old code becomes valid again, such as when you change the
+    email address twice, with the second change undoing the first one. This
+    issue does not occur for password changes; those do permanently invalidate
+    other codes.)
 
     This approach allows us to securely authenticate sensitive user operations
     without keeping a list of requested operations on the server. This is both
@@ -450,8 +454,8 @@ Note that, for now, all tokens have equal power -- every token can authorize
 any action. We are planning to implement scoped tokens in the future.
 
 
-Token Security Considerations
-`````````````````````````````
+Security Considerations
+```````````````````````
 
 This section is for information only. Token length and encoding may change in
 the future.