C, PHP, VB, .NET

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


* Фиксиране на сесиите по характеристики на потребителя

Публикувано на 15 ноември 2008 в раздел ОСУП.

В тази тема ще си поставим въпроса дали може да се направи нещо в помощ на потребителите дори в случая, когато сесията е открадната. Дали можем да "заключим" сесията така, че да не може да бъде използвана в браузър, който е различен от неговия?

Може би най-ефективната техника е заключване на сесия по IP адрес. Чрез използването на супер-глобалната променлива $_SERVER ние можем да извлечем IP адреса на сесията чрез стойността на полето 'REMOTE_ADDR'. Идеята за фиксиране на сесия по IP адрес е при първоначалното зареждане на страницата да запазим IP адреса на потребителя в самата сесия. Ако някой друг IP адрес пожелае да използва същата сесия, ние ще отхвърлим заявката и ще унищожим сесията:

<?php
	session_start();
	session_regenerate_id(true);
?>
<html>
<head>
	<title>WorldBank.dom session page</title>
</head>
<body>
<?php
	if(isset($_SESSION['ip'])){
	   if($_SESSION['ip'] != $_SERVER['REMOTE_ADDR']){
	      echo 'You entered a session fixed by another IP!';
	      session_destroy();
	      return;
	   }
	}
	else{
	   $_SESSION['ip'] = $_SERVER['REMOTE_ADDR'];
	}

	$maxSessionTime = 10 * 60;
	$timeout = 5 * 60;
	$curtime = (int)time();
	if(! isset($_SESSION['initial_time'])){
		$_SESSION['initial_time'] = (int)time();
	}
	if($curtime-$_SESSION['initial_time'] > $maxSessionTime){
		echo 'Maximum session time exceeded';
		session_destroy();
		return;
	}

	if (isset($_SESSION['last_active'])){
		if ($curtime-$_SESSION['last_active'] > $timeout){
			echo 'Session timeout';
			session_destroy();
			return;
		}
	}
	$_SESSION['last_active'] = $curtime;

	echo 'session id is: ';
	echo session_id();
	echo '<br>';
	if (!isset($_SESSION['counter'])){
		$_SESSION['counter'] = 0;
	}
	else{
		$_SESSION['counter']++;
	}
	echo 'counter: ';
	echo $_SESSION['counter'];
?>
</body></html>

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

Има обаче един сериозен недостатък - фиксирането на сесия по IP е валиден метод само за вътрешни или фиксирани мрежи. Използването му в Интернет приложение може да създаде сериозни проблеми на легитимни потребители ако те са част от големи NAT мрежи (например всички потребители на AOL). Получава се така, защото при тях различните заявки идват от различни IP адреси (те нямат собствено IP, а използват няколко възможни изходящи). Използвайте този метод само ако сте напълно убедени, че нямате такива потребители в системата си.

Тогава какво да правим в общия случай? Можем да вземем някои отличителни характеристики на браузърите на клиентите и да ги запишем в сесията. Такива характеристики например са:

  • $_SERVER['HTTP_ACCEPT'] - съдържанието на Accept хедъра на браузъра, с който той посочва на сървъра какви типове информация съдържа. Между различните браузъри има разлики в този хедър;
  • $_SERVER['HTTP_ACCEPT_LANGUAGE'] - чрез тази променлива браузърът съобщава на сървъра какви са предпочитаните езици, с които работи (en, bg, т.н.)
  • $_SERVER['HTTP_ACCEPT_ENCODING'] - хедър, с който браузърът указва на сървъра какъв алгоритъм за компресия поддържа (обикновено gzip);
  • $_SERVER['HTTP_CONNECTION'] - чрез тази променлива браузърът обикновено предлага допълнителни "настройки" на http заявката, например да се използва функционалността "keep alive" на Apache;
  • $_SERVER['HTTP_ACCEPT_CHARSET'] - какви кодировки предпочита браузъра. Най-често браузърите НЕ изпращат този хедър;
  • $_SERVER['HTTP_USER_AGENT'] - текстът, с който браузъра се представя пред сървъра. С него внимавайте, защото при обновяване на браузъра обикновено се сменя и версията вътре в този хедър. Заключване на нормална сесия (като тези, с които сме се запознали до този момент) е безпроблемно, защото тъй или иначе при обновяване на браузъра той се рестартира, а както знаем при рестартиране session id се губи. По-нататък обаче ще правим и друг тип автентикация с перманентно записани cookies, които НЕ трябва да ги фиксираме по User Agent.

