Как стать автором
Обновить

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

Статья может быть и хороша для новичков, но ей не место на Хабре, где для большинства это банальные истины.
Каждый PHPшник должен написать фреймворк, создать блог и написать свой учебник по PHP.
Накинулись на автора. Есть тег — «Для начинающих», есть пометка «Tutorial». По какому принципу вы определяете «место или не место на Хабре»?
По информативности и качеству слога.
Я видел много намного более качественных tutorial и учебников, в которой эта информация изложена более информативно и более качественным языком.
Вы уж опеределитесь по каким критериям статье не место на хабре ;)
Мне лично статья очень интересна. Хороших свободных материалов по пхп не так уж много.
Поэтому голосую обеими руками «ЗА»!
Стоит упомянуть о принудительной очистки сессии. Удалять не валидные сессии в потоке обработки запроса не лучшая идея.
он [HTTP] простой

Нет, он лишь внешне смотрится просто. Один только RFC 2616 занимает 176 страниц, а более новые RFC 7230-7235 — все 305 страниц в сумме


(если использовать метод GET, то строка запроса будет содержать логин и пароль, и может оказаться сохраненной на каких нибудь промежуточных прокси серверах, что очень плохо)

Прокси-серверы по определению видят ВЕСЬ запрос, в том числе тело и куки. Сохранить, соответственно, тоже могут что угодно куда угодно. Если же запрос зашифрованный (https, vpn и т.п.), то они не смогут увидеть ни тела, ни кук, ни строку запроса тоже. Если имеется в виду кеширование GET-запросов, то это отключается средствами HTTP, а POST-запрос нужен совсем не поэтому


Set-Cookie: KEY=VerySecretUniqueKey

Не такой уж и Secret, если флагов HttpOnly и Secure нет (впрочем, ниже по тексту HttpOnly упоминается)


Язык PHP создавался под стать протоколу HTTP — т.е. основная его задача — это дать ответ на HTTP запрос и «умереть» освободив память и ресурсы. Следовательно, и механизм сессий работает в PHP не в автоматическом режиме, а в ручном, и нужно знать что вызвать, да в каком порядке.

Из первого предложения никак не следует второе


расскажу о способах защиты [...] будем запоминать User-Agent

Вообще не защита. User-Agent не является секретом и подбирается на ура, особенно для популярных браузеров


die('Wrong browser');

То есть после выхода новой версии браузера пользователь больше не сможет пользоваться сайтом?


добавьте сюда ещё сохранение и проверку $_SERVER['REMOTE_ADDR']

То есть пользователи мобильных сетей больше не смогут пользоваться сайтом?


$_SERVER['HTTP_X_FORWARDED_FOR']

Этот заголовок вообще может установить пользователь как хочет, и опираться на него без предварительной настройки сервера нельзя


это уже более-менее будет походить на защиту от злоумышленников

Это будет походить на защиту от обычных пользователей, которые всего лишь обновили свой хром из гугл-плея и/или перебежали в другой макдональдс и потеряли доступ к сайту


эта настройка [HttpOnly] позволяет достаточно эффективно бороться с XSS атаками в практически всех браузерах

HttpOnly не имеет вообще никакого отношения к XSS. Если есть XSS, то ничто не мешает напакостить и, например, отправлять ajax-запросы без и чтения кук (куки к запросам на тот же origin автоматически добавит сам браузер)

Ух, как-то совсем серьёзно отнеслись к статье «для начинающих».
Имхо, не стоит начинающих пугать RFC, хорошо если усвоят азы HTTP, и да я не собирался пересказывать весь мануал, лишь провести поверхностное знакомство для понимания механизма сессий, чутка затронув различия GET и POST запросов.
По «защите» я хотел донести главное, что её нужно внедрять, что AS IS оставлять не следует.
По работе сессии и РНР — согласен, не верно сформулировал мысль, надо будет исправить.

«Для начинающих» не означает, что можно писать что-то не соответствующее действительности. Ну или в крайнем случае можно для упрощения объяснений, но с обязательной припиской о неточности и ссылкой для интересующихся, где почитать подробнее


