Не храните токен как cookie.
Создайте уникальный токен на каждой странице показа. После POST убедитесь, что вы выдали токен (этому пользователю) и убедитесь, что он не использовался ранее.
Сначала мы генерируем токен, где мы можем проверить, что мы выдали его для данного пользователя:
token = hash(session_id + secret)
Таким образом, используя их идентификатор сеанса и наш секрет, мы всегда можем проверить, что мы выдали этот токен, поскольку никто не знает секрета.
Теперь нам нужно убедиться, что токен можно использовать только один раз.
rnd = rand()
token = rnd + hash(session_id + secret + rnd)
У токена теперь есть случайное число. Когда происходит POST, мы можем сохранить это случайное число как «ранее использовалось» и отклонить любой токен, который повторно использует то же случайное число.
Но мы не хотим вечно хранить случайные числа использованных токенов. Поэтому мы ограничиваем время жизни токенов.
rnd = rand()
now = time()
token = rnd + time + hash(session_id + secret + rnd + time)
На POST, когда мы получаем токен, мы теперь проверяем, выпустили ли мы его «недавно». Нам нужно только хранить использованные случайные числа для того же времени. Все старые токены недопустимы по определению.
Вы можете сохранить использованные случайные числа вместе с идентификатором сеанса и удалить их, когда вы выселяете идентификатор сеанса или когда они становятся недействительными (в зависимости от того, что произойдет раньше).