Фиксиране/отвличане на PHP сесия

Опитвам се да разбера повече за PHP Фиксиране на сесии и отвличане и как да ги предотвратя проблеми. Четох следните две статии на уебсайта на Крис Шифлет:

Не съм сигурен обаче, че разбирам нещата правилно.

За да предотвратите фиксирането на сесията, достатъчно ли е да извикате session_regenerate_id(true); след успешно влизане на някого? Мисля, че го разбирам правилно.

Той също така говори за използване на токени, предадени в url адреси чрез $_GET, за предотвратяване на отвличане на сесия. Как точно ще стане това? Предполагам, че когато някой влезе, вие генерирате неговия токен и го съхранявате в променлива на сесията, след което на всяка страница ще сравнявате тази променлива на сесията със стойността на променливата $_GET?

Ще трябва ли този маркер да се променя само веднъж на сесия или при всяко зареждане на страница?

Също така има ли добър начин за предотвратяване на отвличане, без да се налага да предавате стойност в URL адресите? Това би било много по-лесно.


person me2    schedule 22.02.2011    source източник
comment
Може би бихте могли да добавите връзки към страниците, където сте намерили тези препоръки.   -  person Gumbo    schedule 22.02.2011


Отговори (5)


Добре, има два отделни, но свързани проблема и всеки се третира по различен начин.

Фиксиране на сесия

Това е мястото, където нападателят изрично задава идентификатора на сесията за даден потребител. Обикновено в PHP това се прави, като им се даде url като http://www.example.com/index...?session_name=sessionid. След като атакуващият даде URL адреса на клиента, атаката е същата като атака за отвличане на сесия.

Има няколко начина за предотвратяване на фиксирането на сесия (направете всички):

  • Задайте session.use_trans_sid = 0 във вашия php.ini файл. Това ще каже на PHP да не включва идентификатора в URL адреса и да не чете URL адреса за идентификатори.

  • Задайте session.use_only_cookies = 1 във вашия php.ini файл. Това ще каже на PHP никога да не използва URL адреси с идентификатори на сесии.

  • Генерирайте отново идентификатора на сесията всеки път, когато състоянието на сесията се промени. Това означава някое от следните:

    • User authentication
    • Съхраняване на чувствителна информация в сесията
    • Промяна на нещо относно сесията
    • и т.н...

Отвличане на сесия

Това е мястото, където нападателят получава идентификатор на сесия и може да изпраща заявки, сякаш е този потребител. Това означава, че тъй като нападателят има идентификатора, те са почти неразличими от валидния потребител по отношение на сървъра.