За съжаление всички изброени дотук променливи са най-малкото предвидими. Вероятността хакера да знае кой точно е браузъра на клиента не е малък, но дори да не го знае, шансът да влезе в сесията с налучкване също не е малък (просто ще пробва да се представи като Chrome или Firefox, които са двата най-популярни браузъра).

Все пак това не е единствения "fingerprint", който можем да използваме. Оказва се, че можем значително да затрудним хакера като използваме допълнителни променливи, които да прочетем чрез javascript:

<script language="JavaScript">
 var x=navigator.plugins.length; 
 var txt=x;
 for(var i=0;i<x;i++)
 {
    txt+=navigator.plugins[i].name; 
 }
 txt+=navigator.platform;
 txt+=navigator.cookieEnabled;
 txt+=navigator.doNotTrack;
 var d = new Date()
 txt+=d.getTimezoneOffset();
 txt+=window.screen.height+window.screen.width;
 txt+=window.devicePixelRatio;
 txt+=window.screen.colorDepth;
</script>

След изпълнението на този скрипт, в променливата txt ще има записани списък с плъгини, които са заредени в браузъра, времева зона, дали използва doNotTrack хедър, резолюция и цветове на екрана, и др. Всички тези характеристики обикновено не се променят по време на работа със сесията на потребителя. Все пак трябва да се знае, че при смяна на резолюцията на екрана му (за щастие нещо, което се случва рядко при днешните LCD монитори) или инсталиране на нов плъгин в браузъра по времето, в което е логнат в нашия сайт, би унищожило сесията му.

За съжаление ще изпитате затруднение да прехвърлите тези променливи от javascript в PHP. JavaScript се изпълнява в браузъра на клиента и няма общо със скриптовете на сървъра. Затова прочитането на тези данни от страна на вашия PHP скрипт няма как да се случи в рамките на текущата страница - те могат да се прехвърлят от една страница на друга чрез GET и POST заявки. Например:

  • Пренасочване с JavaScript и предаване на данните с GET параметър към друг скрипт:
    document.location="http://.../script.php?safe_browser_fingerprint="+txt;
  • Предаване на данните към друг скрипт с POST параметър във форма:
    <input type="text" name="safe_browser_fingerprint" id="safe_browser_fingerprint" style="display:none"/>
    <script type="text/javascript">
    document.getElementById("safe_browser_fingerprint").value=txt;
    </script>

В скрипта, който получава данните, може да прочетете генерирания fingerprint чрез $_GET['safe_browser_fingerprint'] или съответно $_POST['safe_browser_fingerprint']. Един потенциален проблем при подобно предаване е максималната дължина на URL, която е 2000 символа - при браузър с много plugins подобна дължина би могла да бъде прескочена. Затова можете първо да хеширате променливата txt и след това да я изпратите. Ще разгледаме хеширането в следваща статия.

При всички случаи обаче тази техника не е удобна за фиксиране на потребителска сесия. Тя е по-подходяща за фиксиране на cookie за автоматичен вход, което е техника, която също ще разгледаме по-късно.

Задача 1: Помислете какви други отличителни белези за потребителската сесия бихте могли да използвате, за да направите подобни "капани" за атакуващия, като например характеристики на неговия Flash Plugin в браузъра.

Задача 2: А не можем ли да пазим в cookie нещо различно от името и паролата на потребителя - например произволно генериран token? Какви биха били ползите от подобна тактика? Разгледайте следната статия за идеи: https://www.cphpvb.net/network-security/9658-cookie-token-authentication/

 



2 коментара


  1. "при смяна на резолюцията на екрана му (за щастие нещо, което се случва рядко при днешните LCD монитори)"
    При мобилно устройство, при завъртане на телефона – window.screen.height и window.screen.width разменят стойностите си. Завърти си човек телефона и го изхвърляме от профила му 😄
    Разбира се, това лесно може да се оправи, ако например винаги по-голямата от двете стойности е първа.

  2. Е, писано е 2008 г. Тогава не беше модерно. Сега за мобилни телефони по-скоро може да е сбора от двете. Редактирах статията.

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

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


*