@@ -480,6 +480,70 @@ public static function setRequiredIniValues() {
480480 @ini_set ('gd.jpeg_ignore_warning ' , 1 );
481481 }
482482
483+ /**
484+ * Send the same site cookies
485+ */
486+ private static function sendSameSiteCookies () {
487+ $ cookieParams = session_get_cookie_params ();
488+ $ secureCookie = ($ cookieParams ['secure ' ] === true ) ? 'secure; ' : '' ;
489+ $ policies = [
490+ 'lax ' ,
491+ 'strict ' ,
492+ ];
493+ foreach ($ policies as $ policy ) {
494+ header (
495+ sprintf (
496+ 'Set-Cookie: nc_sameSiteCookie%s=true; path=%s; httponly; ' . $ secureCookie . 'expires=Fri, 31-Dec-2100 23:59:59 GMT; SameSite=%s ' ,
497+ $ policy ,
498+ $ cookieParams ['path ' ],
499+ $ policy
500+ ),
501+ false
502+ );
503+ }
504+ }
505+
506+ /**
507+ * Same Site cookie to further mitigate CSRF attacks. This cookie has to
508+ * be set in every request if cookies are sent to add a second level of
509+ * defense against CSRF.
510+ *
511+ * If the cookie is not sent this will set the cookie and reload the page.
512+ * We use an additional cookie since we want to protect logout CSRF and
513+ * also we can't directly interfere with PHP's session mechanism.
514+ */
515+ private static function performSameSiteCookieProtection () {
516+ if (count ($ _COOKIE ) > 0 ) {
517+ $ request = \OC ::$ server ->getRequest ();
518+ $ requestUri = $ request ->getScriptName ();
519+ $ processingScript = explode ('/ ' , $ requestUri );
520+ $ processingScript = $ processingScript [count ($ processingScript )-1 ];
521+
522+ // For the "index.php" endpoint only a lax cookie is required.
523+ if ($ processingScript === 'index.php ' ) {
524+ if (!$ request ->passesLaxCookieCheck ()) {
525+ self ::sendSameSiteCookies ();
526+ header ('Location: ' .$ _SERVER ['REQUEST_URI ' ]);
527+ exit ();
528+ }
529+ } else {
530+ // All other endpoints require the lax and the strict cookie
531+ if (!$ request ->passesStrictCookieCheck ()) {
532+ self ::sendSameSiteCookies ();
533+ // Debug mode gets access to the resources without strict cookie
534+ // due to the fact that the SabreDAV browser also lives there.
535+ if (!\OC ::$ server ->getConfig ()->getSystemValue ('debug ' , false )) {
536+ http_response_code (\OCP \AppFramework \Http::STATUS_SERVICE_UNAVAILABLE );
537+ exit ();
538+ }
539+ }
540+ }
541+ } elseif (!isset ($ _COOKIE ['nc_sameSiteCookielax ' ]) || !isset ($ _COOKIE ['nc_sameSiteCookiestrict ' ])) {
542+ self ::sendSameSiteCookies ();
543+ }
544+ }
545+
546+
483547 public static function init () {
484548 // calculate the root directories
485549 OC ::$ SERVERROOT = str_replace ("\\" , '/ ' , substr (__DIR__ , 0 , -4 ));
@@ -589,6 +653,8 @@ public static function init() {
589653 ini_set ('session.cookie_secure ' , true );
590654 }
591655
656+ self ::performSameSiteCookieProtection ();
657+
592658 if (!defined ('OC_CONSOLE ' )) {
593659 $ errors = OC_Util::checkServer (\OC ::$ server ->getConfig ());
594660 if (count ($ errors ) > 0 ) {
0 commit comments