Безопасные и гибкие междоменные сеансы

У меня возникла проблема, на которую, надеюсь, вы сможете помочь. Предположим, я работаю для гипотетической компании под названием «Blammo», и у нас есть гипотетический продукт под названием «Log». Я пытаюсь создать систему, в которой кто-то может войти в logfromblammo.com и заказать некоторые из наших продуктов, а затем, когда они будут готовы купить, перейдите на checkout.blammo.com, чтобы оплатить их заказ. В конце концов, я хочу, чтобы Blammo запустил новый гипотетический продукт со своим собственным сайтом: rockfromblammo.com, и этот сайт также может поделиться сеансом с checkout.blammo.com, чтобы пользователи могли иметь одну корзину покупок по обоим продуктам веб-сайты.

Естественно, гипотетический сценарий, описанный выше, – это не то, как моя компания работает, но это прекрасный пример того, что мне нужно сделать. У нас есть существующая пользовательская база данных, и у нас есть способы аутентифицировать любого из наших пользователей на любом из наших сайтов, но цель, которую я имею, – позволить пользователям беспрепятственно переходить с одного сайта на другой без повторной аутентификации. Это также позволило бы нам беспрепятственно передавать данные, например, корзину покупок на сайт проверки.

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

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

Самый простой способ – передать идентификатор сеанса через строку запроса; например

 http://whateverblammo.com/?sessid=XXYYZZ 

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

Это не значит, что это безопасно; во-первых, пользователи могут случайно скопировать / вставить адресную строку и тем самым протестировать сеанс. Чтобы ограничить эту экспозицию, вы можете сразу перенаправить на страницу без идентификатора сеанса после ее получения.

Обратите внимание, что использование mcrypt() в идентификаторе сеанса не поможет, потому что это не видимость значения, это проблема; захват сеанса не заботится о базовом значении, а только о его воспроизводимости URL-адреса.

Вы должны убедиться, что идентификатор можно использовать только один раз; это можно сделать, создав переменную сеанса, которая отслеживает счет использования:

 $_SESSION['extids'] = array(); $ext = md5(uniqid(mt_rand(), true)); // just a semi random diddy $_SESSION['extids'][$ext] = 1; $link = 'http://othersite/?' . http_build_query('sessid' => session_id() . '-' . $ext); 

При получении:

 list($sid, $ext) = explode('-', $_GET['sessid']); session_id($sid); session_start(); if (isset($_SESSION['extids'][$ext])) { // okay, make sure it can't be used again unset($_SESSION['extids'][$ext]); } с list($sid, $ext) = explode('-', $_GET['sessid']); session_id($sid); session_start(); if (isset($_SESSION['extids'][$ext])) { // okay, make sure it can't be used again unset($_SESSION['extids'][$ext]); } 

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

Это можно сделать, но не с помощью простых куки, и это не тривиально. То, что вам нужно, – это решение единого входа (SSO), похожее на имя пользователя Google, на котором есть доступ к сайтам i.google.com, gmail.com, youtube.com и т. Д.

Я использовал OpenID для реализации этого в прошлом.

Основная идея состоит в том, чтобы иметь единый домен аутентификации (Provider), когда один из сайтов (Consumer) хочет аутентифицировать пользователя, они перенаправляют их в домен аутентификации. Если они не вошли в систему, они могут войти в систему, используя любую необходимую информацию.

Если они уже вошли в систему (даже с другого целевого сайта), им не нужно снова входить в систему.

Затем пользователь отправляется обратно на целевой сайт с добавлением токена в URL-адресе. Этот токен используется сервером целевого сайта для проверки подлинности пользователя на сервере аутентификации.

Это очень простое объяснение. Делать это не сложно, сделать это безопасно гораздо больше. Детали создания и аутентификации токенов безопасно – сложная часть. Именно поэтому я предлагаю использовать хорошо продуманную систему, такую ​​как OpenID.

Вам необходимо установить домен cookie сеанса следующим образом:

 session_set_cookie_params($lifetime,$path,'.site.com') 

Это будет работать, только если сайты находятся на одном доменном имени, включая TLD (домен верхнего уровня).

См. Здесь для получения дополнительной информации

В качестве альтернативы, если вы пытаетесь получить доступ к перекрестным доменам сеансов, например, от site1.net до site2.com , это невозможно.

Если это нормально для вашего сайта, чтобы полагаться на Javascript для работы, вы могли бы предположительно сделать что-то вроде следующего:

Скажем, у вас есть сеанс на blammo.com, и вы хотите получить к нему доступ с сайта rockblammo.com. На странице rockblammo.com вы можете загрузить <script> из blammo.com/get-session.js , это будет (со стороны сервера) вернуть идентификатор сеанса. Как только это вернется, вы rockblammo.com/set-session.js?sessionId=XXX новый <script> на странице, указав на rockblammo.com/set-session.js?sessionId=XXX , где XXX – это идентификатор сеанса, который вы только что получили от blammo.com. Теперь, на стороне сервера rockblammo.com, cookie сеанса обновляется и устанавливается на этот идентификатор сеанса. В дальнейшем обе страницы будут иметь один и тот же идентификатор сеанса, и если они будут иметь доступ к тому же хранилищу сеансов на бэкэнд, они будут синхронизированы.

Например, выход из blammo.com/get-session.js будет:

 var sessionId = "XXX"; var s = document.createElement("script"); s.src = "/set-session.js?sessionId=" + escape(sessionId); document.body.appendChild(s); 

Выход из rockblammo.com/set-session.js будет пустым, но будет включать заголовок http, например:

 Set-Cookie: sessionId=XXX 

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