csrf
csrf copied to clipboard
Add Custom Request Headers for AJAX/API
https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html#employing-custom-request-headers-for-ajaxapi
By fact, we need two logic for CSRF middleware:
- classic (already implemented)
- alternative with check header only.
Suggest create yet another middleware. Something like that:
final class CsrfHeaderMiddleware implements MiddlewareInterface
{
public const HEADER_NAME = 'X-CSRF-Token';
private string $headerName = self::HEADER_NAME;
private ResponseFactoryInterface $responseFactory;
private ?RequestHandlerInterface $failureHandler;
public function __construct(
ResponseFactoryInterface $responseFactory,
RequestHandlerInterface $failureHandler = null
) {
$this->responseFactory = $responseFactory;
$this->failureHandler = $failureHandler;
}
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
{
if ($this->validateCsrfToken($request)) {
return $handler->handle($request);
}
if ($this->failureHandler !== null) {
return $this->failureHandler->handle($request);
}
$response = $this->responseFactory->createResponse(Status::UNPROCESSABLE_ENTITY);
$response
->getBody()
->write(Status::TEXTS[Status::UNPROCESSABLE_ENTITY]);
return $response;
}
public function withHeaderName(string $name): self
{
$new = clone $this;
$new->headerName = $name;
return $new;
}
public function getHeaderName(): string
{
return $this->headerName;
}
private function validateCsrfToken(ServerRequestInterface $request): bool
{
if (in_array($request->getMethod(), [Method::GET, Method::HEAD, Method::OPTIONS], true)) {
return true;
}
$headers = $request->getHeader($this->headerName);
return (bool) count($headers);
}
}
What do you think?
Separate middleware makes sense. Could be used per-route.
What's next? Is it approved solution and who will make the PR?
What's next? Is it approved solution and who will make the PR?
If possible, you can do a PR.