Перейти к содержимому
0 0 ₽ Корзина

Корзина

Загрузка…
0
Обратный звонок
Студия веб дизайна dharma design
Руководство разработчика

Проверки безопасности

Проверка выполняется повторяемо: dev/security-upload.sh (после входа dev/web-login.sh) — атаки на загрузчик изображений, ожидание «OK» у каждой строки. Любая новая точка входа (загрузка, AJAX, действие из списка, консольная команда) получает здесь запись и проверку.

Загрузчик изображений (MediaController::upload, ImageUploader)

УгрозаЗащитаПроверено
CSRFтокен формы Joomla только в теле POST (Session::checkToken('post')); GET-запросы отклоняются; ответ no-store, nosniffбез токена, токен в адресе, чужой токен, GET — отказ
Доступ без праввход в админку (иначе 403), core.create или core.edit компонентагость — 403
Подмена типа (PHP под видом картинки)расширение из белого списка (настройки «Медиа» ∩ наш список), содержимое проверяется finfo и getimagesizeтекст PHP как .png, файл .php — отказ
«Полиглот» (валидный PNG + PHP в конце)файл с <?php отклоняется; расширение после сохранения только из белого списка; в папке загрузок .htaccess запрещает исполнение скриптовPNG с хвостом — отказ
Обход путейимя файла — транслит [a-z0-9-], расширение приводится к нижнему регистру; папка — только из списка (categories, icons, seo, products, gallery, common, иное → common); корневая папка настроек очищается от .., кириллицы, пустых сегментов../../evil.png, папка ../../../tmp
SVG (скрипты, события, внешние ссылки)штатный санитайзер Joomla (enshrined/svg-sanitize, внешние ссылки убираются), отказ при @import/внешнем url(), <!ENTITY; SVG записывается уже очищенным; при прямом открытии — Content-Security-Policy: sandboxonload через /, javascript: в виде HTML-сущности, @import
Гонка имёнимя занимается атомарно (fopen в режиме x)—
Огромные изображенияплощадь не более 80 млн пикселей, размер — меньший из «Медиа» и PHP—
Конвертация WebPПри включённом image_convert_webp JPEG/PNG/AVIF преобразуются через Joomla\CMS\Image\Image; расширение назначается .webp только после проверки MIME и исходных размеров. SVG, GIF (анимация) и BMP не конвертируются; сбой обработки отклоняет загрузку.Реализация добавлена; поведенческий сценарий загрузки ещё не прогонялся
Переход старых ссылок на WebPКоманда в списке товаров требует CSRF-токен и core.manage, обрабатывает ссылки порциями, ограничивает файлы корнем сайта, проверяет MIME/размер и не перезаписывает оригиналы или существующие WebP. Обновляет обложки, галереи, SEO-картинки, параметры категорий и корневых страниц; неподдерживаемые форматы оставляет без изменений.На test.test команда завершилась успешно: последняя порция перевела 1 ссылку, ошибок 0; WebP проверен по MIME, исходный JPEG сохранён
Подстановка пути в формуфильтр ImageUploader::filterPath у всех полей imageupload: только images/… с допустимым расширением, без .., схем (javascript:, http:), кавычекjavascript:, images/../configuration.php, http://…, .php, кавычки — обнуляются; допустимые пути сохраняются
Удаление чужогоImageCleaner удаляет только файлы внутри папки загрузок и только если на них не ссылается другая запись—
Утечка внутренних сообщенийнепредвиденные ошибки — в журнал, пользователю общий текст—

Ограничения и задел: WebP включается отдельно для новых JPEG/PNG/AVIF; ранее загруженные ссылки меняются только при ручном запуске команды. SVG остаётся векторным, GIF сохраняет анимацию, BMP и неподдерживаемые Joomla форматы остаются исходными. Нарезка изображений и удаление скрытых метаданных пока не реализованы; на nginx .htaccess не действует — нужен запрет исполнения скриптов в images/ в настройках сервера.

Прочие точки входа

Действия списков и команды обслуживания (rebuild, cmdIndex, cmdProducts, cmdSales, пакетная обработка, избранное) проверяют токен и права (core.manage, core.edit/core.create); проверено чтением кода, в dev/security-upload.sh не входит.

Поиск и подсказки (SearchController::suggest, адрес поиска filter[search])

