Интеграция JavaScript cookies в CURL-запросы

В этой статье я расскажу об одной необычной проблеме, с которой мне однажды пришлось столкнуться по роду своей деятельности. Внимание: эта статья не для начинающих. Предполагается, что читатель уже имеет опыт веб-программирования и знаком с языком PHP, библиотекой CURL и основами HTTP.

Перейдем к описанию проблемы.
Мне нужно было написать скрипт бота для одного сайта с целью автоматизации некоторого процесса из нескольких шагов, не считая авторизации. Такова была задача, на первый взгляд, ничего особенного. Впрочем, поначалу так оно и было. Т.к. на сайте применялась авторизация и активно использовались cookies, было решено использовать CURL. Я понемногу сниффил HTTP-запросы к серверу сайта и воспроизводил их у себя в скрипте. Процесс, как говорится, шел…. Неприятности начались на предпоследнем шаге, когда сервер совершенно неожиданно для меня отказался выдавать желаемый результат. Это привело меня в состояние ступора, в котором я пребывал довольно долгое время, продолжая снова и снова сравнивать логи «искусственных» и «естественных» (браузерных) запросов в надежде найти хоть какое-то несовпадение. Прошло несколько часов, прежде чем я понял, что впустую трачу время.

И тогда я обратил свое внимание на так называемые JavaScript (или non-HTTP) cookies. Разумеется, CURL не мог отследить их появление и соответственно не мог добавить их в свои запросы. Но зачем серверу проверять JavaScript cookies? Хороший вопрос, в другой раз он бы обязательно меня заинтересовал, но в тот момент у меня были другие заботы.

Забегая вперед, скажу, что моя догадка оказалась верной. Сервер и в самом деле проверял JS куки, причем только на том злополучном предпоследнем шаге. Я не знаю, с какой целью это было сделано. Тем более я не уверен, что этой целью была защита от ботов. Как говорится, чужая душа (сервер) — потемки.

Итак, мне предстояло решить следующие две задачи:
  1. найти «значимые» JavaScript cookies, которые влияют на ответ сервера;
  2. найти способ вставить эти куки в запросы CURL.

Насчет первой задачи мне, в принципе, было все ясно, во всяком случае, я уже представлял себе примерный план действий. Так что я решил сразу начать со второй: найти средство для добавления «своих» куков в CURL-запрос, дополнительно к тем, что появляются там автоматически, из файла, указанного опцией CURLOPT_COOKIEFILE.

Первым, что пришло мне в голову, была мысль: включить опцию CURLOPT_COOKIE со строкой, составленной из параметров куков. Примерно так:
curl_setopt($hc, CURLOPT_COOKIE, "name1=value1; name2=value2; ...");

Так я и сделал: добавил эту строку в код… и очень скоро убедился, что это не работает. Вернее работает, но совсем не так как мне хотелось. В отправленном HTTP-заголовке были только куки, добавленные этой опцией, а вот куки из файла CURLOPT_COOKIEFILE при этом исчезли (исчезли из заголовка, а не из файла). Т.е. содержимое файла-хранилища куков игнорировалось. Из этого следует простой и бесполезный вывод: опции CURLOPT_COOKIE и CURLOPT_COOKIEFILE/CURLOPT_COOKIEJAR нельзя использовать вместе.

Короче, решить проблему по-быстрому не получилось. Поиск готовых решений в интернете тоже ничего не дал. Может быть я плохо искал, но все, что мне удалось найти по этой теме, вот этот вопрос, заданный на форуме StackOverflow, и как видно, оставшийся без ответа.

А тем временем срок сдачи работы подходил к концу, заказчик требовал объяснений, а у меня, как назло, куда-то пропало все мое красноречие. Тут мне почему-то вспомнилась крылатая фраза из одной советской комедии: «Либо я веду ее в загс, либо она ведет меня к прокурору». Ни в загс, ни к прокурору, мне не хотелось. Мне очень хотелось куда-нибудь скрыться и забыться… но пришлось выбирать другой способ устранения проблем.

К тому времени я уже видел два пути решения:
  1. отказаться от услуг CURL'а в плане автоматической обработки cookies и взять эту «черную» работу себе, т.е. самому парсить куки из заголовков ответа, сохранять их, и передавать вместе с запросами. Звучит немного пугающе, но зато это дает полный контроль над куками.
  2. оставить авто обработку cookies, но добавить возможность вставки в файл куков «своих» (кастомных) параметров. Тоже перспектива не из приятных, поскольку это предполагало ручную правку файла куков.

