JSON stack-overflow bij diep geneste input (jsonDecode is recursief, geen diepte-limiet) #1353
Labels
No labels
accepted
bug
declined
docs
duplicate
enhancement
good first issue
in-progress
needs-info
privacy
security
triage
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
LibreKAT/Ocideck#1353
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Bevinding
Dart's
jsonDecodeis recursief geïmplementeerd. Een sidecar van 16 MiB ofgrafiekdata van 8 MiB vol
[[[[[[...]]]]]]laat de call-stack overlopen encrasht de app — ondanks de byte-cap die er wél is. De byte-cap vangt grootte,
niet diepte.
Er zijn 77
jsonDecode-call sites in de codebase, waarvan er ~20 onvertrouwdeinput decoderen: sidecars (
.ink.json,.user-notes.json,.miauw.json,.seal.json,.dismissals.json), grafiekdata, OpenKat-import, collab-blobs,recovery-bestanden, stijlprofielen. Elke daarvan is een pad waar een
kwaadwillend of corrupt bestand de app kan laten crashen.
De codebase begrijpt dit patroon al — op andere plekken
Diepte-limieten bestaan al voor drie andere invoerformaten:
libreplanXmlMaxDepth(
lib/services/libreplan/libreplan_xml.dart:283)_maxDerDepth(
lib/utils/asn1_der.dart:128)depth > 16(
lib/services/import/importers/keynote/key_text_salvage.dart:61)Maar niet voor JSON. Dat is de asymmetrie.
Oplossingsrichting
Twee opties, oplopend in moeite:
Pre-scan: tel de nesting-diepte van de raw string vóór
jsonDecode(tel ongebalanceerde
[/{). Goedkoop, geen dependency, vangt hetpathologische geval. Weiger boven een redelijke grens (bv. 256).
Depth-guarded parser: een wrapper rond
jsonDecodeof een eigendecoder die een diepte-limiet handhaaft tijdens het parsen. Meer werk,
maar vangt ook geneste structuren die een simpele pre-scan niet ziet
(bv.
{"a":{"b":{"c":...}}}met strings die[bevatten).Optie 1 is de lazy-senior-keuze: het pathologische geval is diep geneste
brackets, en die vang je met een lineaire scan. Optie 2 is pas nodig als er
een realistisch geneste-strings-aanval is, wat voor deze invoer onwaarschijnlijk
is.
Herkomst
Gevonden tijdens security research naar defense-in-depth voor OciDeck. De
bestaande byte-caps (sidecar 16 MiB, grafiekdata 8 MiB) vangen grootte, maar
niet diepte — en een stack-overflow is precies de faalmodus die grootte-caps
niet dekken.