src/Eccube/Session/Storage/Handler/SameSiteNoneCompatSessionHandler.php line 69

Open in your IDE?
  1. <?php
  2. /*
  3.  * This file is part of EC-CUBE
  4.  *
  5.  * Copyright(c) EC-CUBE CO.,LTD. All Rights Reserved.
  6.  *
  7.  * http://www.ec-cube.co.jp/
  8.  *
  9.  * For the full copyright and license information, please view the LICENSE
  10.  * file that was distributed with this source code.
  11.  */
  12. namespace Eccube\Session\Storage\Handler;
  13. use Skorp\Dissua\SameSite;
  14. use Symfony\Component\HttpFoundation\Cookie;
  15. use Symfony\Component\HttpFoundation\Request;
  16. use Symfony\Component\HttpFoundation\Session\Storage\Handler\StrictSessionHandler;
  17. class SameSiteNoneCompatSessionHandler extends StrictSessionHandler
  18. {
  19.     /** @var \SessionHandlerInterface */
  20.     private $handler;
  21.     /** @var bool */
  22.     private $doDestroy;
  23.     /** @var string */
  24.     private $sessionName;
  25.     /** @var string|null */
  26.     private $prefetchId;
  27.     /** @var string|null */
  28.     private $prefetchData;
  29.     /** @var string */
  30.     private $newSessionId;
  31.     /** @var string|null */
  32.     private $igbinaryEmptyData;
  33.     /**
  34.      *  {@inheritdoc}
  35.      */
  36.     public function __construct(\SessionHandlerInterface $handler)
  37.     {
  38.         $this->handler $handler;
  39.         ini_set('session.cookie_secure'$this->getCookieSecure());
  40.         ini_set('session.cookie_samesite'$this->getCookieSameSite());
  41.         ini_set('session.cookie_path'$this->getCookiePath());
  42.     }
  43.     /**
  44.      * {@inheritdoc}
  45.      */
  46.     public function open($savePath$sessionName)
  47.     {
  48.         $this->sessionName $sessionName;
  49.         // see https://github.com/symfony/symfony/blob/2adc85d49cbe14e346068fa7e9c2e1f08ab31de6/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/AbstractSessionHandler.php#L35-L37
  50.         if (!headers_sent() && !ini_get('session.cache_limiter') && '0' !== ini_get('session.cache_limiter')) {
  51.             header(sprintf('Cache-Control: max-age=%d, private, must-revalidate'60 * (int) ini_get('session.cache_expire')));
  52.         }
  53.         return $this->handler->open($savePath$sessionName);
  54.     }
  55.     /**
  56.      * {@inheritdoc}
  57.      */
  58.     protected function doRead($sessionId)
  59.     {
  60.         return $this->handler->read($sessionId);
  61.     }
  62.     /**
  63.      * {@inheritdoc}
  64.      */
  65.     public function updateTimestamp($sessionId$data)
  66.     {
  67.         return $this->write($sessionId$data);
  68.     }
  69.     /**
  70.      * {@inheritdoc}
  71.      */
  72.     protected function doWrite($sessionId$data)
  73.     {
  74.         return $this->handler->write($sessionId$data);
  75.     }
  76.     /**
  77.      * {@inheritdoc}
  78.      * @see https://github.com/symfony/symfony/blob/2adc85d49cbe14e346068fa7e9c2e1f08ab31de6/src/Symfony/Component/HttpFoundation/Session/Storage/Handler/AbstractSessionHandler.php#L126-L167
  79.      */
  80.     public function destroy($sessionId)
  81.     {
  82.         if (\PHP_VERSION_ID 70000) {
  83.             $this->prefetchData null;
  84.         }
  85.         if (!headers_sent() && filter_var(ini_get('session.use_cookies'), FILTER_VALIDATE_BOOLEAN)) {
  86.             if (!$this->sessionName) {
  87.                 throw new \LogicException(sprintf('Session name cannot be empty, did you forget to call "parent::open()" in "%s"?.', \get_class($this)));
  88.             }
  89.             $sessionCookie sprintf(' %s='urlencode($this->sessionName));
  90.             $sessionCookieWithId sprintf('%s%s;'$sessionCookieurlencode($sessionId));
  91.             $sessionCookieFound false;
  92.             $otherCookies = [];
  93.             foreach (headers_list() as $h) {
  94.                 if (!== stripos($h'Set-Cookie:')) {
  95.                     continue;
  96.                 }
  97.                 if (11 === strpos($h$sessionCookie11)) {
  98.                     $sessionCookieFound true;
  99.                     if (11 !== strpos($h$sessionCookieWithId11)) {
  100.                         $otherCookies[] = $h;
  101.                     }
  102.                 } else {
  103.                     $otherCookies[] = $h;
  104.                 }
  105.             }
  106.             if ($sessionCookieFound) {
  107.                 header_remove('Set-Cookie');
  108.                 foreach ($otherCookies as $h) {
  109.                     header($hfalse);
  110.                 }
  111.             } else {
  112.                 if (\PHP_VERSION_ID 70300) {
  113.                     setcookie($this->sessionName''0ini_get('session.cookie_path'), ini_get('session.cookie_domain'), filter_var(ini_get('session.cookie_secure'), FILTER_VALIDATE_BOOLEAN), filter_var(ini_get('session.cookie_httponly'), FILTER_VALIDATE_BOOLEAN));
  114.                 } else {
  115.                     setcookie($this->sessionName'',
  116.                               [
  117.                                   'expires' => 0,
  118.                                   'path' => $this->getCookiePath(),
  119.                                   'domain' => ini_get('session.cookie_domain'),
  120.                                   'secure' => filter_var(ini_get('session.cookie_secure'), FILTER_VALIDATE_BOOLEAN),
  121.                                   'httponly' => filter_var(ini_get('session.cookie_httponly'), FILTER_VALIDATE_BOOLEAN),
  122.                                   'samesite' => $this->getCookieSameSite(),
  123.                               ]
  124.                     );
  125.                 }
  126.             }
  127.         }
  128.         return $this->newSessionId === $sessionId || $this->doDestroy($sessionId);
  129.     }
  130.     /**
  131.      * {@inheritdoc}
  132.      */
  133.     protected function doDestroy($sessionId)
  134.     {
  135.         $this->doDestroy false;
  136.         return $this->handler->destroy($sessionId);
  137.     }
  138.     /**
  139.      * {@inheritdoc}
  140.      */
  141.     public function close()
  142.     {
  143.         return $this->handler->close();
  144.     }
  145.     /**
  146.      * @return bool
  147.      */
  148.     public function gc($maxlifetime)
  149.     {
  150.         return $this->handler->gc($maxlifetime);
  151.     }
  152.     /**
  153.      * @return string
  154.      */
  155.     public function getCookieSameSite()
  156.     {
  157.         if ($this->shouldSendSameSiteNone() && \PHP_VERSION_ID >= 70300 && $this->getCookieSecure()) {
  158.             return Cookie::SAMESITE_NONE;
  159.         }
  160.         return '';
  161.     }
  162.     /**
  163.      * @return string
  164.      */
  165.     public function getCookiePath()
  166.     {
  167.         $cookiePath env('ECCUBE_COOKIE_PATH''/');
  168.         if ($this->shouldSendSameSiteNone() && \PHP_VERSION_ID 70300 && $this->getCookieSecure()) {
  169.             return $cookiePath.'; SameSite='.Cookie::SAMESITE_NONE;
  170.         }
  171.         return $cookiePath;
  172.     }
  173.     /**
  174.      * @return string
  175.      */
  176.     public function getCookieSecure()
  177.     {
  178.         $request Request::createFromGlobals();
  179.         return $request->isSecure() ? '1' '0';
  180.     }
  181.     /**
  182.      * @return bool
  183.      */
  184.     private function shouldSendSameSiteNone()
  185.     {
  186.         $userAgent array_key_exists('HTTP_USER_AGENT'$_SERVER) ? $_SERVER['HTTP_USER_AGENT'] : null;
  187.         return SameSite::handle($userAgent);
  188.     }
  189. }