После недолгих колебаний был выбран второй вариант, т.к. он показался мне более легким в реализации. К тому же меня давно уже интересовал формат Netscape Cookie File, но не было повода познакомиться с ним поближе. И вот этот повод появился.
Искать информацию по этому формату долго не пришлось. С первой же страницы выдачи гугла я попал на оф. сайт CURL'а, в архив переписки пользователей с создателем этой библиотеки, где и нашел то, что искал.

Формат файла оказался довольно простым – 7 полей (атрибутов) в каждой строке, разделенных tab'ами и идущих в таком порядке:
  • domain
  • tailmatch
  • path
  • secure
  • expires
  • name
  • value

Смысл этих полей имхо вполне очевиден. Отмечу только, что tailmatch – это флаг точного совпадения доменного имени сайта.

Теперь, когда формат файла был известен, остальное было уже делом техники.
В итоге мной был написан небольшой класс CookiejarEdit, исходный код которого привожу прямо здесь:

<?php
class CookiejarEdit
{
protected $sFname= false; // имя файла cookies
protected $aPrefix= array( // массив значений общих полей записей
	'',		// #0: domain
	'FALSE',	// #1: tailmatch (строгое совпадение доменного имени)
	'/',	 	// #2: path
	'FALSE',	// #3: secure (https-соединение)
);
protected $sPrefix= ''; // строка значений общих полей записей cookie

function __construct($sFn, $sDomain='', $aXtra=0) {
	if (!$sFn) return;
	$this->sFname= $sFn;
	$this->setPrefix($sDomain, $aXtra);
}
function __clone() {
	$this->setPrefix();
}

/****
** Инициализация/установка общих полей записей cookie:
** Аргументы:
**  1) $sDomain - значение поля 'domain'
**  2) $aXtra - массив значений дополн-ных общих полей:
**     $aXtra['tailmatch'] - значение поля 'tailmatch'
**     $aXtra['path'] - значение поля 'path'
**     $aXtra['secure'] - значение поля 'secure'
*/
function setPrefix($sDomain, $aXtra=0) {
	if ($sDomain)
		$this->aPrefix[0]= $sDomain;
	if (is_array($aXtra)) {
		if (isset($aXtra['tailmatch']))
			$this->aPrefix[1]= $aXtra['tailmatch']? 'TRUE': 'FALSE';
		if (isset($aXtra['path']))
			$this->aPrefix[2]= $aXtra['path'];
		if (isset($aXtra['secure']))
			$this->aPrefix[3]= $aXtra['secure']? 'TRUE': 'FALSE';
	}
	if ($this->aPrefix[0])
		$this->sPrefix= implode("\t", $this->aPrefix). "\t";
}

/****
** Экспорт содержимого файла cookies:
*/
function export() {
	return ($this->sFname)? file_get_contents($this->sFname) : false;
}

/****
** Импорт содержимого файла cookies:
*/
function import($sCont) {
	if (!$sCont || strlen($sCont)<10) return false;
	file_put_contents($this->sFname, $sCont);
	return true;
}

/****
** Добавление/изменение/удаление записи в/из файла cookies:
** Аргументы:
**  1) $aFields - массив значений индивидуальных полей записи cookie
**     $aFields[0] - поле 'name' (имя параметра)
**     $aFields[1] - поле 'value' (значение параметра)
**     $aFields[2] - срок хранения записи в днях
** Возвращает значения:
**  1) false - в случае неправильного вызова
**  2) true - в случае успеха удаления
**  3) string - в случае успеха добавления/изменения, содержимое строки записи
*/
function setCookie($aFields) {
	if (!$this->sFname || !$this->sPrefix)
		return false;
	if (!is_array($aFields) || !($n_arr= count($aFields)))
		return false;
	$name= $aFields[0];
	$cont= file_exists($this->sFname)? file_get_contents($this->sFname): '';
	$cr= (strpos($cont, "\r\n") !== false)? "\r\n" : "\n";
	$a_rows= explode($cr, trim($cont, $cr));
	$i_row= -1;
	foreach ($a_rows as $i=> $row) {
		if (strpos($row, "\t".$name."\t") === false) continue;
		if (strpos($row, $this->sPrefix) !== 0) continue;
		$i_row= $i; break;
	}
	$ret= true;
	if ($n_arr> 1) {
		// add/modify:
		$val= $aFields[1];
		$life= ($n_arr> 2 && $aFields[1]>= 0)? $aFields[1] : 1;
		if ($i_row<0) $i_row= count($a_rows);
		$n_exp= ($life> 0)? (time()+ $life* 24* 60* 60) : 0;
		$a_rows[$i_row]= $ret=
			$this->sPrefix. implode("\t", array($n_exp, $name, $val));
	}
	else if ($i_row>= 0) {
		// remove:
		unset($a_rows[$i_row]);
	}
	file_put_contents($this->sFname, implode($cr, $a_rows).$cr);
	return $ret;
}

/****
** Добавление/изменение записи в файл cookies:
*/
function addCookie($sName, $sVal, $nLife=0) {
	return $this->setCookie(array($sName, $sVal, $nLife));
}

/****
** Удаление записи из файла cookies:
*/
function removeCookie($sName) {
	return $this->setCookie(array($sName));
}
}
?>