я хотел донести главное, что её нужно внедрять

Судя по качеству продемонстрированноый защиты — на этой мысли и стоило остановиться, а сейчас реализация из статьи лишь несёт вред

Согласен, добавлю сноску
Тем не менее, обычно в access-логах сохраняется именно url, а не содержимое тела запроса.

Передавать логин с паролем нужно через POST не для защиты от перехвата трафика, а для защиты от подобного сохранения в логах. Чтобы не получилось, что в БД все пароли хранятся строго в виде хешей и все как бы безопасно — а рядом лежит файлик с теми же самыми паролями в открытом виде.
Прокси-серверы по определению видят ВЕСЬ запрос, в том числе тело и куки. Сохранить, соответственно, тоже могут что угодно куда угодно
Статья рассказывает про механизм сессиий, для совсем начинающих. Придираться и залезать в такие дебри — сильно за рамками. Но это ладно.
Касательно вашего замечания. Ключевое слово тут «могут». Access log апача (и аналоги) практически никогда не отключаются и сохраняют историю запросов именно в виде URL. Т.е. со всеми GET полями. С другой стороны, я ни разу не встречал ситуации, когда сохранялись заголовки и тело. Напомню, что речь про поведение по умолчанию.

Вот именно про access.log и нужно было рассказать в статье, а прокси вообще не упоминать

Ребята, статью я подправил так, чтобы всё было правильно и понятно для начинающих, и не смущало никого.
Логи/кеширование/хистори браузера и т.д. и т.п. — всё это побочные явления при использовании GET (это не считая идеолигически неверного GET для подобных операций).

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

Эта директива относится к файлам кук, но не касается файлов сессии
Да вы правы. Не на тот якорь ссылку скопировал. Нужно было на session.gc_maxlifetime.
Опять же, это время когда сессия будет считаться протухшей, в статье я рассказываю в какой момент времени перестанет существовать файл
Он именно и перестанет существовать после этого времени. Если отключить сборщик мусора, то сессии будут жить долго и счастливо вне зависимости от состояния этой настройки.
Не перестанет, после этого времени она будет протухшей и будет ждать сборщик мусора, а он запускается не каждый же раз
Вы в этом точно уверены? Не хотите проверить?
<?php
//время жизни сессии 10 секунд
ini_set('session.gc_maxlifetime', 10);
session_start();

var_dump(empty($_SESSION['number'])?0:$_SESSION['number']);

if (empty($_SESSION['number'])) {
    $_SESSION['number']=42;
}


Если ваше утверждение верно, то через 10 секунд сессия прервется. Я же утверждаю что она будет вполне себе живой до следующего прохода сборщика мусора.
Где вы это у меня прочитали? О_о
Вы сейчас утверждаете тоже самое, что я написал в статье…
Информацию о этой директиве добавлю
Совсем не затронут вопрос создания «долгоиграющих» сессий. Т.е. таких, которые не прекращаются при закрытии браузера, а восстанавливаются даже через несколько дней.

Я, конечно, давно не делал ничего с сессиями на PHP (да и вообще на PHP), но меня терзает смутное сомнение, что при выполнении session_start() браузеру отправляется директива не приводящая к созданию файловой куки. Сессионный идентификатор хранится только в памяти браузера и уничтожается при закрытии браузера.

А чтобы создать файловую куку, нужно выполнить команду setcookie, причем с установленным достаточно большим временем ее жизни. И в эту куку уже записывать ID сессии или/и «билет» входа (сессионный пароль).

Хотя… извиняюсь, если что попутал.
Вызов session_start() приводит к отправке заголовка Set-Cookie c PHPSESSID, хранит браузер это значение в памяти, в БД или сохраняет в файл — это уже он сам для себя решает.
Да… в последнее время у некоторых браузеров даже сессионные куки хранятся в БД. Но речь не столько о способе хранения, сколько о продолжительности. Раньше различие было четко — сессионные куки не хранились на диске в виде файлов или в БД, а информация длительного хранения записывалась в файловые куки. С тех пор многое изменилось. Но суть в том, что по session_start() в браузер передается директива Set-Cookie без указания срока хранения этой информации (с нулевым сроком), т.е. краткосрочная. А для создания куки длительного хранения используется setcookie с указанием срока годности этой информации.