Не можете директно да предотвратите отвличането на сесия. Можете обаче да поставите стъпки, за да го направите много трудно и по-трудно за използване.

  • Използвайте силен хеш идентификатор на сесията: session.hash_function в php.ini. Ако PHP ‹ 5.3, задайте го на session.hash_function = 1 за SHA1. Ако PHP >= 5.3, задайте го на session.hash_function = sha256 или session.hash_function = sha512.

  • Изпратете силен хеш: session.hash_bits_per_character в php.ini . Задайте това на session.hash_bits_per_character = 5. Въпреки че това не го прави по-трудно за кракване, има значение, когато атакуващият се опита да отгатне идентификатора на сесията. ID ще бъде по-кратък, но ще използва повече знаци.

  • Задайте допълнителна ентропия с session.entropy_file и session.entropy_length във вашия php.ini файл. Задайте първото на session.entropy_file = /dev/urandom, а второто на броя байтове, които ще бъдат прочетени от ентропийния файл, например session.entropy_length = 256.

  • Променете името на сесията от PHPSESSID по подразбиране. Това се постига чрез извикване на session_name() с вашето собствено име на идентификатор като първи параметър преди извикването session_start.

  • Ако наистина сте параноик, можете също да завъртите името на сесията, но внимавайте, че всички сесии автоматично ще бъдат анулирани, ако промените това (например, ако го направите зависимо от часа). Но в зависимост от вашия случай на употреба, това може да е опция...

  • Редувайте често идентификатора на вашата сесия. Не бих направил това при всяка заявка (освен ако наистина не се нуждаете от това ниво на сигурност), но на произволен интервал. Искате да променяте това често, тъй като ако нападател отвлече сесия, не искате той да може да я използва твърде дълго.

  • Включете потребителския агент от $_SERVER['HTTP_USER_AGENT'] в сесията. По принцип, когато сесията започне, запазете я в нещо като $_SESSION['user_agent']. След това при всяка следваща заявка проверявайте дали съвпада. Имайте предвид, че това може да бъде фалшифицирано, така че да не е 100% надеждно, но е по-добре, отколкото не.

  • Включете IP адреса на потребителя от $_SERVER['REMOTE_ADDR'] в сесията. По принцип, когато сесията започне, запазете я в нещо като $_SESSION['remote_ip']. Това може да е проблематично от някои интернет доставчици, които използват множество IP адреси за своите потребители (като AOL преди). Но ако го използвате, ще бъде много по-сигурно. Единственият начин нападателят да фалшифицира IP адреса е да компрометира мрежата в някакъв момент между истинския потребител и вас. И ако компрометират мрежата, те могат да направят много по-лошо от отвличане (като MITM атаки и т.н.).

  • Включете токен в сесията и от страната на браузърите, който увеличавате и сравнявате често. По принцип за всяка заявка направете $_SESSION['counter']++ от страната на сървъра. Също така направете нещо в JS от страната на браузърите, за да направите същото (използвайки локално хранилище). След това, когато изпратите заявка, просто вземете еднократен номер от токен и проверете дали еднократният номер е същият на сървъра. Правейки това, трябва да сте в състояние да откриете отвлечена сесия, тъй като нападателят няма да разполага с точния брояч, или ако го има, ще имате 2 системи, предаващи един и същ брой и можете да кажете, че едната е фалшифицирана. Това няма да работи за всички приложения, но е един от начините за справяне с проблема.

Бележка за двете

Разликата между фиксирането на сесията и отвличането е само в това как идентификаторът на сесията е компрометиран. При фиксиране идентификаторът се задава на стойност, която нападателят знае предварително. При Hijacking е или познато, или откраднато от потребителя. В противен случай ефектите от двете са еднакви, след като идентификаторът е компрометиран.

Регенериране на идентификатор на сесия

Всеки път, когато генерирате повторно идентификатора на сесията с помощта на session_regenerate_id старата сесия трябва да бъде изтрита. Това се случва прозрачно с манипулатора на основната сесия. Въпреки това, някои манипулатори на персонализирани сесии, използващи session_set_save_handler(), не правят това и са отворени за атака на стари идентификатори на сесии. Уверете се, че ако използвате персонализиран манипулатор на сесии, че следите идентификатора, който отваряте, и ако не е същият, който запазвате, че изрично изтривате (или променяте) идентификатора на стария.

Използвайки манипулатора на сесията по подразбиране, можете просто да извикате session_regenerate_id(true). Това ще премахне старата информация за сесията вместо вас. Старият идентификатор вече не е валиден и ще доведе до създаване на нова сесия, ако атакуващият (или някой друг по този въпрос) се опита да го използва. Все пак внимавайте с манипулаторите на персонализирани сесии....

Унищожаване на сесия

Ако ще унищожите сесия (при излизане например), уверете се, че сте я унищожили напълно. Това включва премахване на бисквитката. Използвайки session_destroy:

