Thread
Commits
-
oauth: Limit JSON parsing depth in the client
- cbc8fd0c9aec 18.0 landed
-
[PATCH] oauth: Prevent stack overflow by limiting JSON parse depth
Jacob Champion <jacob.champion@enterprisedb.com> — 2025-05-07T18:13:24Z
Hi all, I forgot to put a recursion limit in the new OAuth parsers; the server-side depth checks don't apply to the client, and it's not using the incremental parser to move the burden from the stack to the heap. Luckily, we track the nesting level already, so a fix (attached) can be pretty small. (Eventually I'd like to set a limit via the client-side JSONAPI, or maybe port the server's stack checks? That's 19 material.) Enforcing a limit is relatively easy, but choosing the limit is somewhat harder, because OAuth will continue to evolve and add extensions that we're supposed to gracefully ignore. The most typical depth I've seen in practice is 2 (an object containing arrays of strings). I took a look through the IANA registry [1], and I think the maximum nesting depth we can get today is if someone plops an entire JSON Web Key Set into their discovery document. That could bring the depth up to 6 with some of the newer key types. Hopefully the extension depth doesn't continue to grow unbounded, but it's easy for spec writers to find ways to nest new stuff, so I've chosen a max depth of 16. Probably overkill. Hopefully overkill? For the SASL parser, which is only speaking OAUTHBEARER to a Postgres server/proxy, I think we can be way more strict. We expect a nesting depth of only 1 there. I've put in a limit of 8, to try to avoid a backport in the case that some nested extension is published and suddenly gains popularity, but I could easily be talked downwards. And if we ever have to revisit these limits, I think that would be reason enough to switch from the recursive descent parser over to the incremental parser, anyway. = Postmortem = As an aside: I was curious why libfuzzer didn't catch this, since I've been using it as a safety net. The answer was a depressingly simple "you ignored a printed warning": INFO: -max_len is not provided; libFuzzer will not generate inputs larger than 4096 bytes It looks like this default limit is variable, based on the corpus you already have, and 4K is not enough to run my machine out of stack. But setting it explicitly to our response limit of 256K allows libfuzzer to find this issue very quickly, and I will do that going forward. (If anyone has run libfuzzer before and has a "favorite configuration", I'd love to hear it!) Thanks, --Jacob [1] https://www.iana.org/assignments/oauth-parameters/oauth-parameters.xhtml#authorization-server-metadata -
Re: [PATCH] oauth: Prevent stack overflow by limiting JSON parse depth
Aleksander Alekseev <aleksander@timescale.com> — 2025-05-08T12:22:35Z
Hi Jacob, > I forgot to put a recursion limit in the new OAuth parsers; the > server-side depth checks don't apply to the client, and it's not using > the incremental parser to move the burden from the stack to the heap. > Luckily, we track the nesting level already, so a fix (attached) can > be pretty small. > > [...] Thanks for the patch. It looks good to me. It's well documented and covered with tests. I can confirm that the tests pass. Also they fail if I decrease the $nesting_limit value to 15. -- Best regards, Aleksander Alekseev
-
Re: [PATCH] oauth: Prevent stack overflow by limiting JSON parse depth
Jacob Champion <jacob.champion@enterprisedb.com> — 2025-05-08T15:45:04Z
On Thu, May 8, 2025 at 5:22 AM Aleksander Alekseev <aleksander@timescale.com> wrote: > Thanks for the patch. It looks good to me. It's well documented and > covered with tests. I can confirm that the tests pass. Also they fail > if I decrease the $nesting_limit value to 15. Thanks for the review! --Jacob