C, PHP, VB, .NET

Дневникът на Филип Петров


* SameSite cookies

Публикувано на 12 май 2020 в раздел ОСУП.

С приемането на RFC6265 и имплементирането на технологията в модерните браузъри, се прие нов начин за справяне с Cross Site Request Forgery. Идеята е да се добави възможност за рестрикция на използването на бисквитка (cookie) от страна на браузъра при заявка, която е генерирана чрез препращане от чужд сайт (cross site request). Досега ако например siteA накара браузъра на клиента (независимо дали автоматично чрез JavaScript или по волята на самия потребител натискайки линк/бутон) да направи GET или POST заявка към siteB и потребителят има записана бисквитка в siteB, тази бисквитка се предаваше и се използваше от siteB при изпълнение на въпросната заявка. Например ако потребителят например е логнат и има отворена сесия в siteB, той ще продължи да е логнат и при cross site заявки. С нововедението за атрибут SameSite на бисквитките се предоставя възможност това да не се случва.

В PHP досега бисквитките се създава чрез функцията setcookie, на която се подаваха серия параметри. Например в пълния си вид (без пропуснат параметър) се правеше следното:

setcookie("user","ivan", time()+3600, "/secure/", "users.cphpvb.net", true, true);

Първият параметър е името на променливата, втората е стойността, след това е времето за валидност, последвано от директория, домейн, задължение за използване на TLS и дали да е валидно само за HTTP (да не може да се предава през други протоколи, например с JavaScript). От PHP 7.3 нататък е въведен алтернативен синтаксис на същата функция, с който се предоставя възможност за изреждането на параметрите от третия нататък като масив, като именно сред елементите на масива е добавена възможност да се подаде и новата директива "samesite":

setcookie("user", "ivan",
          [ "expires"  => time()+3600,
            "path"     => "/secure/",
            "secure"   => true,
            "httponly" => true,
            "domain"   => "users.cphpvb.net",
            "samesite" => "Lax"
          ]);

Всеки един от тези параметри може да бъде пропуснат, с което ще се вземе негова стойност по подразбиране. От интерес за тази статия разбира се е новият "samesite". Той предоставя следните три възможности за своя стойност:

  • None: бисквитката ще се използва при всякакви заявки, които идват от чужд домейн;
  • Strict: не се позволява употребата на бисквитката при никакви заявки, които идват от чужд домейн;
  • Lax: бисквитката ще се използва само при стандартни GET заявки (например при последване на хипервръзка от чужд сайт), но не и други (например няма да се ползва при POST, XMLHttpRequest в JavaScript, и др.).

Режимът Strict е най-сигурен, но с него може да се наруши възможността за хората да споделят вътрешни връзки в уебсайта. Представете си например, че дадете връзка в някакъв интернет форум към интерестен пост във Фейсбук. Знаем, че за да се прочете дадения пост, трябва човекът да е логнат във Фейсбук, т.е. да има стартирана сесия - той трябва да си подаде бисквитката за достъп до акаунта си преди да прочете статията. Ако човекът, който отваря връзката, има сесийно cookie във Фейсбук, но то е обозначено със "Strict" решим, последвайки връзката от форума (домейн, който не е facebook.com) браузърът му няма да предостави бисквитката си към Фейсбук и така за пред социалната мрежа при тази заявка ще излезне, че не е логнат. Тоест ако в този хипотетичен пример (естествено, че Фейсбук няма да започнат да използват Strict бисквитки) няма да може да се прочете статията, а вместо това потребителят ще бъде пренасочен към страница за попълване на име и парола за вход. Естествено, че това не би било удобно за потребителите в този случай. Затова със сигурност при подобни услуги, където се очаква потребители да разменят вътрешни връзки, със сигурност този режим не трябва да се използва. Той би било полезен само за строго специфични нужди - например административен панел на специфична система, - за които не се очаква хората да си споделят връзки във външни системи.