function destroySession() {
    $params = session_get_cookie_params();
    setcookie(session_name(), '', time() - 42000,
        $params["path"], $params["domain"],
        $params["secure"], $params["httponly"]
    );
    session_destroy();
}
person ircmaxell    schedule 22.02.2011
comment
Използването на 5 вместо 4 бита на знак не променя „силата“ по никакъв начин (каквото и да означава „сила“ в този случай). Но въпреки че вашите точки като цяло са препоръчителни, в тях липсват някои важни подробности. Например какво се случва със сесията, която е свързана със стария идентификатор на сесия или как трябва да се обработва сесия със стар идентификатор на сесия, след като стане невалиден. - person Gumbo; 22.02.2011
comment
@Gumbo Не можа да получи последното ви изречение напълно, но няма ли стара сесия да мигрира към новата в случай на повторно генериране на идентификатор; и какъв е проблема с невалидна сесия? - person Halil Özgür; 22.02.2011
comment
@battal: Не, това е смисълът. session_regenerate_id не обезсилва сесията, която все още е свързана със стария идентификатор; само ако параметърът delete_old_session е зададен на true, сесията ще бъде унищожена. Но какво ще стане, ако нападател инициира това повторно генериране на ID? - person Gumbo; 22.02.2011
comment
Не съм съгласен с повторното генериране на сесия всеки път, когато промените променлива на сесия, това трябва да се прави само при влизане/излизане. Също така проверката на потребителския агент е безсмислена и проверката на REMOTE_ADDR е проблематична. Едно нещо, което бих искал да добавя е session.entropy_file = /dev/urandom. Вътрешното генериране на ентропия на PHP е доказано изключително слабо и ентропийният пул, предоставен от /dev/random или /dev/uranom, е най-доброто, което можете да получите на уеб сървър без хардуерен rng. - person rook; 22.02.2011
comment
Също така трябва да добавите session.cookie_httponly и session.cookie_secure. Първият помага да осуети xss (но не е перфектен). Вторият е най-добрият начин да спрете OWASP A9... - person rook; 22.02.2011
comment
@Gumbo Ах, разбирам, продължава на преглед на страница. Това изглеждат като важни точки за всяко нетривиално приложение. Интересно преобразуване в секцията за коментари на session_regenerate_id страница с ръководство. Какво мислите за тях, особено за коментар 87905? - person Halil Özgür; 22.02.2011
comment
@ircmaxell Въпреки че това не го прави по-трудно за кракване, има значение, когато атакуващият се опита да отгатне идентификатора на сесията - каква разлика прави по отношение на сигурността, ако хеш ентропията е същата? И към началото на изречението Изпратете силен хеш - мисля, че няма нищо общо със силата на хеша, а само четливото му представяне. - person Halil Özgür; 22.02.2011
comment
@battal: Не виждам смисъл защо ИД на старата сесия и следователно старата сесия трябва да са валидни за още 60 секунди. Това е като да имате блиндирани врати, които се затварят автоматично едва 60 секунди след като сте ги подминали и разбиват всеки, който е минал след вас. Това би имало смисъл само да се използва за откриване на нападател (в случай на отвличане на сесия) или жертва (в случай на фиксиране на сесия). Но не можете да кажете кой кой е. - person Gumbo; 22.02.2011
comment
@Gumbo: Решението в тези коментари се опитва да върне старата сесия обратно на вече работещите скриптове (обикновено те нямат достъп до нея, докато скриптът, извършващ повторно генериране на идентификатор, не завърши). И тъй като те започнаха да работят преди промяната на сесията, трябва ли да го наречем vuln.? Няма ли да е двусмислено да стартирате половината от скрипт с някаква сесия, а другата половина с друга сесия (с изключение на push неща и т.н.)? - person Halil Özgür; 23.02.2011
comment
@battal: Не, новият идентификатор на сесията се използва след повторно генериране на идентификатора и данните за сесията в $_SESSION остават непроменени. Но честно казано, извикването на session_regenerate_id не записва данните за сесията на $_SESSION обратно в хранилището. Така че локалната промяна на $_SESSION['OBSOLETE'] и $_SESSION['EXPIRES'] променя само данните от текущата сесия с новогенерирания идентификатор, но не и стария. За да зададете тези стойности за старата сесия, по-скоро трябва да извикате session_write_close преди session_regenerate_id. - person Gumbo; 23.02.2011
comment
Можете също така да промените името на сесията по подразбиране (PHPSESSID), като използвате: session.name = blah вместо да извиквате session_name() - person Mike Causer; 17.08.2012
comment
@MikeCauser Но използването на session_name() е по-преносимо; представете си сървъри, които не предоставят достъп до php.ini или .htacess. - person Ja͢ck; 01.12.2012
comment
По-преносим, ​​да, но много по-бавен. Можех да разбера, че php.ini не е изложен в споделена среда, но със сигурност .htaccess ще бъде наличен - person Mike Causer; 03.12.2012
comment
@MikeCauser: по-бавно? Наистина ли? Ще приведеш ли този аргумент? Говорим за буквално микросекунди. И тъй като така или иначе никога няма да бъде в цикъл (не трябва, освен ако не правите нещо ужасно грешно). Бих използвал session_name() просто от гледна точка, че поддържа конфигурацията на приложението в приложението. - person ircmaxell; 03.12.2012
comment
Не разбирам толкова страхотен отговор, но липсва най-важното: използвайте SSL/HTTPS. Увеличаването на брояча е източник на проблем с множество заявки, бързи една след друга, потребител опреснява страница два пъти или натиска два пъти бутони за изпращане. Решението за IP адрес е проблем в днешно време за всички мобилни потребители и постоянно променящите се IP адреси. Можете да погледнете първия набор от IP, но все пак той изисква проблеми. Най-доброто е предотвратяването на откриването на идентификатора на сесията на първо място и това е използването на SSL/HTTPS. - person Sanne; 01.03.2013
comment
@Rook: Съгласен съм с вас, че сесията не трябва да се регенерира всеки път, тъй като това е излишно, но какво ще кажете за регенерирането само на идентификатора, използвайки session_regenerate_id(false), за да направи отгатването на SID по-трудно? - person Marco Sulla; 05.06.2013
comment
@Lucas Malor няма атака, която това да предотврати. Идентификаторът трябва да е достатъчно голям, за да предотврати грубата сила в нашия живот. Ако имате нарушение на owasp a9 или HTTP Response Splitting/XSS/CSRF/Clickjakcing, тогава повторното генериране на ID няма да помогне. - person rook; 05.06.2013
comment
@Rook: Разбира се, но ако имам сесия, която изтича след дълго време, да речем 2 седмици, както прави акаунтът в Google, нападателят има 2 седмици, за да се опита да получи моето SID. Това малко вероятно ли е според вас, ако имам SID, генериран от силна функция за хеширане, или някой може да опита? - person Marco Sulla; 05.06.2013
comment
@Lucas Malor Е, това зависи какво хеширате. Ако използвате мениджъра на сесиите на вашата платформа (което трябва да правите), тогава вероятно използвате /dev/urandom, което е sah1 хеш на ентропиен пул. В този случай вероятно ще ви трябват няколко хиляди години, за да познаете този идентификатор на сесията. - person rook; 05.06.2013
comment
Друг важен момент относно брояча и промяната му на клиента: бисквитката трябва да се направи HttpOnly... така че очевидно клиентският JavaScript няма достъп до бисквитката и не може да увеличи брояча. Би било разумно да обясните, че в този случай са необходими 2 бисквитки. - person Alexis Wilke; 24.12.2013
comment
Не са ли излишни 256 байта ентропия? 32 трябва да е достатъчно. 32 байта = 256 бита в крайна сметка. - person Scott Arciszewski; 30.05.2015
comment
какво ще кажете за session.use_strict_mode? - person Akam; 02.06.2016
comment
Строгият режим @Akam е превръзка за всичко останало, идентифицирано в публикацията. Ако регенерирате при ескалация на привилегии, както е посочено в публикацията, строгият режим буквално не прави нищо за вас. - person ircmaxell; 02.06.2016