Т.е. — это механизмы для создания сессий разного срока действия.

Но… действительно все зависит от браузера. Говорят, Хром не удаляет id сессий с нулевым сроком жизни при закрытии. И они могут быть использованы повторно.
Ещё со времен РНР4 есть возможность задать $lifetime для сессионой куки
session_set_cookie_params ( int $lifetime [, string $path [, string $domain [, bool $secure = FALSE [, bool $httponly = FALSE ]]]] ) : bool

php.net/manual/ru/function.session-set-cookie-params.php
Я в свое время когда изучал php очень рад был найти такие статьи. Так как помню этот ад когда начинаешь проходить какую то тему и начинается что то с чем то. Когда куча ссылок и куча непонятной информации помимо основной темы и ты просто начинаешь прыгать по темам и теряться. Детальность это конечно хорошо но вспомните себя. Для новичка который хочет понять что такое сессия — хорошая статья.
При изучении сессий в свое время очень долго тупил по некоторым моментам (описание ситуация из 2004 года, а не сегодняшние):

1. Не работают сессии. О ней вы немного рассказали — нельзя выводить текст в браузер до старта сессии. Вообще никакой ни из какого файла — если старт сессии происходит в подключенном файле (include), то по пути до самой этой функции старта сессии ничего не должно выводиться в браузер. Я долго спотыкался о display_errors — когда о всех notice и warning инфа выдается в браузер (пример):

index.php
<?php

  if ($_GET["logout"]) {
    // code
  }

  session_start();


Пример больше надуманный, но сессия стартовать не будет, так как прилетит notice о том, что в массиве $_GET нет индекса logout — это неявная на первый взгляд ситуация. Но сам будучи новичком — нервов попила эта ситуация — я же нигде echo не вызываю!?

2. Я изначально думал, что сессии — это замена кукам, а потом сделал для себя «открытие», что для поднятие сессии можно идентификатор хранить в тех же куках, а не дергать его из параметров запроса в браузере на каждом файле. И что это дополняющие друг-друга функциональности.

3. Долго не мог понять, зачем вообще эти сессии помимо авторизации (разграничения доступа к определенному функционалу) — они ведь позволяют хранить вообще любые данные для конкретного пользователя на самом сервере, а не на клиенте. Но фишка как раз в том, что в сессии можно хранить данные, которые можно «передавать» между различными запросами, то есть что-то делал на одном шаге, сохранил состояние. Помню, что когда клепал каталог товаров с возможностью покупки, то хранил эти самые покупки в отдельном файлике — самописный функционал, файл назывался session_id.txt, то есть авторизацию проводил через сессию, а вот параметры хранил через свои костыли. До этого я просто не мог понять, читая статьи, что можно в сессиях хранить что-то больше, чем идентификатор. То есть для новичков пишут — хранить там можно все. А дальше нарисуй сову сам. А если бы объясняли на пальцах, что состояние корзины покупок мы можем хранить в массиве, который в свою очередь можем хранить в сессии и потом доставать из него штатными средствами php, чтобы решить задачу, а не изобретать костыли для ее решения.

4. Так же пояснять о том, что сессию «поднять» можно закрыв вкладку, но для этого нужно настроить куку, точнее ее время хранение и для какого домена (страниц) она должна отдаваться браузеру.

Я б не стал набирать комментарий, но вот кратко описал то, с чем столкнулся при изучении. Из статьи я тоже понял только то, что сессия позволяет распознавать пользователя на сайте. И все. Что там что-то можно хранить, но для чего это может понадобиться — не сказано. А если учитель привел бы конкретные примеры/ситуации или решение пусть того же to-do list через сессии — то есть завтра можно открыть вкладку и что-то дописать/отредактировать (потом как эту же задачу решить с помощью голого mysqli, затем как с помощью классов — PDO — тут же про иньекции и пр). Тут как раз можно было бы осветить большую часть возможностей, но уже вместе с практикой.

