Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions system/Security/Security.php
Original file line number Diff line number Diff line change
Expand Up @@ -307,6 +307,11 @@ private function removeTokenInRequest(IncomingRequest $request): void

// If the token is found in form-encoded data, we can safely remove it.
parse_str($body, $result);

if (! array_key_exists($tokenName, $result)) {
return;
}

unset($result[$tokenName]);
$request->setBody(http_build_query($result));
}
Expand Down
31 changes: 31 additions & 0 deletions tests/system/Security/SecurityCSRFSessionTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,37 @@ public function testCSRFVerifyJsonReturnsSelfOnMatch(): void
$this->assertSame('{"foo":"bar"}', $request->getBody());
}

public function testCSRFVerifyHeaderWithJsonBodyPreservesBody(): void
{
service('superglobals')->setServer('REQUEST_METHOD', 'POST');

$request = $this->createIncomingRequest();
$body = '{"foo":"bar"}';

$request->setHeader('X-CSRF-TOKEN', '8b9218a55906f9dcc1dc263dce7f005a');
$request->setBody($body);
$security = $this->createSecurity();

$this->assertInstanceOf(Security::class, $security->verify($request));
$this->assertLogged('info', 'CSRF token verified.');
$this->assertSame($body, $request->getBody());
}

public function testCSRFVerifyHeaderWithJsonBodyStripsTokenFromBody(): void
{
service('superglobals')->setServer('REQUEST_METHOD', 'POST');

$request = $this->createIncomingRequest();

$request->setHeader('X-CSRF-TOKEN', '8b9218a55906f9dcc1dc263dce7f005a');
$request->setBody('{"csrf_test_name":"8b9218a55906f9dcc1dc263dce7f005a","foo":"bar"}');
$security = $this->createSecurity();

$this->assertInstanceOf(Security::class, $security->verify($request));
$this->assertLogged('info', 'CSRF token verified.');
$this->assertSame('{"foo":"bar"}', $request->getBody());
}

public function testRegenerateWithFalseSecurityRegenerateProperty(): void
{
service('superglobals')
Expand Down
33 changes: 33 additions & 0 deletions tests/system/Security/SecurityTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,39 @@ public function testCsrfVerifyJsonReturnsSelfOnMatch(): void
$this->assertSame('{"foo":"bar"}', $request->getBody());
}

public function testCsrfVerifyHeaderWithJsonBodyPreservesBody(): void
{
service('superglobals')
->setServer('REQUEST_METHOD', 'POST')
->setCookie('csrf_cookie_name', self::CORRECT_CSRF_HASH);

$security = $this->createMockSecurity();
$request = $this->createIncomingRequest();
$body = '{"foo":"bar"}';

$request->setHeader('X-CSRF-TOKEN', self::CORRECT_CSRF_HASH);
$request->setBody($body);

$this->assertInstanceOf(Security::class, $security->verify($request));
$this->assertSame($body, $request->getBody());
}

public function testCsrfVerifyHeaderWithJsonBodyStripsTokenFromBody(): void
{
service('superglobals')
->setServer('REQUEST_METHOD', 'POST')
->setCookie('csrf_cookie_name', self::CORRECT_CSRF_HASH);

$security = $this->createMockSecurity();
$request = $this->createIncomingRequest();

$request->setHeader('X-CSRF-TOKEN', self::CORRECT_CSRF_HASH);
$request->setBody('{"csrf_test_name":"' . self::CORRECT_CSRF_HASH . '","foo":"bar"}');

$this->assertInstanceOf(Security::class, $security->verify($request));
$this->assertSame('{"foo":"bar"}', $request->getBody());
}

public function testCsrfVerifyPutBodyThrowsExceptionOnNoMatch(): void
{
service('superglobals')
Expand Down
2 changes: 2 additions & 0 deletions user_guide_src/source/changelogs/v4.7.2.rst
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ Deprecations
Bugs Fixed
**********

- **Security:** Fixed a bug where the CSRF filter could corrupt JSON request bodies after successful verification when the CSRF token was provided via the ``X-CSRF-TOKEN`` header. This caused ``IncomingRequest::getJSON()`` to fail on valid ``application/json`` requests.

See the repo's
`CHANGELOG.md <https://github.com/codeigniter4/CodeIgniter4/blob/develop/CHANGELOG.md>`_
for a complete list of bugs fixed.
Loading