mirror of
https://github.com/OpenHands/OpenHands.git
synced 2026-03-22 05:37:20 +08:00
Fix CVE-2024-23342: Replace python-jose with jwcrypto (#13012)
Co-authored-by: OpenHands CVE Fix Bot <openhands@all-hands.dev>
This commit is contained in:
@@ -6,8 +6,8 @@ from typing import Any, AsyncGenerator
|
||||
|
||||
import jwt
|
||||
from fastapi import Request
|
||||
from jose import jwe
|
||||
from jose.constants import ALGORITHMS
|
||||
from jwcrypto import jwe as jwcrypto_jwe
|
||||
from jwcrypto import jwk
|
||||
from pydantic import BaseModel, PrivateAttr
|
||||
|
||||
from openhands.agent_server.utils import utc_now
|
||||
@@ -168,21 +168,21 @@ class JwtService:
|
||||
# Derive a 256-bit key using SHA256
|
||||
key_256 = hashlib.sha256(key_bytes).digest()
|
||||
|
||||
# Encrypt the payload (convert to JSON string first)
|
||||
payload_json = json.dumps(jwt_payload)
|
||||
encrypted_token = jwe.encrypt(
|
||||
payload_json,
|
||||
key_256,
|
||||
algorithm=ALGORITHMS.DIR,
|
||||
encryption=ALGORITHMS.A256GCM,
|
||||
kid=key_id,
|
||||
)
|
||||
# Ensure we return a string
|
||||
return (
|
||||
encrypted_token.decode('utf-8')
|
||||
if isinstance(encrypted_token, bytes)
|
||||
else encrypted_token
|
||||
# Create JWK from symmetric key for jwcrypto
|
||||
symmetric_key = jwk.JWK(kty='oct', k=jwk.base64url_encode(key_256))
|
||||
|
||||
# Create JWE token with jwcrypto
|
||||
protected_header = {
|
||||
'alg': 'dir',
|
||||
'enc': 'A256GCM',
|
||||
'kid': key_id,
|
||||
}
|
||||
jwe_token = jwcrypto_jwe.JWE(
|
||||
json.dumps(jwt_payload).encode('utf-8'),
|
||||
recipient=symmetric_key,
|
||||
protected=protected_header,
|
||||
)
|
||||
return jwe_token.serialize(compact=True)
|
||||
|
||||
def decrypt_jwe_token(
|
||||
self, token: str, key_id: str | None = None
|
||||
@@ -201,15 +201,31 @@ class JwtService:
|
||||
ValueError: If token is invalid or key_id is not found
|
||||
Exception: If token decryption fails
|
||||
"""
|
||||
# Deserialize once and reuse for both header extraction and decryption
|
||||
try:
|
||||
jwe_obj = jwcrypto_jwe.JWE()
|
||||
jwe_obj.deserialize(token)
|
||||
except Exception:
|
||||
raise ValueError('Invalid JWE token format')
|
||||
|
||||
# Extract and validate the protected header
|
||||
try:
|
||||
protected_header = json.loads(jwe_obj.objects['protected'])
|
||||
except (KeyError, json.JSONDecodeError) as e:
|
||||
raise ValueError(f'Invalid JWE token format: {type(e).__name__}')
|
||||
|
||||
# Verify algorithms to prevent cryptographic agility attacks
|
||||
if (
|
||||
protected_header.get('alg') != 'dir'
|
||||
or protected_header.get('enc') != 'A256GCM'
|
||||
):
|
||||
raise ValueError('Unsupported or unexpected JWE algorithm')
|
||||
|
||||
if key_id is None:
|
||||
# Try to extract key_id from the token's header
|
||||
try:
|
||||
header = jwe.get_unverified_header(token)
|
||||
key_id = header.get('kid')
|
||||
if not key_id:
|
||||
raise ValueError("Token does not contain 'kid' header with key ID")
|
||||
except Exception:
|
||||
raise ValueError('Invalid JWE token format')
|
||||
# Extract key_id from the token's header
|
||||
key_id = protected_header.get('kid')
|
||||
if not key_id:
|
||||
raise ValueError("Token does not contain 'kid' header with key ID")
|
||||
|
||||
if key_id not in self._keys:
|
||||
raise ValueError(f"Key ID '{key_id}' not found")
|
||||
@@ -221,10 +237,14 @@ class JwtService:
|
||||
key_256 = hashlib.sha256(key_bytes).digest()
|
||||
|
||||
try:
|
||||
payload_json = jwe.decrypt(token, key_256)
|
||||
assert payload_json is not None
|
||||
# Create JWK from symmetric key for jwcrypto
|
||||
symmetric_key = jwk.JWK(kty='oct', k=jwk.base64url_encode(key_256))
|
||||
|
||||
# Decrypt the JWE token (reusing already deserialized jwe_obj)
|
||||
jwe_obj.decrypt(symmetric_key)
|
||||
|
||||
# Parse the JSON string back to dictionary
|
||||
payload = json.loads(payload_json)
|
||||
payload = json.loads(jwe_obj.payload.decode('utf-8'))
|
||||
return payload
|
||||
except Exception as e:
|
||||
raise Exception(f'Token decryption failed: {str(e)}')
|
||||
|
||||
Reference in New Issue
Block a user