Отступление: еще в школьные годы, к бабушке-соседке приезжал внук: толковый парень, мог с механикой решить и аргументировать решение своей задачи — под каким углом срезать уголки, чтобы сварочный шов был крепче. По звуку мог определить с каким цилиндром что-то не то. И т. д. Но вот забить гвоздь не умел. Наточить нож тоже. Зато знал все в теории, потому что практика у него отсутствовала. Может не было мотивации, может еще что-то. Может он просто инженер-теоретик. Но суть мысли в другом — теория, закрепленная практикой — бесценна. Знания остаются в памяти более надолго.

Нужно знать для чего инструмент (сессия) — для решения каких задач — корзина покупателя, авторизация, передача параметров между запросами в пять дней между самими запросами и т.д. — а не просто вот вам есть такой инструмент. Бывают конечно и исключения из правил — синяя изолента и молоток с ломом, но это уже другая история.

Какое-то большое имхо получилось…

Статья неплохая получилась, но хотелось бы чуточку «для чего это нужно», а не просто сухой текст.

Автору респект. Когда то помогла статья про принеси, там более менее я смог понять что это и зачем. Так же ковырял фрэймворк товарища, чисто в научных целях.
Можно узнать оглавление будущих тем?

У меня в серии статей «PHP для начинающих» пока лишь 4-ре статьи:
— Сессия
— Подключение файлов
— Буфер вывода
— Обработка ошибок

Не думали подойти к обучению не стандартным способом, каких тысячи, что книги, что статьи? Например начинать с композера и с readme пакетов, строить свое используя чужие наработки. Думаю js чистый ни кто не пишет сейчас. Сразу берут все с пакетов

Ну книгу уже писал, ещё про jQuery :)

Мне нравится формат статей с акцентом на то, что обычно опускают в книгах. Например рассказ про сессии без привязки к HTTP считаю как раз таким моментом, и про то где же хранятся данные сессий и как с ними можно работать. Интересно получать фидбек и доводить статьи «до блеска», так что я очень положительно смотрю на фидбеки в комментариях (как например andreymal писал выше).

Мне интересно воспитывать разработчиков, а не пользователей фреймворков. Писать про пакеты и наработки — этим должны авторы фреймворков и пакетов заниматься, мне же за изменениями не успеть.
А как же кэширование? Или это рассматривается в «Буфере вывода»?
Предложения:
1. Про подключение файлов — неплохо бы было затронуть тот факт, что подключаемый файл может возвращать результат, а то глаза были квадратными, когда я впервые увидел конструкцию $data = require 'config.php';
2. Если будете рассказывать про операции с файлами, то неплохо уделить внимание, что работать можно с контекстом — то есть контекст — интерфейс ввода вывода, или если на пальцах — писать/читать можно в сокет, буфер, файл, что это одно и тоже по-сути, только трансфер данных идет на разном уровне (протоколе).
3. Для буфера — разницу между ob_flush, что он не везде сразу выводит данные в браузер, что буферизацию можно использовать для пост обработки данных (по сути создавать фильтры и модули в проекте без изменения ядра), создания виджетов на сайте, что ее не нужно использовать везде и всякий раз.
4. Обработка ошибок — FILE_APPEND для file_put_content, перехват throw new Exception('error text') в блоке try catch, var_export, своя раскраска debug_backtrace. Что фатальные ошибки (ошибки парсинга) не перехватываются и пр.

