Closed nelhage closed 5 years ago
FWIW, if you wanted to use pyca/cryptography to resolve this you could change these lines to:
self.extra_data_serializer = JSONWebSignatureSerializer(
HKDF(
algorithm=hashes.SHA256(),
length=32,
salt=None,
info="extra-data-serializer",
backend=default_backend(),
).derive(app.config['SECRET_KEY'])
)
self.cookie_serializer = TimedJSONWebSignatureSerializer(
HKDF(
algorithm=hashes.SHA256(),
length=32,
salt=None,
info="cookie-serializer",
backend=default_backend(),
).derive(app.config['SECRET_KEY'])
)
This will protect against the attack @nelhage describes.
Looking further, it looks like itsdangerous
has a built-in solution for this: The salt
parameter to signers
I believe you can just change the lines to
self.extra_data_serializer = JSONWebSignatureSerializer(
app.config['SECRET_KEY'], salt='flask-oidc-extra-data-serializer')
self.cookie_serializer = TimedJSONWebSignatureSerializer(
app.config['SECRET_KEY'], salt='flask-oidc-cookie-data-serializer')
I don't have time to test that change locally but I'll send a PR when I get a chance to.
That seems way simpler than my HKDF solution :-)
flask-oidc
creates two JWS signers using the Flask secret key:https://github.com/puiterwijk/flask-oidc/blob/68767920a5419e45ba5e725bf758587f01a2a265/flask_oidc/__init__.py#L179-L183
In addition, Flask itself creates a signer [using the same key].(https://github.com/pallets/flask/blob/7e714bd28b6e96d82b2848b48cf8ff48b517b09b/flask/sessions.py#L330-L332)
In general, it's unsafe to reuse a cryptographic key for multiple purposes. The proper construction is to use a KDF like HKDF to derive separate keys for each application.
I'm not sure if this is actually exploitable in this case, but if it is, one shape would look like:
OIDC_ID_TOKEN_COOKIE_NAME
cookie, which is certainly surprising at best.I haven't verified that the outputs are sufficiently compatible for the attack to actually work, but ideally the code wouldn't rely on details of slightly-differing output formats for an important security property.