И двете сесийни атаки имат една и съща цел: получаване на достъп до легитимна сесия на друг потребител. Но векторите на атака са различни:

  • При атака Session Fixation нападателят вече има достъп към валидна сесия и се опитва да принуди жертвата да използва тази конкретна сесия.

  • При атака отвличане на сесия нападателят се опитва да получи ID на сесията на жертвата, за да използвате неговата/нейната сесия.

И при двете атаки идентификаторът на сесията е чувствителната информация, върху която са фокусирани тези атаки. Така че идентификаторът на сесията трябва да бъде защитен както за достъп за четене (отвличане на сесия), така и за достъп за запис (фиксиране на сесия).

Общото правило за защита на чувствителни данни чрез използване на HTTPS важи и в този случай. Освен това трябва да направите следното:

За да предотвратите атаки на фиксиране на сесии, уверете се, че:

  • идентификаторът на сесията се приема само от бисквитка (задайте session.use_only_cookies до true) и го направете само за HTTPS, ако е възможно (задайте session.cookie_secure до true); можете да направите и двете с session_set_cookie_params.

За да предотвратите атаки отвличане на сесии, уверете се, че:

За да предотвратите атаки от двете сесии, уверете се, че:

  • да приемате само сесии, които вашето приложение е инициирало. Можете да направите това чрез отпечатване на сесия при започване със специфична за клиента информация. Можете да използвате ID на User-Agent, но не използвайте отдалечения IP адрес или друга информация, която може да се промени между заявките.
  • за промяна на идентификатора на сесията с помощта на session_regenerate_id(true) след опит за удостоверяване (true само при успех) или промяна на привилегиите и унищожаване старата сесия. (Уверете се, че сте съхранили всички промени на $_SESSION с помощта на session_write_close преди да генерирате повторно идентификатора, ако искате да запазите сесията, свързана със стария идентификатор; в противен случай само сесията с новия идентификатор ще бъде засегната от тези промени. )
  • за използване на правилно изпълнение на изтичане на сесията (вижте Как да изтека PHP сесия след 30 минути?).
