Symfony YAML Security Audit

TL;DR

Shielder, together with OSTIF and the Sovereign Tech Agency, performed a Security Audit of the Symfony YAML component, a PHP library to parse and dump YAML files.

The audit resulted in five (5) findings ranging from low to informational severity, including three (3) CVEs. All of them have been addressed by the Symfony maintainers.

Today, we are publishing the full report in our dedicated repository.

Introduction

In December 2025, Shielder was hired to perform a Security Audit of the Symfony YAML component, a PHP library to load and dump YAML files. The audit was facilitated by the Open Source Technology Improvement Fund (OSTIF).

YAML - YAML Ain’t Markup Language - is a human-friendly data serialization language for all programming languages. It is a hugely popular format for configuration files, striking a balance between human readability and advanced features. That same set of “advanced features” is exactly what makes YAML parsers an interesting target: the more a format can do, the more an attacker can ask it to do on your behalf.

The Symfony YAML component is shipped by default together with Symfony, an industry-leading PHP framework for building web applications, but it can also be pulled in as a standalone Composer package. It provides:

  • A Parser to load YAML into PHP.
  • A Dumper to serialize PHP structures into YAML.
  • A LintCommand CLI to validate the syntax of YAML files.

The source code is available at https://github.com/symfony/yaml.

Context and Scope

When you ship a YAML parser inside a framework that powers a large chunk of the PHP web, the most interesting question is not “is the happy path correct?” but rather “what happens when someone feeds it input it was never meant to see?”. Developers reach for Yaml::parse() to read config files they control, but the same call frequently ends up at the other end of an HTTP request, parsing data that an attacker fully controls.

With that in mind, the audit focused on:

  • Assessing the risks of passing untrusted YAML content to the Parser.
  • Assessing the lack of proper security considerations in the documentation and in the provided examples.

Given the limited size of the codebase, the audit was mostly performed following a Manual Source Code Review approach. We directed the review towards three classes of threats:

  • Code deserialization gadgets triggered during YAML parsing.
  • Leaks of sensitive information into the parsed PHP output.
  • Resource starvation or program hangs occurring during parsing.

We also set up a fuzzing campaign using the experimental PHP-Fuzzer. Honesty time: between some rough edges of the tool and the harnesses we wrote, this particular campaign did not surface anything interesting. Not every avenue pays off, and that’s fine - it’s part of the job.

Finally, to measure how closely Symfony YAML follows the YAML spec, we wrote a script to run the component against the YAML Test Suite: feed each test’s in.yaml to the parser, serialize the result with json_encode, and diff it against the expected in.json. This is where parser differentials start to show up - more on that below.

Findings Summary and Recommendations

The Symfony YAML component is, overall, adequately robust and well designed from a security standpoint - but there is still some room for improvement.

The Shielder team identified three (3) low and two (2) informational findings. The main themes were unmitigated resource starvation when parsing complex data, and a lack of security-focused documentation around the parser’s more dangerous features.

IDVulnerabilitySeverityStatus
1Denial of Service (DoS) via Infinite Recursion of Nested YAML Blocks (CVE-2026-45133)LowClosed
2Denial of Service (DoS) via Unbounded Alias Resolution (CVE-2026-45304)LowClosed
3Regular Expression Denial of Service (ReDoS) in Parser::cleanup (CVE-2026-45305)LowClosed
4Lack of Security Warning for Object DeserializationInformationalClosed
5Lack of Security Warning for Constant ResolutionInformationalClosed

Three flavors of Denial of Service

The first three findings are all variations on the same theme: handing the parser a small, innocent-looking string that asks it to do an enormous amount of work.

Infinite Recursion of Nested YAML Blocks (CVE-2026-45133). The doParse() and parseBlock() methods in Parser.php are mutually recursive: every time doParse() meets an indented line, it calls parseBlock(), which spins up a new parser and calls doParse() on the indented content. Since nothing capped the maximum depth, a deeply nested document keeps pushing frames until PHP gives up with a Fatal Error. A few kilobytes of indentation is enough to take down the process.

Unbounded Alias Resolution - a.k.a. “Billion Laughs” (CVE-2026-45304). YAML supports anchors (&foo) and aliases (*foo) so you can reuse a node instead of repeating it. Without a cap on how many times those references can expand, you get the classic Billion Laughs amplification:

1
2
3
4
5
a: &a ["lol","lol","lol","lol","lol","lol","lol","lol","lol"]
b: &b [*a,*a,*a,*a,*a,*a,*a,*a,*a]
c: &c [*b,*b,*b,*b,*b,*b,*b,*b,*b]
d: &d [*c,*c,*c,*c,*c,*c,*c,*c,*c]
# ... keep going and watch the memory graph go vertical

