소스 검색

feat(api): switch to 164 bit tokens with base58 encoding

Peter Thomassen 2 년 전
부모
커밋
0aed954842
2개의 변경된 파일32개의 추가작업 그리고 12개의 파일을 삭제
  1. 7 1
      api/desecapi/models/tokens.py
  2. 25 11
      docs/auth/tokens.rst

+ 7 - 1
api/desecapi/models/tokens.py

@@ -18,6 +18,11 @@ from django_prometheus.models import ExportModelOperationsMixin
 from netfields import CidrAddressField, NetManager
 from netfields import CidrAddressField, NetManager
 
 
 
 
+# No 0OIl characters, non-alphanumeric only (select by double-click no line-break)
+# https://github.com/bitcoin/bitcoin/blob/master/src/base58.h
+ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
+
+
 class Token(ExportModelOperationsMixin("Token"), rest_framework.authtoken.models.Token):
 class Token(ExportModelOperationsMixin("Token"), rest_framework.authtoken.models.Token):
     @staticmethod
     @staticmethod
     def _allowed_subnets_default():
     def _allowed_subnets_default():
@@ -73,7 +78,8 @@ class Token(ExportModelOperationsMixin("Token"), rest_framework.authtoken.models
         return True
         return True
 
 
     def generate_key(self):
     def generate_key(self):
-        self.plain = secrets.token_urlsafe(21)
+        # Entropy: len(ALPHABET) == 58, log_2(58) * 28 = 164.02
+        self.plain = "".join(secrets.choice(ALPHABET) for _ in range(28))
         self.key = Token.make_hash(self.plain)
         self.key = Token.make_hash(self.plain)
         return self.key
         return self.key
 
 

+ 25 - 11
docs/auth/tokens.rst

@@ -396,14 +396,28 @@ Security Considerations
 This section is for purely informational. Token length and encoding may change
 This section is for purely informational. Token length and encoding may change
 in the future.
 in the future.
 
 
-Any token secret is generated from 168 bits of randomness at the server and stored in
-hashed format (PBKDF2-HMAC-SHA256). Guessing the secret correctly or reversing
-the hash is hence practically impossible.
-
-The token's secret value is represented by 28 characters using a URL-safe variant of
-base64 encoding. It comprises only the characters ``A-Z``, ``a-z``, ``0-9``, ``-``,
-and ``_``. (Base64 padding is not needed as the string length is a multiple of 4.)
-
-Old versions of the API encoded 20-byte token secrets in 40 characters with hexadecimal
-representation. Such tokens are not issued anymore, but remain valid until
-invalidated by the user.
+Any token secret is generated from 164 bits of randomness at the server and
+stored in hashed format (PBKDF2-HMAC-SHA256).
+Guessing the secret correctly or reversing the hash is considered practically
+impossible.
+
+The token's secret value is represented by 28 characters using a URL-safe
+base58 encoding.
+It is based on a case-sensitive alphanumeric alphabet excluding the characters
+``lIO0`` (hence comprising only the symbols ``a-k``, ``m-z``, ``A-H``,
+``J-N``, ``P-Z``, and ``1-9``).
+This encoding is optimized for maximum clarity and usability:
+Exclusion of certain letters minimizes visual ambiguity, while the restriction
+to alphanumeric symbols allows easy selection (double-click) and input, and
+helps avoid line breaks during display.
+
+Before December 2022, tokens encoded at 21-byte secret using 28 characters in
+a URL-safe variant of base64 encoding, comprising only of the characters
+``A-Z``, ``a-z``, ``0-9``, ``-``, and ``_``.
+(Base64 padding was not needed as the string length is a multiple of 4.)
+
+Before September 2018, tokens encoded a 20-byte secret using 40 hexadecimal
+characters.
+
+Legacy tokens are not issued anymore, but remain valid until invalidated by
+the user.