Skip to content

Commit

Permalink
Add CSRF token security to the realtime compiler dashboard
Browse files Browse the repository at this point in the history
  • Loading branch information
caendesilva committed Nov 13, 2023
1 parent 48f1f58 commit f06ff3a
Show file tree
Hide file tree
Showing 3 changed files with 50 additions and 1 deletion.
5 changes: 5 additions & 0 deletions packages/realtime-compiler/resources/dashboard.blade.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="csrf-token" content="{{ $csrfToken }}">
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-T3c6CoIi6uLrA9TneNEoa7RxnatzjcDSCmG1MXxSR1GAsXEV/Dwwykc2MPK8M2HN" crossorigin="anonymous">
<title>{{ $title }}</title>
<base target="_parent">
Expand Down Expand Up @@ -64,6 +65,7 @@
<h2 class="h5 mb-0">Site Pages & Routes</h2>
@if($dashboard->isInteractive())
<form class="buttonActionForm" action="" method="POST">
<input type="hidden" name="_token" value="{{ $csrfToken }}">
<input type="hidden" name="action" value="openInExplorer">
<button type="submit" class="btn btn-outline-primary btn-sm" title="Open project in system file explorer">Open folder</button>
</form>
Expand Down Expand Up @@ -136,6 +138,7 @@
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<form id="createPageForm" action="" method="POST">
<input type="hidden" name="_token" value="{{ $csrfToken }}">
<input type="hidden" name="action" value="createPage">

<div class="modal-body">
Expand Down Expand Up @@ -242,6 +245,7 @@
<div class="d-flex justify-content-end">
@if($dashboard->isInteractive())
<form class="buttonActionForm" action="" method="POST">
<input type="hidden" name="_token" value="{{ $csrfToken }}">
<input type="hidden" name="action" value="openPageInEditor">
<input type="hidden" name="routeKey" value="{{ $route->getRouteKey() }}">
<button type="submit" class="btn btn-outline-primary btn-sm me-2" title="Open in system default application">Edit</button>
Expand Down Expand Up @@ -302,6 +306,7 @@
@if($dashboard->isInteractive())
<div class="w-auto ps-0">
<form class="buttonActionForm" action="" method="POST">
<input type="hidden" name="_token" value="{{ $csrfToken }}">
<input type="hidden" name="action" value="openMediaFileInEditor">
<input type="hidden" name="identifier" value="{{ $mediaFile->getIdentifier() }}">
<button type="submit" class="btn btn-link btn-sm py-0" title="Open this image in the system editor">Edit</button>
Expand Down
44 changes: 44 additions & 0 deletions packages/realtime-compiler/src/Http/BaseController.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

namespace Hyde\RealtimeCompiler\Http;

use BadMethodCallException;
use Desilva\Microserve\Request;
use Desilva\Microserve\Response;
use Desilva\Microserve\JsonResponse;
Expand Down Expand Up @@ -76,6 +77,14 @@ protected function authorizePostRequest(): void
if (! $this->isRequestMadeFromLocalhost()) {
throw new HttpException(403, "Refusing to serve request from address {$_SERVER['REMOTE_ADDR']} (must be on localhost)");
}

if ($this->withSession) {
if (! $this->validateCSRFToken($this->request->get('_token'))) {
throw new HttpException(403, 'Invalid CSRF token');
} else {
$this->expireCSRFToken();
}
}
}

protected function isRequestMadeFromLocalhost(): bool
Expand All @@ -91,6 +100,41 @@ protected function isRequestMadeFromLocalhost(): bool
return in_array($requestIp, $allowedIps, true);
}

protected function generateCSRFToken(): string
{
if (session_status() !== PHP_SESSION_ACTIVE) {
throw new BadMethodCallException('Session not started');
}

if (empty($_SESSION['csrf_token'])) {
$_SESSION['csrf_token'] = bin2hex(random_bytes(32));
}

return $_SESSION['csrf_token'];
}

protected function validateCSRFToken(?string $suppliedToken): bool
{
if (session_status() !== PHP_SESSION_ACTIVE) {
throw new BadMethodCallException('Session not started');
}

if ($suppliedToken === null) {
return false;
}

return ! empty($_SESSION['csrf_token']) && hash_equals($_SESSION['csrf_token'], $suppliedToken);
}

protected function expireCSRFToken(): void
{
if (session_status() !== PHP_SESSION_ACTIVE) {
throw new BadMethodCallException('Session not started');
}

unset($_SESSION['csrf_token']);
}

protected function writeToConsole(string $message, string $context = 'dashboard'): void
{
if (isset($this->console)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ public function handle(): Response
protected function show(): string
{
return AnonymousViewCompiler::handle(__DIR__.'/../../resources/dashboard.blade.php', array_merge(
(array) $this, ['dashboard' => $this, 'request' => $this->request],
(array) $this, ['dashboard' => $this, 'request' => $this->request, 'csrfToken' => $this->generateCSRFToken()],
));
}

Expand Down

0 comments on commit f06ff3a

Please sign in to comment.