forked from orbit-oss/flask
secret key rotation: fix key list ordering
The `itsdangerous` serializer interface[1] expects keys to be provided with the oldest key at index zero and the active signing key at the end of the list. We document[2] that `SECRET_KEY_FALLBACKS` should be configured with the most recent first (at index zero), so to achieve the expected behaviour, those should be inserted in reverse-order at the head of the list. [1] - https://itsdangerous.palletsprojects.com/en/stable/serializer/#itsdangerous.serializer.Serializer [2] - https://flask.palletsprojects.com/en/stable/config/#SECRET_KEY_FALLBACKS
This commit is contained in:
parent
941efd4a36
commit
fb54159861
3 changed files with 15 additions and 5 deletions
|
|
@ -3,6 +3,8 @@ Version 3.1.1
|
||||||
|
|
||||||
Unreleased
|
Unreleased
|
||||||
|
|
||||||
|
- Fix signing key selection order when key rotation is enabled via
|
||||||
|
``SECRET_KEY_FALLBACKS``. :ghsa:`4grg-w6v8-c28g`
|
||||||
- Fix type hint for `cli_runner.invoke`. :issue:`5645`
|
- Fix type hint for `cli_runner.invoke`. :issue:`5645`
|
||||||
- ``flask --help`` loads the app and plugins first to make sure all commands
|
- ``flask --help`` loads the app and plugins first to make sure all commands
|
||||||
are shown. :issue:5673`
|
are shown. :issue:5673`
|
||||||
|
|
|
||||||
|
|
@ -318,11 +318,12 @@ class SecureCookieSessionInterface(SessionInterface):
|
||||||
if not app.secret_key:
|
if not app.secret_key:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
keys: list[str | bytes] = [app.secret_key]
|
keys: list[str | bytes] = []
|
||||||
|
|
||||||
if fallbacks := app.config["SECRET_KEY_FALLBACKS"]:
|
if fallbacks := app.config["SECRET_KEY_FALLBACKS"]:
|
||||||
keys.extend(fallbacks)
|
keys.extend(fallbacks)
|
||||||
|
|
||||||
|
keys.append(app.secret_key) # itsdangerous expects current key at top
|
||||||
return URLSafeTimedSerializer(
|
return URLSafeTimedSerializer(
|
||||||
keys, # type: ignore[arg-type]
|
keys, # type: ignore[arg-type]
|
||||||
salt=self.salt,
|
salt=self.salt,
|
||||||
|
|
|
||||||
|
|
@ -381,14 +381,21 @@ def test_session_secret_key_fallbacks(app, client) -> None:
|
||||||
def get_session() -> dict[str, t.Any]:
|
def get_session() -> dict[str, t.Any]:
|
||||||
return dict(flask.session)
|
return dict(flask.session)
|
||||||
|
|
||||||
# Set session with initial secret key
|
# Set session with initial secret key, and two valid expiring keys
|
||||||
|
app.secret_key, app.config["SECRET_KEY_FALLBACKS"] = (
|
||||||
|
"0 key",
|
||||||
|
["-1 key", "-2 key"],
|
||||||
|
)
|
||||||
client.post()
|
client.post()
|
||||||
assert client.get().json == {"a": 1}
|
assert client.get().json == {"a": 1}
|
||||||
# Change secret key, session can't be loaded and appears empty
|
# Change secret key, session can't be loaded and appears empty
|
||||||
app.secret_key = "new test key"
|
app.secret_key = "? key"
|
||||||
assert client.get().json == {}
|
assert client.get().json == {}
|
||||||
# Add initial secret key as fallback, session can be loaded
|
# Rotate the valid keys, session can be loaded
|
||||||
app.config["SECRET_KEY_FALLBACKS"] = ["test key"]
|
app.secret_key, app.config["SECRET_KEY_FALLBACKS"] = (
|
||||||
|
"+1 key",
|
||||||
|
["0 key", "-1 key"],
|
||||||
|
)
|
||||||
assert client.get().json == {"a": 1}
|
assert client.get().json == {"a": 1}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue