154 lines
4.6 KiB
PHP
154 lines
4.6 KiB
PHP
|
<?php
|
||
|
|
||
|
namespace Tests\Framework\Integration\Security;
|
||
|
|
||
|
require_once dirname(__DIR__, 3) . '/app/classes/log.php';
|
||
|
require_once dirname(__DIR__, 3) . '/app/helpers/security.php';
|
||
|
|
||
|
use PHPUnit\Framework\TestCase;
|
||
|
|
||
|
class TestLogger {
|
||
|
public static function insertLog($user_id, $message, $scope = 'user') {
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Override the CSRF middleware to use our test logger
|
||
|
function applyCsrfMiddleware() {
|
||
|
$security = \SecurityHelper::getInstance();
|
||
|
|
||
|
// Skip CSRF check for GET requests
|
||
|
if ($_SERVER['REQUEST_METHOD'] === 'GET') {
|
||
|
return ['status' => 200, 'message' => ''];
|
||
|
}
|
||
|
|
||
|
// Skip CSRF check for initial login attempt
|
||
|
if ($_SERVER['REQUEST_METHOD'] === 'POST' &&
|
||
|
isset($_GET['page']) && $_GET['page'] === 'login' &&
|
||
|
!isset($_SESSION['username'])) {
|
||
|
return ['status' => 200, 'message' => ''];
|
||
|
}
|
||
|
|
||
|
// Check CSRF token for all other POST requests
|
||
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||
|
$token = $_POST['csrf_token'] ?? '';
|
||
|
if (!$security->verifyCsrfToken($token)) {
|
||
|
// Log CSRF attempt
|
||
|
$message = "CSRF attempt detected from IP: " . $_SERVER['REMOTE_ADDR'];
|
||
|
TestLogger::insertLog(0, $message, 'system');
|
||
|
|
||
|
// Return error message
|
||
|
return ['status' => 403, 'message' => $message];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return ['status' => 200, 'message' => ''];
|
||
|
}
|
||
|
|
||
|
class CsrfProtectionTest extends TestCase
|
||
|
{
|
||
|
private $security;
|
||
|
|
||
|
protected function setUp(): void
|
||
|
{
|
||
|
parent::setUp();
|
||
|
$this->security = \SecurityHelper::getInstance();
|
||
|
$_SERVER['REMOTE_ADDR'] = '127.0.0.1';
|
||
|
}
|
||
|
|
||
|
protected function tearDown(): void
|
||
|
{
|
||
|
parent::tearDown();
|
||
|
unset($_SESSION['csrf_token']);
|
||
|
unset($_POST['csrf_token']);
|
||
|
unset($_GET['page']);
|
||
|
unset($_SESSION['username']);
|
||
|
}
|
||
|
|
||
|
public function testCsrfProtectionValidToken()
|
||
|
{
|
||
|
// Generate CSRF token
|
||
|
$_SESSION['csrf_token'] = bin2hex(random_bytes(32));
|
||
|
$token = $_SESSION['csrf_token'];
|
||
|
|
||
|
// Simulate POST request with valid token
|
||
|
$_SERVER['REQUEST_METHOD'] = 'POST';
|
||
|
$_POST['csrf_token'] = $token;
|
||
|
|
||
|
// Call CSRF middleware
|
||
|
$response = applyCsrfMiddleware();
|
||
|
|
||
|
// Assert that the response is success
|
||
|
$this->assertEquals(200, $response['status']);
|
||
|
$this->assertEmpty($response['message']);
|
||
|
}
|
||
|
|
||
|
public function testCsrfProtectionInvalidToken()
|
||
|
{
|
||
|
// Simulate POST request with invalid token
|
||
|
$_SERVER['REQUEST_METHOD'] = 'POST';
|
||
|
$_POST['csrf_token'] = 'invalid_token';
|
||
|
|
||
|
// Call CSRF middleware
|
||
|
$response = applyCsrfMiddleware();
|
||
|
|
||
|
// Assert that the response is forbidden with error message
|
||
|
$this->assertEquals(403, $response['status']);
|
||
|
$this->assertStringContainsString("CSRF attempt detected from IP", $response['message']);
|
||
|
}
|
||
|
|
||
|
public function testCsrfProtectionGetRequest()
|
||
|
{
|
||
|
// Simulate GET request without token
|
||
|
$_SERVER['REQUEST_METHOD'] = 'GET';
|
||
|
|
||
|
// Call CSRF middleware
|
||
|
$response = applyCsrfMiddleware();
|
||
|
|
||
|
// Assert that GET requests are allowed without token
|
||
|
$this->assertEquals(200, $response['status']);
|
||
|
$this->assertEmpty($response['message']);
|
||
|
}
|
||
|
|
||
|
public function testCsrfProtectionInitialLogin()
|
||
|
{
|
||
|
// Simulate initial login POST request
|
||
|
$_SERVER['REQUEST_METHOD'] = 'POST';
|
||
|
$_GET['page'] = 'login';
|
||
|
|
||
|
// Call CSRF middleware
|
||
|
$response = applyCsrfMiddleware();
|
||
|
|
||
|
// Assert that initial login is allowed without token
|
||
|
$this->assertEquals(200, $response['status']);
|
||
|
$this->assertEmpty($response['message']);
|
||
|
}
|
||
|
|
||
|
public function testCsrfProtectionMissingToken()
|
||
|
{
|
||
|
// Simulate POST request without token
|
||
|
$_SERVER['REQUEST_METHOD'] = 'POST';
|
||
|
|
||
|
// Call CSRF middleware
|
||
|
$response = applyCsrfMiddleware();
|
||
|
|
||
|
// Assert that missing token is rejected
|
||
|
$this->assertEquals(403, $response['status']);
|
||
|
$this->assertStringContainsString("CSRF attempt detected from IP", $response['message']);
|
||
|
}
|
||
|
|
||
|
public function testCsrfProtectionEmptyToken()
|
||
|
{
|
||
|
// Simulate POST request with empty token
|
||
|
$_SERVER['REQUEST_METHOD'] = 'POST';
|
||
|
$_POST['csrf_token'] = '';
|
||
|
|
||
|
// Call CSRF middleware
|
||
|
$response = applyCsrfMiddleware();
|
||
|
|
||
|
// Assert that empty token is rejected
|
||
|
$this->assertEquals(403, $response['status']);
|
||
|
$this->assertStringContainsString("CSRF attempt detected from IP", $response['message']);
|
||
|
}
|
||
|
}
|