SameSiteCookieMiddleware.php 2.3 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788
  1. <?php
  2. /**
  3. * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
  4. * SPDX-License-Identifier: AGPL-3.0-or-later
  5. */
  6. namespace OC\AppFramework\Middleware\Security;
  7. use OC\AppFramework\Http\Request;
  8. use OC\AppFramework\Middleware\Security\Exceptions\LaxSameSiteCookieFailedException;
  9. use OC\AppFramework\Utility\ControllerMethodReflector;
  10. use OCP\AppFramework\Http;
  11. use OCP\AppFramework\Http\Response;
  12. use OCP\AppFramework\Middleware;
  13. class SameSiteCookieMiddleware extends Middleware {
  14. /** @var Request */
  15. private $request;
  16. /** @var ControllerMethodReflector */
  17. private $reflector;
  18. public function __construct(Request $request,
  19. ControllerMethodReflector $reflector) {
  20. $this->request = $request;
  21. $this->reflector = $reflector;
  22. }
  23. public function beforeController($controller, $methodName) {
  24. $requestUri = $this->request->getScriptName();
  25. $processingScript = explode('/', $requestUri);
  26. $processingScript = $processingScript[count($processingScript) - 1];
  27. if ($processingScript !== 'index.php') {
  28. return;
  29. }
  30. $noSSC = $this->reflector->hasAnnotation('NoSameSiteCookieRequired');
  31. if ($noSSC) {
  32. return;
  33. }
  34. if (!$this->request->passesLaxCookieCheck()) {
  35. throw new LaxSameSiteCookieFailedException();
  36. }
  37. }
  38. public function afterException($controller, $methodName, \Exception $exception) {
  39. if ($exception instanceof LaxSameSiteCookieFailedException) {
  40. $response = new Response();
  41. $response->setStatus(Http::STATUS_FOUND);
  42. $response->addHeader('Location', $this->request->getRequestUri());
  43. $this->setSameSiteCookie();
  44. return $response;
  45. }
  46. throw $exception;
  47. }
  48. protected function setSameSiteCookie() {
  49. $cookieParams = $this->request->getCookieParams();
  50. $secureCookie = ($cookieParams['secure'] === true) ? 'secure; ' : '';
  51. $policies = [
  52. 'lax',
  53. 'strict',
  54. ];
  55. // Append __Host to the cookie if it meets the requirements
  56. $cookiePrefix = '';
  57. if ($cookieParams['secure'] === true && $cookieParams['path'] === '/') {
  58. $cookiePrefix = '__Host-';
  59. }
  60. foreach ($policies as $policy) {
  61. header(
  62. sprintf(
  63. 'Set-Cookie: %snc_sameSiteCookie%s=true; path=%s; httponly;' . $secureCookie . 'expires=Fri, 31-Dec-2100 23:59:59 GMT; SameSite=%s',
  64. $cookiePrefix,
  65. $policy,
  66. $cookieParams['path'],
  67. $policy
  68. ),
  69. false
  70. );
  71. }
  72. }
  73. }