Blog
5 min read

JSON Schema References Considered Harmful

  • avatar
    Name
    James Brown
    Twitter
View as Markdown

Svix recently found and remediated a frustrating vulnerability in one of our endpoints, introduced due to a combination of a bad specification and risky defaults in an open-source library. This blog post walks through the details and what we as a community of engineers can do to stop having the same family of problem every few years.

Background

"JSON Schema" is a meta-language for writing schemata of other file formats; basically, a language for describing formats. JSON Schema documents are themselves valid JSON1, and expected to be applied to JSON-like data2. In addition to normal schema constructs (primitive types like number and string, and composite types like object and array) JSON Schema includes facilities for decomposing schemata into multiple different sections or files, which can be referenced using the $ref keyword. These can either be other fragments of the same document (written as #/path/to/other/section), or entirely-other JSON Schema files hosted at an arbitrary URI.

Svix allows users to upload arbitrary schemata for Event Types (as part of v1.event-type.create) and uses the open-source Rust jsonschema library to validate those schemata. This library automatically (and, as of v0.21.0, eagerly) handles dereferencing $ref entries, including fetching arbitrary HTTP Resources and loading files off of disk if a file:/// URI is specified. If a resource doesn't include a valid JSON Schema document, the error message will include fragments of the affected document.

If this problem is starting to sound a lot like the external entity expansion attacks that have plagued XML since 1998, well, you're not wrong. There's nothing new under the sun, and as long as the same enterprise architecture astronauts keep building the same kinds of systems the same ways, we'll have the same security problems. Large applications inevitably develop a huge API surface area3, and every IDL inevitably ends up grappling with the same problems of how to foster type-reuse and DRY principles in schema design. XML did it, Avro did it, and now here we are with the same problem in JSON Schema.

Svix Impact

An attacker could use this as a primitive to make arbitrary HTTP GET requests to any URI from the (potentially-network-privileged) standpoint of the Svix server. If these GET requests returned valid JSON that was not a valid JSON Schema document, the error message may contain fragments of the returned document4. There do not appear to have been any such malicious schemata uploaded to the Svix cloud environment. Any attacker would have to already have organization-level access to any OSS or Enterprise deployment, so this is an unlikely attack vector there. Nonetheless, OSS customers are encouraged to upgrade to v1.104.0 and Enterprise customers are encouraged to upgrade to v1.3116.0 or later.

Looking to the Future

We look forward to an eventual future where Rust offers tools for robustly consuming third-party libraries without having to painstakingly audit their code; some kind of comprehensive entitlements system like Joe-E, but for Rust. Barring language features, a carefully-designed microservice architecture, or a decomposition into communicating wasm components could be an effective strategy to reduce dependency risk.

This is also a good opportunity to restate a principle for library development: delegate I/O to your callers. The choice that jsonschema embeds its own HTTP client by default is more than a little maddening -- aside from the security issues, it also makes testing quite a bear. Dependency Injection can be a valuable pattern for this sort of thing; if jsonschema took a mandatory Retrieve instead of shipping a "convenient" pre-baked option, this issue could never have arisen.

Alas, you have to go to war with the army you have, so for now it's just important for software engineers to be vigilant about the third-party libraries that we use and depend on — a cursory examination of JSON Schema wouldn't make most engineers perk up about XXE risks and arbitrary network requests. Every dependency needs to be fully understood by the humans responsible for integrating and maintaining it.


If you have any questions or concerns, please don't hesitate to reach out to support@svix.com.

Stay tuned at https://www.svix.com/blog for future posts, follow us on GitHub, Mastodon, or RSS for the latest updates for the Svix webhook service, or join the discussion on our community Slack.

Are you interested in working with us to build and secure cool Rust applications for server-to-server communication? Come work at Svix!

Footnotes

  1. Yes, of course there's a JSON Schema schema for JSON Schema. Unfortunately, it is not referred to as the meta-circular schema... ↩

  2. For example, JSON Schema is used to describe the schemata for Kubernetes, even though Kubernetes configurations are usually written in YAML instead of JSON ↩

  3. A problem which LLM-assisted software development, which makes new code cheap and good taste expensive, is unlikely to ameliorate ↩

  4. An attacker could also trigger arbitrary file reads and, if those files contained valid JSON that was not a valid JSON Schema document, read fragments of those files. There aren't any interesting JSON files on any of the Svix containers, though. ↩

Ready to send webhooks?

Svix is the enterprise ready webhooks service. Build a secure, reliable, and scalable webhook platform in minutes.