person Gumbo    schedule 22.02.2011
comment
Страхотен пост, особено последния раздел. - person Mattis; 29.05.2011

Токените, които споменавате, са "nonce" - номер, използван веднъж. Не е задължително да се използват само веднъж, но колкото по-дълго се използват, толкова по-големи са шансовете, че nonce може да бъде уловен и използван за отвличане на сесията.

Друг недостатък на nonces е, че е много трудно да се изгради система, която ги използва и позволява множество паралелни прозорци на една и съща форма. напр. потребителят отваря два прозореца на форум и започва да работи върху две публикации:

window 'A' loads first and gets nonce 'P'
window 'B' loads second and gets nonce 'Q'

Ако нямате начин да проследявате няколко прозореца, ще имате съхранен само един nonce - този на прозорец B/Q. Когато след това потребителят изпрати своята публикация от прозорец A и подаде nonce 'P', тази система ще отхвърли публикацията като P != Q.

person Marc B    schedule 22.02.2011
comment
И така, какво общо има това с фиксирането на сесията? - person rook; 22.02.2011
comment
Той има валидна точка, особено в областта на използването на много AJAX заявки едновременно. - person DanielG; 18.04.2012

Не прочетох статията на Shiflett, но мисля, че сте разбрали нещо погрешно.

По подразбиране PHP предава токена на сесията в URL адреса всеки път, когато клиентът не приема бисквитки. Иначе в най-честия случай токенът на сесията се съхранява като бисквитка.

Това означава, че ако поставите токен на сесия в URL адреса, PHP ще го разпознае и ще се опита да го използва впоследствие. Фиксирането на сесия се случва, когато някой създаде сесия и след това подмами друг потребител да сподели същата сесия, като отвори URL адрес, който съдържа токена на сесията. Ако потребителят се удостовери по някакъв начин, злонамереният потребител след това знае токена на сесията на удостоверения, който може да има различни привилегии.

Както съм сигурен, че Shiflett обяснява, обичайното нещо, което трябва да направите, е да генерирате отново различен токен всеки път, когато привилегиите на даден потребител се променят.

person Andrea    schedule 22.02.2011
comment
За да добавите към това, моля, не забравяйте да унищожите всички отворени преди това сесии, тъй като те все още ще бъдат валидни със съществуващите потребителски разрешения. - person corrodedmonkee; 22.02.2011

Да, бихте могли да предотвратите фиксирането на сесия, като регенерирате идентификатора на сесията веднъж при влизане. По този начин, ако нападателят няма да знае стойността на бисквитката на току-що удостоверената сесия. Друг подход, който напълно спира проблема, е зададен session.use_only_cookies=True във вашата конфигурация по време на изпълнение. Нападателят не може да зададе стойността на бисквитката в контекста на друг домейн. Фиксирането на сесията разчита на изпращане на стойността на бисквитката като GET или POST.

person rook    schedule 22.02.2011