Log out with a POST form.

The logout link put the CSRF token in the URL, which goes into logs and the browser history.
This commit is contained in:
tim
2026-09-26 15:27:51 +02:00
parent 061f9c1a37
commit 71f768bff2
5 changed files with 38 additions and 10 deletions
+14 -6
View File
@@ -182,7 +182,7 @@ class LoginTest extends PageTestCase {
// A new browser session with only the cookie
unset($this->cookies['PHPSESSID']);
$body = $this->get('/')['body'];
$this->assertStringContainsString('/publish/logout?token=', $body);
$this->assertStringContainsString('<form action="/publish/logout" method="post">', $body);
$this->assertSame('ed', $this->session()['editor']);
}
@@ -224,7 +224,15 @@ class LoginTest extends PageTestCase {
public function testLogoutNeedsTheToken(): void {
$this->loginAs();
$this->assertRedirect('/', $this->get('/publish/logout?token=wrong'));
$this->assertRedirect('/', $this->post('/publish/logout', array('csrf_token' => 'wrong')));
$this->assertSame('ed', $this->session()['editor']);
}
// The token stays out of URLs, which go into logs and the browser history
public function testLogoutNeedsAPost(): void {
$token = $this->loginAs();
$this->assertRedirect('/', $this->get('/publish/logout?token=' . $token));
$this->assertRedirect('/', $this->get('/publish/logout?csrf_token=' . $token));
$this->assertSame('ed', $this->session()['editor']);
}
@@ -236,7 +244,7 @@ class LoginTest extends PageTestCase {
$this->cookies['PersistentAuth'] = 'ed|key';
$session = self::$state . '/sessions/sess_' . $this->cookies['PHPSESSID'];
$answer = $this->get('/publish/logout?token=' . $token);
$answer = $this->post('/publish/logout', array('csrf_token' => $token));
$this->assertRedirect('/article?id=2', $answer);
$this->assertFileDoesNotExist($session);
$this->assertArrayNotHasKey('PHPSESSID', $this->cookies);
@@ -247,17 +255,17 @@ class LoginTest extends PageTestCase {
public function testAForgedHostDoesNotMoveTheLogoutRedirect(): void {
$token = $this->loginAs('ed', array('referrer' => '/article?id=2'));
$this->assertRedirect('/article?id=2',
$this->get('/publish/logout?token=' . $token, array('Host' => 'evil.example')));
$this->post('/publish/logout', array('csrf_token' => $token), array('Host' => 'evil.example')));
}
#[DataProvider('otherSites')]
public function testLogoutIgnoresALastPageOnAnotherSite(string $referrer): void {
$token = $this->loginAs('ed', array('referrer' => $referrer));
$this->assertRedirect('/', $this->get('/publish/logout?token=' . $token));
$this->assertRedirect('/', $this->post('/publish/logout', array('csrf_token' => $token)));
}
public function testLogoutDoesNotGoBackToTheLogoutPage(): void {
$token = $this->loginAs('ed', array('referrer' => '/publish/logout'));
$this->assertRedirect('/', $this->get('/publish/logout?token=' . $token));
$this->assertRedirect('/', $this->post('/publish/logout', array('csrf_token' => $token)));
}
}