Методы __clone(), export(), import() были добавлены «чисто для украшения» кода. Честно говоря, я не вижу в них большого смысла, так же как и в дополнительном аргументе $aXtra для метода setPrefix, который я добавил просто на всякий случай (хотя имхо, необходимость в нем не может возникнуть по определению). В любом случае код рабочий и готов к использованию (PHP >= 5.0).
Я не претендую на оригинальность идеи и не исключаю возможности, что это очередной «велосипед». Возможно, аналогичные и даже более простые решения давно уже существуют. Тем не менее, мне мой «велосипед» помог и я буду рад, если он поможет кому-нибудь еще.

Итак, инструмент для «продвинутой» работы с куками был готов. Но на этом мои приключения не закончились. Предстояло еще отследить те самые «значимые» JS куки, понять какие значения им присваиваются и многое другое. Но это уже другая история. А эту я пожалуй закончу. Спасибо за внимание.

P.S.:
Перечитав текст статьи в очередной раз, я понял, что ей все-таки недостает «примера из реальной жизни». Неплохо было бы, подумал я, устроить небольшую демонстрацию на примере какого-нибудь сайта. Использовать тот сайт, с которым я работал, я, по определенным причинам, не мог. Поэтому нужно было найти ему замену: какой-нибудь известный сайт, не требующий авторизации, где хоть как-то используется проверка JavaScript cookies на стороне сервера. К моему удивлению и счастью такой сайт нашелся очень быстро: в моих закладках. Это всем известный Яндекс-Каталог, категория Фриланс (то, что мне ближе всего).

Сначала эта страница выглядит так:


Но если перейти к настройкам, выбрать там пункт «стандартное с номерами»:


и вернуться на страницу каталога, то мы добъемся «чудесного» эффекта: превью со страницы исчезнут и останутся только «сухие» цифры и текст:


Давайте попробуем написать простейший бот для скачивания первой страницы этого каталога без превьюшек. Я понимаю, как глупо это выглядит со стороны: писать бота только для того чтобы изменить вид страницы. Но не забывайте, что это всего лишь пример. Давайте представим, что получение страницы без превьюшек – наше самое заветное желание).

Для начала проведем рекогносцировку. Обратим внимание на url страницы каталога после изменения настроек: он не изменился. Правда, к нему добавилась строка "?rnd=xxx", но это, по всей видимости, всего лишь указание браузеру не брать страницу из кэша. Отсюда можно сделать вывод, что настройки передаются и сохраняются, скорее всего, через куки.

Попробуем разобраться как именно это происходит. В этом нам поможет такой полезный инструмент, как Live HTTP Headers:


Это расширение Firefox'а позволяет отслеживать весь входящий и исходящий HTTP-траффик в браузере, в том числе и куки, посредством которых и осуществляется запоминание настроек в нашем примере. Происходит это, очевидно, после нажатия кнопки «Сохранить» и перехода к странице каталога.

Зайдем еще раз на страницу настроек, предварительно включив снифер Live HTTP Headers. Выберем снова пункт «стандартное с номерами» и нажмем кнопку «Сохранить». А теперь посмотрим на наш улов в снифере. Нас интересуют подробности запроса страницы каталога. У меня они имеют такой вид:

GET /yca/cat/Employment/Freelance/?rnd=191 HTTP/1.1
Host: yaca.yandex.ru
User-Agent: ...
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: ru-ru,ru;q=0.8,en-us;q=0.5,en;q=0.3
Accept-Encoding: gzip,deflate
Accept-Charset: windows-1251,utf-8;q=0.7,*;q=0.7
Keep-Alive: 115
Connection: keep-alive
Referer: http://yaca.yandex.ru/setup.xml
Cookie: yandexuid=796954901281541279; fuid01=4c62c49f04c00e82.pQ2hPKLWAnitiiTOBnW-nvOhiFssICTMfcKaMv0ZeTsFKaxVHOYxAPA2AGRsdF1qi3rm7fAKk77gJevuaNmhtnNUx_k0ykECc8bRJv3dUadZ_YDF1QLDZddTzYP_ZfOs; my=YwA=; L=eEAcXVFJR252Q0ADVkt9BW5wWmFyXXhXBkBYAwQaYmIRBgo6Ciw9ZggRFwUmNQwcOUs5LwQvVD42OjAPCmFfFQ==.1310050733.9042.213864.a4f928b9d113358bc254454a879f6c5c; yp=1636215158.sp.; yabs-frequency=/3/UOW2AQmAGyle0Ici2au0/; yaca_view=num


