attribute = $attribute ?? self::ATTRIBUTE; $this->ttl = $ttl ?? self::TTL; $this->tokenLength = $tokenLength ?? self::CSRF_TOKEN_LENGTH; if (!is_callable($errorResponse)) { if (!$errorResponse instanceof ResponseInterface) { if (!is_string($errorResponse)) { $errorResponse = self::DEFAULT_ERROR_RESPONSE_STRING; } $errorResponse = new Response(400, ['content-type' => 'text/plain'], $errorResponse); } $errorResponse = function (ServerRequestInterface $request) use ($errorResponse) { return $errorResponse; }; } $this->errorResponse = $errorResponse; if (!$logger instanceof LoggerInterface) { $logger = new NullLogger(); } $this->logger = $logger; } public function setLogger(LoggerInterface $logger) { $this->logger = $logger; } public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface { // Generate a new CSRF token, add it to the request attributes, and as a cookie on the response. $csrfToken = generateRandomPrintableAsciiString($this->tokenLength); $request = $request->withAttribute($this->attribute, $csrfToken); if (!in_array(strtoupper($request->getMethod()), self::READ_METHODS) && !$this->isValid($request)) { // This request is a write method with invalid CSRF parameters. $response = call_user_func($this->errorResponse, $request); } else { $response = $handler->handle($request); } // Add the new CSRF cookie, restricting its scope to match the current request. $response = FigCookies\FigResponseCookies::set($response, FigCookies\SetCookie::create($this->attribute) ->withValue($csrfToken) ->withMaxAge($this->ttl) ->withSecure($request->getUri()->getScheme() == 'https') ->withDomain($request->getUri()->getHost()) ->withPath($request->getUri()->getPath())); return $response; } protected function isValid(ServerRequestInterface $request) { if (array_key_exists($this->attribute, $request->getParsedBody() ?? [])) { if (array_key_exists($this->attribute, $request->getCookieParams() ?? [])) { // TODO: make sure CSRF token isn’t the empty string, possibly also check that it’s the same length // as defined in $this->tokenLength. return hash_equals($request->getParsedBody()[$this->attribute], $request->getCookieParams()[$this->attribute]); } } return false; } }