Статьи

Создание одноразовых URL-адресов использования

Одноразовый URL-адрес — это специально созданный адрес, действительный только для одного использования. Обычно он предоставляется пользователю для получения привилегированного доступа к файлу в течение ограниченного времени или как часть определенного действия, такого как проверка учетной записи пользователя. В этой статье я покажу, как создавать, реализовывать и истекать одноразовые URL-адреса.

Создание URL

Допустим, нам было поручено написать компонент управления пользователями, и проект создает запись для новой учетной записи пользователя в базе данных. После регистрации пользователь получает подтверждение по электронной почте с одноразовым URL-адресом для активации своей учетной записи. Мы можем сделать этот URL-адрес одноразовым, включив параметр отслеживаемого токена, что означает, что URL будет выглядеть следующим образом:

  http://example.com/activate?token= ee97780 ... 

Неудивительно, что отслеживание одноразового URL-адреса выполняется путем сохранения информации в таблице базы данных. Итак, начнем с определения таблицы:

CREATE TABLE pending_users ( token CHAR(40) NOT NULL, username VARCHAR(45) NOT NULL, tstamp INTEGER UNSIGNED NOT NULL, PRIMARY KEY(token) ); 

В таблице хранится соответствующее имя пользователя, уникальный токен и метка времени. Я покажу, как сгенерировать токен с помощью функции sha1() , которая возвращает строку из 40 символов, следовательно, емкость поля токена tstamp 40. Поле tstamp — это целое поле без знака, используемое для хранения отметки времени, указывающей, когда токен был сгенерирован и может использоваться, если мы хотим реализовать механизм, с помощью которого токен истекает через определенное время.

Есть много способов сгенерировать токен, но здесь я просто буду использовать функции uniqid() и sha1() . Независимо от того, как вы решите сгенерировать свои токены, вам нужно, чтобы они были непредсказуемыми (случайными) и имели низкий шанс дублирования (коллизии).

 <?php $token = sha1(uniqid($username, true)); 

uniqid() принимает строку и возвращает уникальный идентификатор, основанный на строке и текущем времени в микросекундах. Функция также принимает необязательный логический аргумент, чтобы добавить дополнительную энтропию, чтобы сделать результат более уникальным. Функция sha1() вычисляет хэш заданной строки с использованием алгоритма безопасного хэша США 1.

После выполнения функций у нас есть уникальная строка из 40 символов, которую мы можем использовать в качестве токена для создания одноразового URL. Мы хотим записать токен вместе с именем пользователя и отметкой времени в базе данных, чтобы мы могли ссылаться на него позже.

 <?php $query = $db->prepare( "INSERT INTO pending_users (username, token, tstamp) VALUES (?, ?, ?)" ); $query->execute( array( $username, $token, $_SERVER["REQUEST_TIME"] ) ); 

Очевидно, что мы хотим сохранить токен, но мы также сохраняем имя пользователя, чтобы помнить, какого пользователя активировать, и временную метку. В реальном приложении вы, вероятно, сохраните идентификатор пользователя и будете ссылаться на запись пользователя в отдельной пользовательской таблице, но я, например, использую строку имени пользователя.

С информацией, безопасно помещенной в базу данных, мы можем теперь создать наш одноразовый URL, который указывает на скрипт, который получает токен и обрабатывает его соответственно.

 <?php $url = "http://example.com/activate.php?token=$token"; 

URL может быть передан пользователю по электронной почте или каким-либо другим способом.

 <?php $message = <<<ENDMSG Thank you for signing up at our site. Please go to $url to activate your account. ENDMSG; mail($address, "Activate your account", $message); 

Использование URL

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

 <?php // retrieve token if (isset($_GET["token"]) && preg_match('/^[0-9A-F]{40}$/i', $_GET["token"])) { $token = $_GET["token"]; } else { throw new Exception("Valid token not provided."); } // verify token $query = $db->prepare("SELECT username, tstamp FROM pending_users WHERE token = ?"); $query->execute(array($token)); $row = $query->fetch(PDO::FETCH_ASSOC); $query->closeCursor(); if ($row) { extract($row); } else { throw new Exception("Valid token not provided."); } // do one-time action here, like activating a user account // ... // delete token so it can't be used again $query = $db->prepare( "DELETE FROM pending_users WHERE username = ? AND token = ? AND tstamp = ?", ); $query->execute( array( $username, $token, $tstamp ) ); 

Если пойти дальше, мы могли бы установить 24-часовой TTL (время жизни) для покупки URL, проверяя временную метку, хранящуюся в таблице рядом с токеном.

 <?php // 1 day measured in seconds = 60 seconds * 60 minutes * 24 hours $delta = 86400; // Check to see if link has expired if ($_SERVER["REQUEST_TIME"] - $tstamp > $delta) { throw new Exception("Token has expired."); } // do one-time action here, like activating a user account // ... 

Работая в области временных отметок Unix, срок действия будет выражен в виде смещения в секундах. Если предполагается, что URL действителен только в течение 24 часов, у нас есть окно 86 400 секунд. Чтобы определить, истек ли срок действия ссылки, становится просто сравнить текущее время с исходной временной меткой и посмотреть, меньше ли разница между ними, чем дельта истечения. Если разница больше дельты, срок действия ссылки должен истечь. Если разница меньше или равна дельте, ссылка все еще «свежая».

Вывод

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

Как правило, вы можете написать дополнительный сценарий, чтобы токены с истекшим сроком не накапливались в базе данных, если пользователь никогда не следует за ними. Сценарий может периодически запускаться администратором или предпочтительно настраиваться как запланированное задание или задание cron и запускаться автоматически.

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

Изображение через Fotolia