Здесь сразу бросается в глаза фрагмент "yaca_view=num". Скорее всего, это и есть наш искомый cookie-параметр. Но где он устанавливается? Во всяком случае не в заголовках ответа сервера, поскольку там этот параметр не встречается. Тогда логично предположить, что это JavaScript cookie и значит, его установка происходит где-то в яваскриптах страницы настроек ("setup.xml"). Попробуем найти его в тексте этой страницы. Так и есть. Вот строка из файла "setup.xml":
$.cookie('yaca_view', $('input[name="yaca_view"]:checked' ).val());

По всей видимости здесь и происходит установка параметра "yaca_view" со значением, взятым из одноименного элемента формы (в нашем случае это значение 'num').

Итак, мы выяснили, что для того, чтобы увидеть страницу каталога без превьюшек, нужно передать серверу cookie-параметр с именем 'yaca_view' и значением 'num'. Теперь, когда у нас уже есть средство для добавления cookies в CURL-запросы, можно без особого труда написать скрипт бота. Вот его код с небольшими комментариями:

<?php
require_once "cookiejaredit.inc";

if (!function_exists('curl_setopt_array')) {
function curl_setopt_array(&$hc, $a_opts) {
	foreach ($a_opts as $name=> $val)
		if (!curl_setopt($hc, $name, $val)) return false;
	return true;
}
}

/****
** Скачивание файла с помощью CURL:
** Аргументы:
**  1) $aOpts - массив значений опций CURL:
**  2) $sUrl - URL файла
**  3) $sUrlRef - URL реферера
** Возвращает значения:
**  1) false - в случае ошибки
**  2) string - в случае успеха, содержимое файла
*/
function getByCurl($aOpts, $sUrl, $sUrlRef='') {
	$hc= curl_init();
	curl_setopt_array($hc, $aOpts);
	curl_setopt($hc, CURLOPT_URL, $sUrl);
	curl_setopt($hc, CURLOPT_REFERER, $sUrlRef);
	$cont= curl_exec($hc);
	$b_ok= curl_errno($hc)==0 && curl_getinfo($hc, CURLINFO_HTTP_CODE)==200;
	echo "\nSent HTTP Header:\n". curl_getinfo($hc, CURLINFO_HEADER_OUT).
		"Content Length: ".strlen($cont)."\n\n";
	curl_close($hc);
	return $b_ok? $cont : false;
}

// Имя (путь) файла cookies:
$fn_cook= $_SERVER['DOCUMENT_ROOT'].'/cookiejar-tmp.txt';
// Массив значений опций CURL:
$a_curl_opts= array(
	CURLOPT_NOBODY => 0,
	CURLOPT_RETURNTRANSFER => 1,
	CURLOPT_CONNECTTIMEOUT => 10,
	CURLOPT_TIMEOUT => 15,
	CURLOPT_USERAGENT => 'Mozilla/5.0 Gecko/20110920 Firefox/3.6.23',
	CURLINFO_HEADER_OUT => true,
	CURLOPT_COOKIEFILE => $fn_cook,
	CURLOPT_COOKIEJAR => $fn_cook,
);
define('URL0', 'http://yaca.yandex.ru/yca/cat/Employment/Freelance/');
define('URL1', 'http://yaca.yandex.ru/setup.xml');
define('URL2', 'http://yaca.yandex.ru/yca/cat/Employment/Freelance/?rnd=');
define('FN_RESULT',	'result.htm');

echo '<h3>Trace Log:</h3><pre>';
$cookedit= new CookiejarEdit($fn_cook, 'yaca.yandex.ru');
// Скачиваем страницу настроек:
getByCurl($a_curl_opts, URL1, URL0);
// Добавляем JS cookie-параметр:
$rec= $cookedit->addCookie('yaca_view', 'num');
echo "addCookie:\n". ($rec? "$rec\n" : "Fail\n");
// Скачиваем страницу каталога:
$cont= getByCurl($a_curl_opts, URL2. rand(0,999), URL1);
echo '</pre>
<hr><h3>Result: ';
if ($cont) {
	file_put_contents(FN_RESULT, $cont);
	echo 'OK</h3><a href="'.FN_RESULT.'" target="_blank">Result page</a>';
}
else
	echo 'Fail</h3>';