УгрозаЗащитаПроверено
Внедрение SQL через запросзапрос очищается (управляющие символы, длина ≤ 100), слова нормализуются до букв и цифр, значения только параметрами (bind), в булев FULLTEXT-запрос попадают только буквы и цифры; артикул — экранирование % _ \запросы '<script>, %, a\0b, a'b — без ошибок и совпадений
Синонимы и варианты словправила синонимов из настроек (доступ только администратору) разбираются и нормализуются до букв и цифр, варианты (раскладка, транслитерация) строятся только из букв; число вариантов слова ≤ 8, слов запроса ≤ 8; все значения — параметрами—
Перегрузкапредел числа подсказок (≤ 20), задержка и отмена запроса на клиенте, минимум символов—
Скрытые товарызапрос присоединяется к обычному запросу каталога: опубликованные товары в опубликованных и доступных категориях—
XSS в подсказкахскрипт подставляет значения через textContent, подсветка — DOM-узлами (mark), innerHTML не используется; ссылки и картинки — из ответа сервера (экранированные адреса)запрос с разметкой выводится текстом
Кэш и индексацияответ no-store, X-Robots-Tag: noindex; страницы списка с filter[search] — noindex, follow—

Раздел «Корзины» и ключи доступа (админка, ссылка в письме)

УгрозаЗащитаПроверено
Просмотр чужой корзины по ключу/korzina?cart=<токен> работает только при праве dharmashop.orders.manage (иначе параметр игнорируется), только чтение, noindex, Referrer-Policy: no-referrer; просроченную корзину не открытьгость с токеном видит свою (пустую) корзину; администратор — состав в режиме «только чтение», кнопки отключены, скрипт не подключён
Подбор ключа гостевого заказаключ — 128 бит (bin2hex(random_bytes(16))), сравнение в SQL по уникальному индексу, 404 при несовпадении; ключ действует только для гостевых заказов (user_id IS NULL); страница заказа с no-store и Referrer-Policy: no-referrerверный ключ — 200, чужой/пустой/ключ другого заказа — 404
CSRF на удаление и очистку корзинPOST + токен формы, право core.delete и dharmashop.orders.manageудаление выбранной и очистка просроченных отработали, остальные корзины не затронуты
Секреты в админкетокены и ключи видны только пользователям с правом управления заказами—

Привязка гостевых заказов к аккаунту (OrdersModel::claimGuestOrders)

УгрозаЗащитаПроверено
Захват чужих заказов регистрацией с чужой почтойпо умолчанию привязка только при подтверждённой почте (активация по письму включена и пройдена, пользователь не заблокирован); режим «по любому совпадению» помечен небезопаснымпри «Активации: нет» заказ не привязывается; режим «любой» привязал заказ с совпавшим e-mail (после проверки возвращён)
Лишние запросыодин индексированный UPDATE (по contact_email) раз за сессию—

Мои заказы (Site\Model\OrdersModel, вид orders)

УгрозаЗащитаПроверено
Чужие заказызапрос ограничен orders.user_id = номер вошедшего пользователя (привязка параметром), гость получает пустой список и приглашение войти; состав и контакты в списке не читаются; просмотр заказа — прежняя проверка OrderModelпользователь видит свои заказы и открывает их; чужой заказ — 404; гость на странице — перенос на вход
Кэшстраница no-store, private, noindex, nofollow—

Корзина (CartController: cart.add / update / remove / clear)

УгрозаЗащитаПроверено
CSRFтолько POST и токен формы Joomla (Session::checkToken('post')); GET и чужой токен — отказ (JSON success: false)запрос без токена, GET с токеном, чужой токен — отказ
Внедрение через идентификаторы и числаproduct_id, variant_id, item_id приводятся к целым, все запросы — с привязкой параметров (bind); количество — число, округление до 3 знаков, проверка минимума/шага/максимума/остаткаproduct_id=205' OR 1=1-- → 205 (целое), отрицательный id — «товар недоступен», qty=abc — «меньше минимального», qty=1e9 — «нет в наличии»
Чужая корзинакорзина определяется токеном cookie (32 hex, HttpOnly, SameSite=Lax, Secure на HTTPS) или user_id; строки другой корзины недоступны (cart_id в каждом запросе)—
Режим каталогапри catalog_mode = 1 все действия отклоняются, страница корзины — 404, модуль и скрипты корзины не подключаютсяпроверяет владелец (настройки через базу не меняются)
Скрытые товарыдобавить можно только опубликованный товар в опубликованных и доступных категориях (как на странице товара)снятый и несуществующий товар — «недоступен»
Фрагмент мини-корзины (format=raw)показ определяют настройки модуля, читаемые из базы по номеру mid (только опубликованный модуль mod_dharmashop_cart сайта; параметры показа через адрес не принимаются); классы кнопок очищаются (буквы, цифры, «-», «_») и экранируются; данные корзины — те же, что у страницы (по токену cookie)запрос с чужим mid — значения по умолчанию; класс с кавычкой — вычищается
Окно выбора товара (products&layout=modal)админка, доступ как к списку товаров (core.manage), токен в адресе поля, значения выводятся через escapeзапрос без входа — форма входа