Обязательным считаю рассказать новичкам про форматирование кода при наборе — сразу же, 0-м пунктом. Чтобы приучать к порядку. Еще бы и IDE бы настраивать сразу. Потому как связка FileZilla --> Notepad++ (Sublime) --> FileZilla отнимает очень много времени от решения задач. Еще в IDE есть сразу подсказки, есть ссылки на онлайн документацию — причем она же на русском языке. Раньше таких средств не было, книги и чужие исходники. Это очень не хватало раньше. А то открываем блокнот… Расскажите, что нет необходимости уже закрывать файл ?>. Что функция — это ключ на 10, который откручивает только гайку на 10, что набор ключей — это несколько функций, которые решают определенный тип задач и их можно выгрузить в отдельный модуль — пространство имен. Что такой же ключ в другом чемодане — это уже другое пространство имен. То есть одна функция от разных авторов, выполняющая туже задачу. Что железный конструктор, о котором в 90-х мечтали — гайки, болты, штанги, колеса — это просто переменные, разные. Даже можно сказать — что гайка — это константа, то есть переменная, значение которой никогда не меняется. Что ключ, отвертка — это функции для работы с данными (гайками), по сути это уже объект (класс) — содержит данные и инструменты (функции) для работы с ними. Конструктор — и есть класс. Объект класса — это конкретный конструктор. Вот из конструктора можно сделать разные детали — это потомки объекта. Сам конструктор как идея — это интерфейс, если дальше проводить аналогию. То есть он предполагает наличие деталей и инструментов для работы с ними. Два обязательных условия, о которых мы просто заявили. А вот в каждом конструкторе есть свой набор деталей — у железного один набор, у лего — другой, у паззла — третий. Все они идут в коробках (интерфейсах). Проданный конструктор одному клиенту — это как создание объекта — new ClassName. И вот клиент ушел с покупкой делать свои дела и магазину неважно, так и менеджер памяти сам следит за кол-вом ссылок на объект. Вот как-то на примитиве хочется все.
фатальные ошибки (ошибки парсинга) не перехватываются и пр.
Это не совсем верно.
register_shutdown_function вызовется по завершении скрипта даже если была ошибка парсинга.
При этом если Вы ловите буфер через ob_start то внутри register_shutdown_function можете посмотреть и обработать соответственно контент перед выдачей юзеру, там же заодно и ошибку увидеть при display_errors=1.
Кроме того, debug_backtrace поможет Вам определить где она произошла и что к этому привело.
Т.е. продолжить работу скрипта Вы не сможете, но обработать ошибку парсинга на достаточно высоком идейно-художественном уровне можно.
Если использовать PHP7, то всё становится куда как проще:
try {
    require_once 'file-with-parse-error.php';
} catch (\Throwable $e) {
    // display error page
}
Статьи про сессии от новичков в написании обучающих статей всегда можно определить по отсутствию в них хоть пары букв про закрытие сессий. По нашему опыту в эти грабли (с закрытие сессии) вляпывается каждый первый, при чем именно благодаря игнорированию в обучающих статьях закрытия сессий.

Допустим Вы открыли сессию, скрипт у Вас работает 10 секунд (не важно почему, так получилось). А пользователь в то время пытается открыть другую страницу Вашего сайта. Что будет? Если Вы не закрыли сессию, то другая страница откроется только после того, как полностью отработает первая. Почему? Потому что Вы не закрыли сессию!
Кажется мелочью? Но не тогда, когда у Вас сайт грузится 0.5 секунды, а юзер открыл 10 вкладок и они у него висят по 1-5 секунд.
Или когда повисла одна вкладка надолго, а юзер пытается посмотреть другие страницы сайта и ждет, ждет, ждет открытия.
А еще на некоторых сайтах всякие цсс стили и некоторые картинки отдаются скриптом, при чем разумеется, со стартом сессии. Как долго будет грузиться это? А если открыто несколько страниц?

А ведь в мануале написано же session_write_close
данные сессии заблокированы для предотвращения одновременной конкурирующей записи, только один скрипт может работать с сессией в любой момент времени


И даже в мануале по session_start об этом упоминается, хотя и не так явно
// Если мы знаем, что в сессии не надо ничего изменять,
// мы можем просто прочитать ее переменные и сразу закрыть,
// чтобы не блокировать файл сессии, который может понадобиться другим сессиям
Спасибо за ценное дополнение, видать я эти грабли так давно прошёл, что уже забыл про них =)
Добавил раздел про блокировку
Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.