?>


Теперь действительно все. Еще раз спасибо за внимание, особенно тем кто дочитал до конца).

Похожие публикации

Средняя зарплата в IT

113 000 ₽/мес.
Средняя зарплата по всем IT-специализациям на основании 5 091 анкеты, за 2-ое пол. 2020 года Узнать свою зарплату
AdBlock похитил этот баннер, но баннеры не зубы — отрастут

Подробнее
Реклама

Комментарии 68

    0
    В такой большой статье нужна картинка для привлечения внимания
      +33
        +4
        Я устал видеть это лицо, надеялся что тут не увижу… Ошибался.
      0
      Открою маленький секрет.
      Можно эмулировать работу настоящего браузера при помощи Selenium :)
        0
        Описаный автором метод больше подходит для парсинга сайтов(как в последнем примере), нежели для эмуляции браузера. Эмуляция, в данном случае, только метод достижения цели.
        Насколько знаю в таких случаях Селениум не используется(пните если неправ).
        +2
        Прошу прощения, может не совсем вник в суть проблемы, но что мешало между вызовами CURLOPT_COOKIEFILE и CURLOPT_COOKIEJAR сделать fwrite и дописать в файл необходимые куки?
          +1
          curl не сбрасывает куки в файл пока не закроете его дескриптор
            0
            Не знал про это, однако разве эту проблему нельзя обойти? Код ведь в наших руках.
              0
              лично я когда столкнулся с такой проблемой, не заморачивался, а действовал по принципу:
                0
                … на каждый вызов открывал и закрывал curl, а между вызовами дописывал в файл необходимую строку с куки.
                Но т.к. работал через ssl, то добавился небольшой оверхед, теперь curl'у приходилось заново на каждый вызов открывать ssl соединение.
          +3
          JavaScript (или non-HTTP) cookies

          Сам придумал?
            +1
            Я долго искал упоминание про это. Уж думал, что что-то пропустить успел в этой жизни.
              0
              я прошу прощения за то что ввел всех в заблуждение этим термином. но я не знал как это назвать по другому, не заморачиваясь. а какой термин вы предложили бы в этом случае?
                0
                Если развернуто, то это кукисы устанавливаемые веб-приложением на стороне клиента.
                  0
                  Вообще-то я просил указать термин, а не определение. К тому же ваше определение неверное по сути: куки в любом случае устанавливаются на стороне клиента. Если уж давать определение, то имхо лучше всего подойдет такое: non-HTTP (или JavaScript) cookies устанавливаются во время загрузки содержимого веб-страницы (чаще всего через JavaScript), тогда как HTTP cookies — во время чтения/парсинга HTTP-заголовка ответа.
                    0
                    Это как же кукисы устанавливаются в любом случае на стороне клиента? Разве не для того нужна функция setcookie, чтобы установить их на стороне сервера? Или вы хотите сказать что, если вы пишите http-клиент на питоне, то он использует python-cookie, java — java-cookie, а c++ — cpp-cookie? А сервер использует php-cookies? Суть лишь в том, что эти кукисы необходимы, для корректной работы приложения, но устанавливаются клиентом, а не сервером, хотите термин, пожалуйста — client defined cookies.
                      0
                      Просто мы с вами говорим на разных языках, потому и не понимаем друг друга. Я привык считать, что если говорят «устанавливается на чем-то», то это «что-то» обозначает цель, а не источник. В этом смысле ваше определение неверно, т.к. целью (местом хранения) cookies всегда является клиент, а источником — сервер.
                        +1
                        HTTP — транспортный протокол, он вообще ничего не устанавливает, лишь передает от клиента, серверу.

                        Cookie — технология хранения данных на клиенте. Кукисы могут устанавливать как клиент, так и сервер.

                        non-HTTP — обычно называются средства передачи каких-либо данных с помощью недокументированных заголовков, либо использующиеся поверх HTTP-протокола (заверните JSON в заголовок X-JSON-COOKIE, разверните на клиенте, вот вам non-HTTP).

                        Создать кукис вы можете хоть с помощью js, хоть из консоли ручками. Вы все-равно будете использовать HTTP для их передачи. Серверу все равно как вы установили кукис, главное что пришел он в корректном виде в соответствующем HTTP-заголовке.

                        Так что не вводите людей в заблуждение, новыми терминами, смысл которых сами придумали, да еще из непонимания технологии.
                          0
                          Я не хочу углубляться в бессмысленные споры. В любом случае я не виноват в том, что для этого понятия еще нет общепринятого термина. А раз его нет, то почему бы не предложить свой вариант названия? Вы ведь тоже придумали свой термин (client defined cookies), которого не существует.
                        +1
                        Имхо «non-HTTP cookies» — самый подходящий для данного случая термин. Он и встречается в тексте поста.
              0
              HTTP Заголовки можно смотреть в фаербаге.
                +1
                JavaScript cookies — вы так говорите, будто серверу есть разница каким образом пользователь получил их, через Javascript или нет.
                  –6
                  Вы так говорите, словно уверены что владеете вопросом лучше автора, при этом поленившись даже проверить в гугле свою собственную компетентность. В целях повышения безопасности можно действительно разрешить лишь http only cookie, www.php.net/manual/en/function.setcookie.php
                    0
                    Серверу всё равно откуда получены куки. А опция, про которую вы говорите, для браузеров.
                      –1
                      Данный флаг отсылается СЕРВЕРОМ, значит серверу таки " есть разница каким образом пользователь получил их, через Javascript или нет". Ну, а верификацию изменения кук можно делать двумя строчками кода.
                      if ($_SEESION['cookieCheck'] != $_COOKIE) die('bla bla'); проверяем.
                      $_SEESION['cookieCheck'] = $_COOKIE; привязываем к сессии. (сессии, как известно могут быть не только на куках)
                        +2
                        Серверу нет разницы откуда пользователь взял куку, что ему прислали то и принял. А флаг httponly лишь не дает доступ к куки с ява-скрипта. Вот.
                          –1
                          я привёл пример каким образом сервер может попросить клиента не трогать куки и код, который позволяет серверу не обрабатывать запрос с модифицированной кукой.
                          Да, браузер может вовсе не поддерживать данной технологии, да, запрос можно сформировать руками, но я программист или где? Я могу на сервер сайд коде проверить модификацию данных и блочить негодяев.
                            0
                            Зачем отсылать в куках данные, которые лежат в сессии?
                              –1
                              Какое отношение ваш вопрос имеет к тезису habrahabr.ru/blogs/php/133191/?reply_to=4420881#comment_4420609

                              Если интересно, то этот приём я использовал когда делал сессии в обход стандартного php обработчика сессий, просто хранил данные в шифрованной сессии, а для контроля модификаций дополнительно ставили куку с чексумой. Часть скриптов проекта была на си и perl, так что техника была вынужденной мерой.
                                0
                                Хорошо.
                                Покажите мне разницу между проведениями вашего приложения, при получении запроса от пользователя, в следующих случаях:
                                а. у кук пользователя флаг httponly = true
                                б. у кук пользователя флаг httponly = false
                                  –1
                                  Выше я показывал пример, просто запрещал доступ к функционалу, если куки изменены. А флаг позволяет защититься от случайных кук на клиенте, которые могут поставить js скрипты.
                                    0
                                    Переформулирую вопрос, на этот вопрос надо ответить «Да» или «Нет»: ведёт ли себя сервер по-разному в выше перечисленных случаях? Да/Нет
                                      –1
                                      Вы занимаетесь софистикой и меняете тему обсуждения. Я отвечаю на тезис на который изначально писал коментарий.
                                      >>вы так говорите, будто серверу есть разница каким образом пользователь получил их, через Javascript или нет.

                                      Ответ однозначен. ДА, для сервера может иметь большую разницу каким образом пользователю установлена кука. А то что в серверном языке имеется специальная опция, показывает что это не только моя фантазия.
                                        0
                                        Погодите. Вы высказали мысль. В качестве доказательства вы привели пример, используя его как аргумент в пользу своего довода.

                                        Я, в свою очередь, пытаюсь показать, что ваш пример как раз и иллюстрирует наш довод: «серверу всё равно как куки попали». Поясню почему я так думаю:

                                        Что сделает сервер, если получит куки, которые сформированы ява скриптом? — Проверит их валидность.
                                        Что сделает сервер, если получит куки, которые пользователь отправил вручную? — Проверит их валидность.
                                        Что сделат сервер, если получит куки, которые отправлял сам до этого?
                                        — Проверит из валидность.
                                        Т.е. как бы куки не были сформированы они будут обработаны одинаково.
                                        Может ли сервер различить то как были установлены куки (вручную, ява скриптом или действительно пришли с сервера)? — Нет.

                                        Таким образом Ваш пример и показывает, что «Серверу всё равно как были сформированы куки».
                                          0
                                          Я думаю вам стоит почитать не только про куки, а и как вообще идет обмен в http протоколе, потом закурить и подумать.
                                        0
                                        И ещё вопрос:
                                        Я злостный извращенец. Мне открылась страница, я удалил все куки, потом написал скрипт, которые создаёт эти же куки с такими же значениями. Затем я отправил всё на сервер. Приложение просто проверит контрольную сумму как и всегда или будет вести себя по-другому?
                              0
                              Установка данного флага, грубо говоря, делается «для красоты» (или для того, чтобы у пользователя не смогли украсть эти куки). Т.е. установка такого флага сама по себе является указателем для браузера и только (и то не для всех — по словам документации).

                              Такое поведение сравнимо с тем, что мы для текстового поля установили максимальную длину в 10 символов(/>). Да, браузер не даст вводить больше, но при получении данных с браузера серверу будет всё равно какой он там атрибут отослал в тэге. Если, конечно, не будет дополнительных проверок, которые и будут фактически ограничивать что-то.

                              Кстати, по поводу дополнительных проверок. Вы сделали хранение данных сессии в куках, но, чтобы проверить, что они те, что были Вы храните их в сессии.…

                              P.S. опция httponly — не для того, чтобы куку не могли изменить, а скорее для того, чтобы куку не могли украсть.
                            0
                            Скорее всего вы ошиблись топиком.
                              –3
                              Скорее всего я ошибся ресурсом, всё никак не привыкну, что на хабре не положено говорить на технические темы, а в комментариях нужно либо лажать автора, либо постить фото Джимми Уэйлса и демотиваторы.
                                0
                                Вы не правы, куки лишь строка, где они были добавлены и как серверу на самом деле плевать, потому что это лишь очередная строчка в заголовке. И та опция что Вы указали легко обходится.
                                  0
                                  Чукча — не читатель, Чукча — писатель. Я прекрасно знаю что опцию можно не только обойти, но и она может быть тупо не поддерживаемой клиентом. Но проверить модификацию куки на сервере — не проблема, с помочью полного сравнения с копией на сервере, либо записью чексумы. Выше всё это уже писал и привёл пример из практики, когда проверка кук на серере использовалась.

                                  А в итоге за то что поделился знаниями и опытом, ещё и должен оправдываться. Тьфу.
                                    0
                                    Тут не в знаниях дело, просто куки добавлялись на клиенте и человек хотел эти же куки вставить курлом и столкнулся с проблемой дозаписи своих куков к существующим. Никто ведь не говорил что Вы не знаете чего-то, просто Вы не о том говорили.
                                      0
                                      Я НЕ комментировал сам пост, а комментировал конкретный комментарий к этой записи. Не нужно тащить свои глобальные переменные в этот неймспэйс.
                                        0
                                        Ну так серверу на самом деле без разницы =) Он получает строку независимо от того как куки ставили. Человек просто поправил в терминологии, не более.
                                          0
                                          Ок. Вы правы, но но мне кажется что за демагогией и софистикой вы где-то теряете истинный смысл. Я мог бы продолжить вашу цепочку, вель в реальности куку посылаются не с сервера на сервер, а их отсылает отсылает клиент, коим обычно является браузер. Да и кук никаких нет, это всего-лишь дополнительный заголовок. Да и заголовков нет, это набор байтов, да и байтов нет, это набор битов нулдей и единиц, да и нулей их нет, на самом деле это состояния есть сигнал, нет сигнала, да и сигналов нет, это ток, да и тока нет, это движение электирческих зарядов… Причём каждый термин можно оспорить и представить в нужно виде.

                                          Но если мои куки изменил на клиенте и это может повлиять на логику серверного приложения, то мне плевать как это называется, потому что я знаю как это на сервере проверить и какой оптицей рекомендовать клиенту не трогать присланное сервером.
                                            0
                                            А не проще в сесии хранить данные которые не хотите чтоб были изменены? Всеравно ведь храните копию кук в сесии. А получается что Вы всеравно храните копию и пишете куки лишь для того (!) чтоб просто проверить «а не изменились ли они». Если это для передачи данных на клиент, так через JavaScript их проще и удобней передавать.
                                              0
                                              Ну ёлки ж. Я выше писал об этом. Сессии это внутриязыковая сущность, если используешь разные языки, то куки являются более универсальным средством, но приходится их шифровать и проверять целостность. Вот у человека возникла проблема, что нужно учитывать изменения куки посредством js, бывает обратная задача, когда нужно защититься от изменения посредством js. Причём не от злонамеренного, а случайного плагина, который может переписать нужную переменную. Здесь приходит на помощь столь полезный флаг. Мы в своё время писали под обёртку на php4 и не знали о нём, слава богу тогда js-а было мало и мы всё контролировали.
                                              Понятно, что http — протокол без состояния и фактически каждый запрос делается с нуля, а восстановить состояния можно лишь по токену. Но знать о защите «кук» от дурака полезно, даже если с этим сталкивается 1 из тысяч разработчиков.
                                                0
                                                <?php
                                                echo 'var jsVar = " '. addslashes($_SESSION['data_for_js']). ' " ';
                                                ?>
                                                И не стоит совать в куки данные которые не хотите чтоб изменили.
                                                  0
                                                  тогда уж лучше
                                                    0
                                                    прошу прощения за ctrl+Enter

                                                    echo 'var jsVar = '. json_encode($_SESSION['data_for_js']);
                                                  0
                                                  Хотя, может я чего-то не понимаю, приведите мне один пример когда нужно записать в куки _неизменяемое_ значение. Может с примером пойму что Вы имеете ввиду.
                                                    0
                                                    Там проблема, что они работают в разных языках одновременно(PHP, c, perl). А хранить сессии так, чтобы они были легко доступны из любого языка не получилось. Поэтому решили хранить всё в куках. Но потом появилась мысль в том что куки всё же могут изменить и стали думать над защитой. И придумали хранить контрольную сумму, которую можно получить из любого языка. И всё же научились получать контрольную сумму из разных языков, а эта сумма является тоже данными сессии… а класть данные туда где хранится контрольная сумма ещё не догадались.
                                                      0
                                                      а не… с суммой я ошибся… сумма передаётся в куках тоже. Поэтому только первая часть моего сообщения истинна.
                                                        0
                                                        1. Делаем общую папку и храним данные в JSON
                                                        2. Бд придумали тоже не для хранения данных
                                                        3. NOSQL бд очень быстры и для таких задач идеальны
                                                        4. (и главное) КАК Вы в perl узнаете что я в браузере не менял куки которые Вы поставили в PHP?
                                                        0
                                                        Да хоть саму сессию можно делать только на куках, если у вас используется несколько разных языков. программирования. Функцию упаковки-распаковки такой сессии можно написать на любом языке.

                                                        Сейчас, конечно, проще хранить токен и поместить сессию на сервере в каком-нить memcached, который быстр и клиенты для работы с которым есть на другом языке.
                                                          0
                                                          Существуют паттерны проектирования: server session state и client session state… Сессии в PHP просто реализуют первый. Никто не мешает вам сделать свою реализацию.
                                                    0
                                                    > мои куки изменил на клиенте
                                                    В точку Ваше приложение не занимается проверкой того как были сформированы куки, оно проверяет те ли эти куки.
                                    0
                                    это название в терминах бота на curl'е, т.к. бот может получить куки только от сервера и только в заголовках.
                                      0
                                      Вы правы, серверу все равно каким образом пользователь получает cookies: из заголовков ответа или из JS-кода. но мне (как боту а не как пользователю) не все равно. если в первом случае я могу все свалить на CURL и не париться, то во 2-м — придется делать вот такие костыли.
                                      0
                                      Мне кажется надо уже всем сделать API или прямо выкладывать SQL с дампом базы данных. Что бы можно было не утруждать одних придумывать других ломать.

                                      P.S. Странно, что вы еще на баг с путями к JAR файлу не наткнулись…
                                        0
                                        За Live HTTP Headers, спасибо, будем знать.
                                        До этого пользовался Wireshark для анализа HTTP-трафика.
                                        Возникало ощущение, что как пушкой по воробью.
                                          0
                                          HTTP Analyzer Standart попробуйте. Значительно мощнее и можно тестировать свои запросы «не отходя от кассы».
                                            0
                                            а разве фаербаг во вкладке «Net» уже не показывает заголовки запросов/ответов? там все должно быть, и даже в более красивом виде…
                                              0
                                              одно неосторожное движение, и все запросы очищаются
                                                0
                                                Насчет красоты фаербага спорить не буду, а вот насчет удобства… В фаербаге все приходится делать онлайн, а сохранить текущие результаты в файле для последующего изучения не так просто, если не невозможно. В Live HTTP Headers сохранение выполняется одной кнопкой. В этом плане он для меня незаменим. А фаербаг имхо больше всего подходит для анализа и отладки JS/AJAX.
                                              0
                                              Столкнулся с тем же при «автоматизации работы» с порталом на IBM WebSphere, как раз на последнем-предпоследнем шаге он добавляет cookie в js.
                                                0
                                                Я так понимаю под термином JavaScript cookie подразумеваются куки которые были положены через JavaScript? Потому что реально термина JavaScript Cookie я лично не встречал…
                                                  0
                                                  лепру парсили или что-то похожее? там очень хорошо с авторизацией, вроде

                                                  Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                                                  Самое читаемое