Посадочные страницы (com_dharmashop_landing)

  • Админка: все задачи (page.*, pages.*, включая pages.check) проверяют токен и права компонента (core.manage, core.create, core.edit, core.edit.state, core.delete); pages.check принимает токен только в POST. Страницы вне админки не пишут данные.
  • Сайт: страница открывается, только если она опубликована (state = 1), не помечена broken, доступна по уровню доступа и вся ветка категории-источника открыта посетителю (Service\CategoryAccess); иначе 404. Список страниц и редирект со страницы фильтра применяют те же условия.
  • Адреса: для неопубликованной, закрытой или несуществующей страницы маршрутизатор не раскрывает алиас (сегмент — номер, ответ — 404). Смещение start за последней страницей списка товаров или списка страниц — 404 (без дублей адресов).
  • Условия страницы: строка запроса не вводится, значения выбираются из справочников и проходят FilterService::state() (неизвестные алиасы отбрасываются, лимиты полей и значений общие с фильтром).
  • Фильтр на странице: filter.refresh и AJAX-фрагмент (format=raw) — те же ограничения, что у категории (только модуль mod_dharmashop_filter с указанным номером; вид должен быть из списка разрешённых).
  • Аудит безопасности 2026-10-13: AUDIT_SECURITY_2026-10-13.md.

Исправления по аудиту 2026-10-13 (AUDIT_SECURITY_2026-10-13.md)

НаходкаРешение
С1. Привязка гостевых заказов по e-mail обходилась сменой e-mail в профилепо умолчанию режим link: привязка только действием «Привязать заказ к аккаунту» на странице заказа (OrderController::claim, POST + токен, секретный ключ заказа из письма, заказ без владельца); автопривязка по e-mail оставлена как устаревшие режимы с предупреждением
С2. Песочница оплаты включалась при установкеплагин не включается установщиком; в списке способов оплаты опубликованная песочница показывается предупреждением; PaymentService::handle не засчитывает оплату с суммой, не равной сумме заказа
С3. Рост кэша фильтра от анонимных запросовFilterCache::remember(..., $cacheable): запросы с текстом поиска и с более чем тремя условиями фильтра считаются без кэша (CategoryModel::cacheableRequest, ScopedFacets); проверено: 12 запросов с уникальным поиском и сочетаниями не создали ни одного файла
Н1. Алиасы скрытых записей в 301маршрутизатор ядра и посадочных страниц не отдаёт алиас неопубликованного товара, закрытой категории и закрытой страницы (сегмент — номер → 404)
Н2, Н3. Посадочные страницысм. раздел «Посадочные страницы» выше
Н4. Гостевое оформление без защитыSupport\RateLimiter (файловый кэш, окна по часу): заказов с одного адреса и на один e-mail (настройки, 10 и 3 по умолчанию; считаются только проверенные заказы), невидимое поле-ловушка
Н5. Различимые ответы payment.callbackединый ответ 400 для «нет заказа», «чужой способ» и неверной подписи
Н6. Лимит динамики доставки и оплаты по сессиидобавлен лимит по адресу клиента (RateLimiter)
Н8. DDL при каждом обращении к избранномутаблицы создаёт установка; при обращении выполняются два пустых SELECT, создание — только если таблиц нет
И1. nosniffвсе JSON-ответы витрины отдают X-Content-Type-Options: nosniff
И3. Токен в GETpages.check — только POST
И5. Cookie избранного на каждой страницеcookie гостю ставится только при создании списка
Гипотеза про qtyколичество в корзине — конечное число от 0 до 1 000 000 (CartService::normalise)
Не исправленоН7 (обновление JS-библиотек доверяет CDN: нужен core.admin, токен и клик администратора; хеши в пакете — отдельная задача); И2 и серверные настройки — PRODUCTION_CHECKLIST.md

Права доступа (C90, 2026-10-13)

УгрозаЗащитаПроверено
Менеджер товаров меняет структуру каталогаполя, группы полей, группы товаров — отдельные действия; контроллеры требуют их на входе (RequiresActionTrait, 403), виды и модальные окна проверяют то же; общие «Создать»/«Править» не заменяютdev/acl-checks.php: поведение трейта с заглушками, наличие защиты у всех контроллеров и видов
Смена статуса любым, кто видит заказыdharmashop.orders.status проверяется в order.changeStatus; форма смены статуса скрыта без правакод; acl-checks (действие объявлено и используется)
Опечатка в названии действия молча отключает проверкуacl-checks: каждое проверяемое в коде действие объявлено в access.xml, каждое объявленное dharmashop.* используетсяdev/acl-checks.php
Потеря доступа менеджерами после обновленияAclMigration: новые действия получают группы прежних общих прав при повышении acl_version (параметр компонента, ставится установщиком; повторного переноса по содержимому правил нет, отозванное право не возвращается)dev/acl-checks.php: 7 проверок миграции; сценарий оператора в dev/scenario
Лишние пункты в менюпункты бокового меню по правам пользователя (прямой адрес всё равно защищён)меню на test.test под суперпользователем

Была ли эта статья полезна?

Ваш отзыв помогает нам делать инструкции лучше

✓ Спасибо за ваш отзыв!