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.

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

The audit mainly 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. 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.

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: 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:

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(), 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.

The dangerous features nobody warned you about

Symfony YAML can, when explicitly opted in, expose two advanced features that can be abused by an attacker feeding an arbitrary YAML:

  • 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, it’s a PHP object injection primitive, potentially leading to RCE.
  • 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. 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).

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 the inconsistency might be abused by attackers. 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. 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.

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.