mod: согласие на отслеживание совмещено с фингерпринтом браузера UUIDv4

This commit is contained in:
2026-08-22 15:55:39 +03:00
parent 2bec2e860e
commit cc3a17e792
4 changed files with 23 additions and 9 deletions
+11 -3
View File
@@ -332,9 +332,17 @@ HTMX: форма с `hx-post`, `hx-encoding="multipart/form-data"` → SVG вс
#### 4.1. Принципы приватности, Zero-PII и учет голосов
- **Полное отсутствие регистрации для посетителей:** Пользователи не создают аккаунты, не вводят email, пароли или личные данные. Единственный пользователь в `auth.User` — администратор сайта для входа в Django Admin.
- **Исходные растровые изображения (JPG/PNG/WEBP):** Обрабатываются исключительно в оперативной памяти (через буфер `BytesIO`) и **не сохраняются на диск сервера вообще**.
- **Куки-согласие и идентификация устройства (Cookie-токен):**
- При согласии с правилами («Подчиниться Гипножабе!» в `allow-tracking.html`) клиенту выставляется долгоживущая анонимная кука устройства `hypn0_vid` со случайным UUID (`Max-Age=2 года`, `SameSite=Lax`, `HttpOnly`).
- **Голосование доступно только при наличии согласия на куки:** Если посетитель не принял соглашение (кука отсутствует), голосование заблокировано интерфейсом и отклоняется бэкендом (с требованием подчиниться Гипножабе).
- **Куки-согласие и идентификация устройства (Cookie `hypn0_vid`):**
- При согласии с правилами («Подчиниться Гипножабе!» в `allow-tracking.html`) в браузере генерируется случайный UUIDv4 (`vid`), который сохраняется в долгоживущую анонимную куку `hypn0_vid` (`Max-Age=2 года`, `SameSite=Lax`) и дублируется в `localStorage`.
- **Единый статус согласия и идентификации:**
- Наличие куки `hypn0_vid` одновременно означает:
1. Посетитель дал согласие на условия / трекинг (`ALLOW_TRACKING = 'hypn0_vid' in request.COOKIES` в `context_processors.py`).
2. У бэкенда есть анонимный `visitor_uuid` для формирования цифрового отпечатка `s_fingerprint` в `TbVote`.
- **Правила для пользователей без подчинения Гипножабе (без куки `hypn0_vid`):**
- Пользователь **может** загружать изображение, экспериментировать с настройками, генерировать и скачивать SVG-файл на свое устройство (чистая локальная работа).
- Пользователь **НЕ может** публиковать картину в галерею или голосовать за чужие работы:
- Публикация картины на сервер требует немедленного создания авторской записи `TbVote(i_direction=Direction.AUTHOR)` для закрепления авторства, защиты от самолайка и стартового рейтинга.
- Без согласия и UUID сформировать `s_fingerprint` невозможно $\rightarrow$ кнопка «Опубликовать в галерею» и лайки заблокированы в интерфейсе (с всплывающим требованием подчиниться Гипножабе) и отклоняются бэкендом (HTTP 403).
- **Учет голосов, защита от накрутки и определение авторства (`TbVote`):**
- **Бэкенд:** Голоса учитываются анонимно через модель `TbVote` со связкой `(k_item, s_fingerprint)`. Поле `s_fingerprint` формируется как необратимый хэш: `sha256(visitor_uuid + SECRET_KEY)`. Это надежно разделяет разные компьютеры в одной корпоративной сети/NAT и не содержит открытых ПДн (Zero-PII).
- **Направление голоса (`i_direction` / `Direction`):**
+6 -4
View File
@@ -1,10 +1,12 @@
from django.conf import settings
from django.http import HttpRequest
# Имя куки, которая ставится клиенту, когда он СОГЛАСИЛСЯ на отслеживание
# (Google Tag Manager, Яндекс.Метрика и т.п.). Если кука присутствует —
# предупреждение о трекинге больше не показываем.
ALLOW_TRACKING_COOKIE_NAME = "allow_tracking"
# Имя куки с UUID устройства посетителя. Устанавливается при согласии
# с условиями ("Подчиниться Гипножабе!").
# Одновременно служит:
# 1) Флагом согласия на отслеживание (ALLOW_TRACKING).
# 2) Анонимным идентификатором устройства для учета голосов (TbVote) без сбора ПДн.
ALLOW_TRACKING_COOKIE_NAME = "hypn0_vid"
def site_context(request: HttpRequest) -> dict:
+2 -2
View File
@@ -1,4 +1,4 @@
{# НИЖНИЙ POPUP О КУКУХ И ОТСЛЕЖИВАНИИ #}<div id="allow-tracking-popup" class="sticky bottom-0 z-50 bg-gray-100/80 dark:bg-slate-800/70 backdrop-blur border-t border-gray-300 dark:border-gray-700">
{# НИЖНИЙ POPUP О КУКУХ И ОТСЛЕЖИВАНИИ #}<div id="allow-tracking-popup" x-data="{ agreed: !!localStorage.getItem('hypn0_vid') }" x-show="!agreed" x-cloak class="sticky bottom-0 z-50 bg-gray-100/80 dark:bg-slate-800/70 backdrop-blur border-t border-gray-300 dark:border-gray-700">
<div class="max-w-7xl mx-auto px-4 py-8 grid grid-cols-[auto_1fr] gap-x-4">
<p class="flex-col px-6 py-6">
Внимание! В&nbsp;соответствии с&nbsp;152-<abbr title="Федеральный закон">ФЗ</abbr>
@@ -8,6 +8,6 @@
впадаете в&nbsp;транс и&nbsp;принимаете условия нашей <a href="/info/privacy-policy">политики
коконфи&shy;денциаль&shy;ности</a>. <strong>Не&nbsp;пытайтесь переключить вкладку!</strong>
</p>
<button x-data @click="document.cookie='allow_tracking=1; path=/; max-age=31536000'; $el.closest('#allow-tracking-popup').remove()" class="text-lg px-6 py-2 my-6 border-2 rounded-md bg-emerald-600 hover:bg-emerald-500 dark:bg-emerald-500 dark:hover:bg-emerald-400 text-white">Подчиниться Гипножабе!</button>
<button @click="const vid = crypto.randomUUID(); localStorage.setItem('hypn0_vid', vid); document.cookie = `hypn0_vid=${vid}; path=/; max-age=63072000; SameSite=Lax`; agreed = true" class="text-lg px-6 py-2 my-6 border-2 rounded-md bg-emerald-600 hover:bg-emerald-500 dark:bg-emerald-500 dark:hover:bg-emerald-400 text-white">Подчиниться Гипножабе!</button>
</div>
</div>{# / НИЖНИЙ POPUP О КУКУХ И ОТСЛЕЖИВАНИИ #}
+4
View File
@@ -74,6 +74,10 @@
добавить border-0 (убирает пунктир) и свой text-slate-900 dark:text-
slate-100 (задаёт свой цвет вместо цвета типовой ссылки) — это и сделано
в block/header.html.{% endcomment %} */
[x-cloak] {
display: none !important;
}
a {
@apply border-b border-dotted
/* В светлой теме: глубокий изумрудный, в темной: блеклый лайм (отлично читается) */