serializer.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295
  1. import json
  2. import typing as _t
  3. from .encoding import want_bytes
  4. from .exc import BadPayload
  5. from .exc import BadSignature
  6. from .signer import _make_keys_list
  7. from .signer import Signer
  8. _t_str_bytes = _t.Union[str, bytes]
  9. _t_opt_str_bytes = _t.Optional[_t_str_bytes]
  10. _t_kwargs = _t.Dict[str, _t.Any]
  11. _t_opt_kwargs = _t.Optional[_t_kwargs]
  12. _t_signer = _t.Type[Signer]
  13. _t_fallbacks = _t.List[_t.Union[_t_kwargs, _t.Tuple[_t_signer, _t_kwargs], _t_signer]]
  14. _t_load_unsafe = _t.Tuple[bool, _t.Any]
  15. _t_secret_key = _t.Union[_t.Iterable[_t_str_bytes], _t_str_bytes]
  16. def is_text_serializer(serializer: _t.Any) -> bool:
  17. """Checks whether a serializer generates text or binary."""
  18. return isinstance(serializer.dumps({}), str)
  19. class Serializer:
  20. """A serializer wraps a :class:`~itsdangerous.signer.Signer` to
  21. enable serializing and securely signing data other than bytes. It
  22. can unsign to verify that the data hasn't been changed.
  23. The serializer provides :meth:`dumps` and :meth:`loads`, similar to
  24. :mod:`json`, and by default uses :mod:`json` internally to serialize
  25. the data to bytes.
  26. The secret key should be a random string of ``bytes`` and should not
  27. be saved to code or version control. Different salts should be used
  28. to distinguish signing in different contexts. See :doc:`/concepts`
  29. for information about the security of the secret key and salt.
  30. :param secret_key: The secret key to sign and verify with. Can be a
  31. list of keys, oldest to newest, to support key rotation.
  32. :param salt: Extra key to combine with ``secret_key`` to distinguish
  33. signatures in different contexts.
  34. :param serializer: An object that provides ``dumps`` and ``loads``
  35. methods for serializing data to a string. Defaults to
  36. :attr:`default_serializer`, which defaults to :mod:`json`.
  37. :param serializer_kwargs: Keyword arguments to pass when calling
  38. ``serializer.dumps``.
  39. :param signer: A ``Signer`` class to instantiate when signing data.
  40. Defaults to :attr:`default_signer`, which defaults to
  41. :class:`~itsdangerous.signer.Signer`.
  42. :param signer_kwargs: Keyword arguments to pass when instantiating
  43. the ``Signer`` class.
  44. :param fallback_signers: List of signer parameters to try when
  45. unsigning with the default signer fails. Each item can be a dict
  46. of ``signer_kwargs``, a ``Signer`` class, or a tuple of
  47. ``(signer, signer_kwargs)``. Defaults to
  48. :attr:`default_fallback_signers`.
  49. .. versionchanged:: 2.0
  50. Added support for key rotation by passing a list to
  51. ``secret_key``.
  52. .. versionchanged:: 2.0
  53. Removed the default SHA-512 fallback signer from
  54. ``default_fallback_signers``.
  55. .. versionchanged:: 1.1
  56. Added support for ``fallback_signers`` and configured a default
  57. SHA-512 fallback. This fallback is for users who used the yanked
  58. 1.0.0 release which defaulted to SHA-512.
  59. .. versionchanged:: 0.14
  60. The ``signer`` and ``signer_kwargs`` parameters were added to
  61. the constructor.
  62. """
  63. #: The default serialization module to use to serialize data to a
  64. #: string internally. The default is :mod:`json`, but can be changed
  65. #: to any object that provides ``dumps`` and ``loads`` methods.
  66. default_serializer: _t.Any = json
  67. #: The default ``Signer`` class to instantiate when signing data.
  68. #: The default is :class:`itsdangerous.signer.Signer`.
  69. default_signer: _t_signer = Signer
  70. #: The default fallback signers to try when unsigning fails.
  71. default_fallback_signers: _t_fallbacks = []
  72. def __init__(
  73. self,
  74. secret_key: _t_secret_key,
  75. salt: _t_opt_str_bytes = b"itsdangerous",
  76. serializer: _t.Any = None,
  77. serializer_kwargs: _t_opt_kwargs = None,
  78. signer: _t.Optional[_t_signer] = None,
  79. signer_kwargs: _t_opt_kwargs = None,
  80. fallback_signers: _t.Optional[_t_fallbacks] = None,
  81. ):
  82. #: The list of secret keys to try for verifying signatures, from
  83. #: oldest to newest. The newest (last) key is used for signing.
  84. #:
  85. #: This allows a key rotation system to keep a list of allowed
  86. #: keys and remove expired ones.
  87. self.secret_keys: _t.List[bytes] = _make_keys_list(secret_key)
  88. if salt is not None:
  89. salt = want_bytes(salt)
  90. # if salt is None then the signer's default is used
  91. self.salt = salt
  92. if serializer is None:
  93. serializer = self.default_serializer
  94. self.serializer: _t.Any = serializer
  95. self.is_text_serializer: bool = is_text_serializer(serializer)
  96. if signer is None:
  97. signer = self.default_signer
  98. self.signer: _t_signer = signer
  99. self.signer_kwargs: _t_kwargs = signer_kwargs or {}
  100. if fallback_signers is None:
  101. fallback_signers = list(self.default_fallback_signers or ())
  102. self.fallback_signers: _t_fallbacks = fallback_signers
  103. self.serializer_kwargs: _t_kwargs = serializer_kwargs or {}
  104. @property
  105. def secret_key(self) -> bytes:
  106. """The newest (last) entry in the :attr:`secret_keys` list. This
  107. is for compatibility from before key rotation support was added.
  108. """
  109. return self.secret_keys[-1]
  110. def load_payload(
  111. self, payload: bytes, serializer: _t.Optional[_t.Any] = None
  112. ) -> _t.Any:
  113. """Loads the encoded object. This function raises
  114. :class:`.BadPayload` if the payload is not valid. The
  115. ``serializer`` parameter can be used to override the serializer
  116. stored on the class. The encoded ``payload`` should always be
  117. bytes.
  118. """
  119. if serializer is None:
  120. serializer = self.serializer
  121. is_text = self.is_text_serializer
  122. else:
  123. is_text = is_text_serializer(serializer)
  124. try:
  125. if is_text:
  126. return serializer.loads(payload.decode("utf-8"))
  127. return serializer.loads(payload)
  128. except Exception as e:
  129. raise BadPayload(
  130. "Could not load the payload because an exception"
  131. " occurred on unserializing the data.",
  132. original_error=e,
  133. )
  134. def dump_payload(self, obj: _t.Any) -> bytes:
  135. """Dumps the encoded object. The return value is always bytes.
  136. If the internal serializer returns text, the value will be
  137. encoded as UTF-8.
  138. """
  139. return want_bytes(self.serializer.dumps(obj, **self.serializer_kwargs))
  140. def make_signer(self, salt: _t_opt_str_bytes = None) -> Signer:
  141. """Creates a new instance of the signer to be used. The default
  142. implementation uses the :class:`.Signer` base class.
  143. """
  144. if salt is None:
  145. salt = self.salt
  146. return self.signer(self.secret_keys, salt=salt, **self.signer_kwargs)
  147. def iter_unsigners(self, salt: _t_opt_str_bytes = None) -> _t.Iterator[Signer]:
  148. """Iterates over all signers to be tried for unsigning. Starts
  149. with the configured signer, then constructs each signer
  150. specified in ``fallback_signers``.
  151. """
  152. if salt is None:
  153. salt = self.salt
  154. yield self.make_signer(salt)
  155. for fallback in self.fallback_signers:
  156. if isinstance(fallback, dict):
  157. kwargs = fallback
  158. fallback = self.signer
  159. elif isinstance(fallback, tuple):
  160. fallback, kwargs = fallback
  161. else:
  162. kwargs = self.signer_kwargs
  163. for secret_key in self.secret_keys:
  164. yield fallback(secret_key, salt=salt, **kwargs)
  165. def dumps(self, obj: _t.Any, salt: _t_opt_str_bytes = None) -> _t_str_bytes:
  166. """Returns a signed string serialized with the internal
  167. serializer. The return value can be either a byte or unicode
  168. string depending on the format of the internal serializer.
  169. """
  170. payload = want_bytes(self.dump_payload(obj))
  171. rv = self.make_signer(salt).sign(payload)
  172. if self.is_text_serializer:
  173. return rv.decode("utf-8")
  174. return rv
  175. def dump(self, obj: _t.Any, f: _t.IO, salt: _t_opt_str_bytes = None) -> None:
  176. """Like :meth:`dumps` but dumps into a file. The file handle has
  177. to be compatible with what the internal serializer expects.
  178. """
  179. f.write(self.dumps(obj, salt))
  180. def loads(
  181. self, s: _t_str_bytes, salt: _t_opt_str_bytes = None, **kwargs: _t.Any
  182. ) -> _t.Any:
  183. """Reverse of :meth:`dumps`. Raises :exc:`.BadSignature` if the
  184. signature validation fails.
  185. """
  186. s = want_bytes(s)
  187. last_exception = None
  188. for signer in self.iter_unsigners(salt):
  189. try:
  190. return self.load_payload(signer.unsign(s))
  191. except BadSignature as err:
  192. last_exception = err
  193. raise _t.cast(BadSignature, last_exception)
  194. def load(self, f: _t.IO, salt: _t_opt_str_bytes = None) -> _t.Any:
  195. """Like :meth:`loads` but loads from a file."""
  196. return self.loads(f.read(), salt)
  197. def loads_unsafe(
  198. self, s: _t_str_bytes, salt: _t_opt_str_bytes = None
  199. ) -> _t_load_unsafe:
  200. """Like :meth:`loads` but without verifying the signature. This
  201. is potentially very dangerous to use depending on how your
  202. serializer works. The return value is ``(signature_valid,
  203. payload)`` instead of just the payload. The first item will be a
  204. boolean that indicates if the signature is valid. This function
  205. never fails.
  206. Use it for debugging only and if you know that your serializer
  207. module is not exploitable (for example, do not use it with a
  208. pickle serializer).
  209. .. versionadded:: 0.15
  210. """
  211. return self._loads_unsafe_impl(s, salt)
  212. def _loads_unsafe_impl(
  213. self,
  214. s: _t_str_bytes,
  215. salt: _t_opt_str_bytes,
  216. load_kwargs: _t_opt_kwargs = None,
  217. load_payload_kwargs: _t_opt_kwargs = None,
  218. ) -> _t_load_unsafe:
  219. """Low level helper function to implement :meth:`loads_unsafe`
  220. in serializer subclasses.
  221. """
  222. if load_kwargs is None:
  223. load_kwargs = {}
  224. try:
  225. return True, self.loads(s, salt=salt, **load_kwargs)
  226. except BadSignature as e:
  227. if e.payload is None:
  228. return False, None
  229. if load_payload_kwargs is None:
  230. load_payload_kwargs = {}
  231. try:
  232. return (
  233. False,
  234. self.load_payload(e.payload, **load_payload_kwargs),
  235. )
  236. except BadPayload:
  237. return False, None
  238. def load_unsafe(self, f: _t.IO, salt: _t_opt_str_bytes = None) -> _t_load_unsafe:
  239. """Like :meth:`loads_unsafe` but loads from a file.
  240. .. versionadded:: 0.15
  241. """
  242. return self.loads_unsafe(f.read(), salt=salt)