A tiny document expands into a structure with billions of nodes, exhausting memory. A fun wrinkle we noted in the report: thanks to PHP’s lazy allocation, the process often doesn’t die on parse() - it dies later, on json_encode(), when something finally walks the structure to materialize it. So the crash can surface far away from the line that actually parsed the attacker’s input.

ReDoS in Parser::cleanup (CVE-2026-45305). Before parsing, Parser::cleanup() strips spaces, headers, and comments. One of the regexes used to remove YAML headers is:

#^\%YAML[: ][\d\.]+.*\n#u

The [\d\.]+ and .* subpatterns can match the same input, and with greedy quantifiers that’s a recipe for catastrophic backtracking: a crafted header makes the regex engine explore an exponential number of paths, stalling the process. The fix is the textbook one - remove the overlap or switch to possessive quantifiers.

All three were reported to security@symfony.com, moved to GitHub advisories, assigned CVEs, and fixed by adding the appropriate limits.

The dangerous features nobody warned you about

The two informational findings aren’t bugs in the strict sense - they’re sharp edges that the documentation didn’t flag.

Symfony YAML can, when explicitly opted in, do two things that should make any security person sit up:

  • With Yaml::PARSE_OBJECT, the !php/object tag feeds attacker-controlled data straight into PHP’s unserialize(). If untrusted YAML ever reaches a parser with this flag on, you’ve got a classic PHP object injection primitive, with RCE as the worst-case outcome.
  • With Yaml::PARSE_CONSTANT, the !php/const tag resolves arbitrary PHP constants via \constant(). An attacker can use this to read built-in constants - or, more interestingly, any custom constant defined by the application, such as a hard-coded secret.
1
2
3
4
5
6
7
8
# Inline.php
case str_starts_with($scalar, "!php/object"):
    if (self::$objectSupport) {
        // ...
        return unserialize(
            self::parseScalar(substr($scalar, 12)),
        );
    }

These are intentional features, gated behind flags, and perfectly safe when used on trusted input. The problem was simply that the docs never spelled out “do not turn these on for untrusted input”. The maintainers fixed this by adding security callouts to the documentation (commit aeeba0f).

Recommendations

Beyond the findings, we left the project with two longer-term recommendations:

  • Add Security Documentation. The component lacked a dedicated “Security” section to inform users about known risks, the implications of the various parser flags, and general hardening guidance.
  • Harden Against Parser Differentials. When tested against the YAML Test Suite, Symfony YAML shows a number of inconsistencies with the spec. These matter whenever the same YAML is parsed by two different implementations - for instance, validated/sanitized by one parser and then acted upon by another. The two can disagree on what the document means, and an attacker can live in that gap. We recommended improving spec adherence, raising the success ratio against the test suite, wiring up a Symfony YAML interface for play.yaml.com to make differences easy to quantify, and documenting the differential risk for developers.

The full details and rationale can be read in the report.

Conclusions

The Symfony YAML component is a mature, well-designed piece of software, and it shows: there were no critical, high, or even medium findings to report. What we found instead is the kind of thing that tends to hide in any sufficiently expressive parser - resource-exhaustion edge cases when fed deliberately pathological input, and powerful opt-in features whose risks weren’t loudly enough advertised.

The recurring lesson, the same one we keep relearning across these audits, is that a parser’s threat model lives and dies on the trust boundary. Parsing a config file you wrote is one thing; parsing a blob that arrived over the network is another entirely. Limits on recursion, alias expansion, and regex backtracking are cheap insurance against an attacker who only needs to send you a few kilobytes to ruin your day. And when a library exposes a feature that hands user input to unserialize(), the documentation should say so in no uncertain terms.

If you use Symfony YAML - directly or via Symfony - the recommendation is the usual one: update to the latest release, never enable PARSE_OBJECT or PARSE_CONSTANT on input you don’t fully trust, and treat untrusted YAML with the same suspicion you’d give any other untrusted data.

We would like to thank the Symfony maintainers - notably Nicolas Grekas and Fabien Potencier - for being responsive and collaborative throughout the triage and remediation of these findings.

It was a pleasure for our team to work with OSTIF and the Symfony core team in securing a small but widely-used corner of the PHP ecosystem.

Pitch 🗣

Did you know OSTIF helps sensitive open-source projects in securing funds to perform security audits? They will also help you in scoping the assessment, finding a trusted partner to perform the analysis, and ensuring full transparency along the way.

P.S. if you need help in threat modeling and auditing your PHP applications or libraries –> get in touch with us!

4 min

Data

30 giugno 2026

Autore

smaury

Sono Abdel Adim Oisfi più conosciuto come smaury.
Lavoro: CEO, Security Researcher, Penetration Tester in Shielder.
Passioni: Hacking, autostop, tuffi e ginocchia sbucciate.

Autore

suidpit

Security Researcher e Penetration Tester in Shielder. Umano, Caotico Buono. Seguace del Bushido e della Disney.