Skip to content
nulltapE-reader edition

← All e-reader articles

3 min read

Authlib accepts unsigned JWS payloads as verified

An empty signatures array skips Authlib's verification loop. Locate callers of the JSON JWS deserializer and reject unsigned input before trusting its payload.

Read on the standard site

A glass authentication token with an empty signature recess, against a pale blue-gray background.

A JWS with zero signatures passes Authlib’s JSON signature verification path. In a September 28 vulnerability note, CERT/CC says Authlib through 1.7.2 accepts an empty signatures array and returns the payload as verified. Python services that use this library to authenticate users, authorize API calls, or trust signed messages need to identify where that result becomes a decision.

The caller can receive a verified payload from an object with no signature; the attacker needs no signing key. CERT/CC names CVE-2026-96760 and credits Tong Hoang Gia and Nguyen Minh Tuan with reporting it. The CVE Program record describes the affected range as 1.7.2 and below.

An empty list passes verification

A JWS normally carries a payload and one or more signatures. Authlib’s JsonWebSignature.deserialize_json() starts with a successful verification state, then checks each signature in the array. With an empty signatures array, that loop never runs; the initial success state survives. As CERT/CC puts it, the function “treats the payload as successfully verified,” allowing forged content without signing material.

CERT/CC says both a structured-object call to deserialize_json() and a string-based call to deserialize() reach this behavior. The issue is in JWS general JSON serialization. An application that passes untrusted general-JSON JWS input to either path can treat its unsigned payload as authenticated. A role, scope, identity claim, service-to-service message, or signed configuration value could then be accepted on the strength of a verification result that skipped cryptography.

Authlib also handles OAuth and OpenID Connect; scope exposure by finding callers that pass untrusted general-JSON JWS objects to the affected deserializers and trust their results.

Guard the JWS entry points

Search application code for JsonWebSignature.deserialize_json, jws.deserialize_json, and jws.deserialize, then trace which callers receive untrusted general-JSON JWS objects. Add a validation guard before deserialization that rejects a missing, empty, or otherwise invalid signatures list and require a successfully checked signature before using the payload for authorization or integrity decisions. Keep that guard close to the call, including at service boundaries where signed messages enter.

Check deployed dependency versions as well as lockfiles. PyPI’s Authlib metadata lists 1.8.0, uploaded August 30; CERT/CC reported on September 28 that an official patch had not been made available. Treat 1.8.0 as an unconfirmed fix until a maintainer documents one, and keep the input guard in place. CERT/CC also says it could not reach the vendor for coordination and had received no vendor statement at publication.

For a regression check, use a non-operational fixture in your own test suite representing a JWS object whose signatures field is empty. Expect your application to reject it before accepting any payload as verified. Also confirm your legitimate signed-message fixtures still pass. This checks the application guard; a later vendor fix needs its own documented version and upgrade verification.

Keep unsigned payloads out

Find services that trust the output of Authlib’s JSON JWS deserializers; record the installed Authlib version and the call sites where that output controls an identity, permission, or signed message. Reject an empty signatures list before the call, and test that the rejection happens at every exposed entry point while valid signed messages still work. Retain the guard when updating the library, and re-run the same rejection test against each deployed service.

Primary sources