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.
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.
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:
Parser to load YAML into PHP.Dumper to serialize PHP structures into YAML.LintCommand CLI to validate the syntax of YAML files.The source code is available at https://github.com/symfony/yaml.
The audit mainly focused on:
Parser.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:
We also set up a fuzzing campaign using the experimental PHP-Fuzzer. The goal of this campaign was to surface crashes and similar errors, but no interesting behavior was observed.
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.
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.
| ID | Vulnerability | Severity | Status |
|---|---|---|---|
| 1 | Denial of Service (DoS) via Infinite Recursion of Nested YAML Blocks (CVE-2026-45133) | Low | Closed |
| 2 | Denial of Service (DoS) via Unbounded Alias Resolution (CVE-2026-45304) | Low | Closed |
| 3 | Regular Expression Denial of Service (ReDoS) in Parser::cleanup (CVE-2026-45305) | Low | Closed |
| 4 | Lack of Security Warning for Object Deserialization | Informational | Closed |
| 5 | Lack of Security Warning for Constant Resolution | Informational | Closed |
The first three findings are all variations on the same theme: trying to exploit an “amplification factor” to turn a small input into a huge amount of work for the component.
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.
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, this can be exploited for the classic Billion Laughs amplification:
| |
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(), but on the later json_encode(), when something finally walks the structure to materialize it.
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 can lead to catastrophic backtracking: a crafted header makes the regex engine explore an exponential number of paths, stalling the process.
All three were reported to security@symfony.com, moved to GitHub advisories, assigned CVEs, and fixed by following the attached recommendations.
Symfony YAML can, when explicitly opted in, expose two advanced features that can be abused by an attacker feeding an arbitrary YAML:
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, it’s a PHP object injection primitive, potentially leading to RCE.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. | |
These are intentional features, gated behind flags, and perfectly safe when used on trusted input. However, the documentation did not sufficiently inform developers of the security risks related to enabling these features for untrusted input. The maintainers fixed this by adding security callouts to the documentation (commit aeeba0f).
Beyond the findings, we left the project with two longer-term recommendations:
The full details and rationale can be read in the report.
The Symfony YAML component is a mature, well-designed piece of software. The findings we reported are the kind of things that we oftentimes encounter with expressive parsers that provide advanced features, with a threat model that expects untrusted data to be parsed: recursions and entity expansion are dangerous, regular expressions are complex, and it’s always better to add – reasonable and customizable – specific limits to potentially intensive operations. Furthermore, when a library exposes a feature that hands user input to unserialize(), the documentation should say so in a big red danger callout.
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.
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!
Previous post