In April 2023, a vulnerability tagged as CVE-2023-29197 rocked the PHP community—particularly developers using the popular guzzlehttp/psr7 package. This library is a key implementation of the PSR-7 HTTP message interface and is a backbone for many web APIs and tools built with PHP. What makes CVE-2023-29197 alarming is its potential to let an attacker sneak special characters into HTTP headers, opening up possibilities for request smuggling, injection attacks, and more.

If you’re a PHP developer, here's what you need to know—broken down simply and effectively.


## What Is guzzlehttp/psr7?

guzzlehttp/psr7 is a Composer package that helps PHP applications handle HTTP messages in a standardized way. It’s used by millions of projects, as it participates in the PSR-7 interface for request and response messages.

What Actually Happened?

The vulnerability allows a malicious user to inject newline characters (\n) *into* HTTP header names and values. The HTTP specification says headers end with \r\n\r\n; however, a lot of servers also accept just \n\n as a valid header boundary.

This means that inserting raw \n characters can help attackers “smuggle” extra headers or break the protocol, leading to:

Enable HTTP request smuggling, which can bypass security controls

This vulnerability is actually a follow-up to CVE-2022-24775 from the previous year—a similar bug that was only partially fixed.

Suppose you use guzzlehttp/psr7 to create a request and you take headers from user input

use GuzzleHttp\Psr7\Request;

$headerName = $_GET['header_name']; // Potentially tainted input
$headerValue = $_GET['header_value']; // Potentially tainted input

$request = new Request('GET', 'https://example.com';, [
    $headerName => $headerValue
]);

header_value = value\nX-Another-Header: injected2

These newline characters (\n) can trick lower-level libraries or servers into parsing new headers or breaking apart the request, depending on the stack and server configuration.

Bad Header Example

GET / HTTP/1.1
Host: example.com
legitimate-header
X-Evil-Header: injected: value
X-Another-Header: injected2

The Fix: Input Validation

The maintainers patched this in versions 1.9.1 and 2.4.5 by carefully validating headers to *forbid* newlines.

Patched Code Example from guzzlehttp/psr7

// src/Message.php

private function filterHeader($name, $value)
{
    if (preg_match("/[\r\n]/", $name)) {
        throw new \InvalidArgumentException("Header name contains invalid characters: $name");
    }
    if (preg_match("/[\r\n]/", $value)) {
        throw new \InvalidArgumentException("Header value contains invalid characters: $value");
    }
}

Exploit Details: How Could an Attacker Use This?

To exploit this, an attacker needs some way to influence/request headers sent via guzzlehttp/psr7, such as:

`bash

curl 'https://your-api.com?header_name=foo%aSet-Cookie:%20evil=1&header_value=bar'

`php

// foo\nSet-Cookie: evil=1 => causes HTTP splitting

Server receives split headers:

Now, some servers might process “Set-Cookie: evil=1” as a real header, issuing a cookie under an attacker’s control.

Original References

- Official CVE-2023-29197 details (NVD)
- Guzzle Patch Discussion/PR
- PSR-7 HTTP Message Interface Spec

Upgrade immediately!

Patch to guzzlehttp/psr7 version 1.9.1 or 2.4.5 (or newer).

No Workarounds

There are no known workarounds for this vulnerability. Input validation and proper escaping is complex in HTTP header contexts, so upgrading is the only recommended path.

Conclusion

Even small bugs in header handling can chain into major vulnerabilities—especially with the flexibility and power of modern PHP HTTP libraries. CVE-2023-29197 is a stark reminder: always keep packages up-to-date, especially when they touch critical protocol logic.

Timeline

Published on: 04/17/2023 22:15:00 UTC
Last modified on: 05/03/2023 18:12:00 UTC