Lax в общи линии е режимът, който се очаква да се използва най-масово. При него бисквитките няма да се използват при заявки идващи от HTML форми с POST или през  JavaScript, но стандартните хипервръзки (<a href=...>) ще работят нормално както е било винаги досега. Следвайки философията, че GET заявките са само за четене на информация, а POST заявките са за извършване на промяна (нещо, което за съжаление не се спазва винаги), това би спряло нежелани Cross Site Request Forgery заявки, но без да се чупи стандартната функционалност на съществуващите уебсайтове.

Въпреки, че технологията беше създадена отдавна и всички модерни браузъри я поддържат от няколко години, практиката показа, че разработчиците се възползваха много рядко от тези нововъведения. Затова с версия 80 на Chrome (февруари 2020 г.) беше въведена тестово промяна в поведението на браузъра при третирането на бисквитки, които нямат указан samesite - те са приемани Lax, вместо като None. Епидемията от Covid-19 наложи това да бъде върнато обратно при версия 81, но от Google засега обещават, че нововъведението ще се имплементира отново в близко бъдеще. От Firefox потвърждават подобни планове, а от Microsoft Edge също започват свои тестове от версия 80 нататък.

Паралелно с това тече и засилено желание на големите компании за все по-масово преминаване към HTTPS. Поради тази причина основно Google Chrome, но също така подкрепени от Mozilla Firefox, смятат да направят и допълнителна забрана - да не може да се използват samesite="None" бисквитки по некриптиран канал, т.е. комбинацията samesite="None" и secure=false да бъде забранена. Не е напълно ясно какво точно ще се наложи като масов стандарт, но засега изгледите са следните:

  • Ако не е указано изрично, samesite атрибутът на бисквитките ще бъде приеман от браузърите по подразбиране "Lax";
  • По HTTP (некриптиран) канал няма да бъде възможно да се създава бисквитка с атрибут samesite със стойност "None". Казано по друг начин - ако създавате бисквитка със samesite="None", то тя трябва задължително да има атрибут secure=true.

Дали двете обещания ще станат реалност и ще се утвърдят в практиката, не е напълно гарантирано, но засега браузърите уведомяват потребителите със съобщение, когато второто не е спазено. Например в момента при стандартна инсталация и стартиране на сесия в PHP по HTTP канал без да е добавен samesite атрибут, в Mozilla Firefox ще се изведе следното съобщение:

Cookie “PHPSESSID” will be soon rejected because it has the “sameSite” attribute set to “none” or an invalid value, without the “secure” attribute. To know more about the “sameSite“ attribute, read https://developer.mozilla.org/docs/Web/HTTP/Cookies

При всички положения е силно препоръчително при създаване на бисквитки от тук насетне винаги да указвате samesite атрибута. Специално при сесийните cookies в PHP това се прави с отделна функция - session_set_cookie_params, - която трябва да се извика непосредствено преди session_start() по следния начин:

session_set_cookie_params(["samesite" => "Lax"]);
session_start();

Разбира се бихте могли да указвате и допълнителни параметри за сесийната бисквитка - същите са както и при обикновените, които бяха изредени по-горе. Ако не желаете да извиквате всеки път тази настройка ръчно, можете да настроите следното през php.ini:

session.cookie_samesite=Lax

Това ще накара PHP винаги да създава сесийните cookies като Lax (освен ако не укажете друго сами). Стандартната настройка в php.ini е да не е зададена стойност, т.е. PHP оставя тази отговорност (какви да са по подразбиране) на браузъра на клиента.

Наистина правилната употреба на samesite атрибут на бисквитките би разрешила основният проблем с Cross Site Request Forgery атаките. Въпреки това аз лично бих ви препоръчал да не се отказвате от утвърдените и изпитани в практиката защити - създавайте и проверявайте xsrftoken при всички важни заявки и не се доверявайте изцяло на защитата на сайта (към този тип уязвимости) на браузъра на клиента (при samesite бисквитките всъщност се случва точно това). Приемете samesite атрибутът на бисквитките като добра допълнителна защита.

 



Добави коментар

Адресът на електронната поща няма да се публикува


*