Good Carder
Professional
- Messages
- 759
- Reaction score
- 493
- Points
- 63
Введение: три фундаментальных принципа
Платёжная форма — это не просто набор полей для ввода данных карты. Это цифровой рубеж, за которым лежат реальные деньги, конфиденциальная информация и репутация бизнеса. Уязвимость здесь может привести к утечке платёжных данных тысяч клиентов, массовому кардингу и регуляторным штрафам, исчисляемым миллионами евро.Безопасная платёжная форма строится на трёх фундаментальных принципах, которые должны быть реализованы совместно:
- Никогда не прикасаться к сырым платёжным данным. Ваш сервер не должен видеть номер карты, срок действия или CVV/CVC. Если эти данные оказались на вашем бэкенде — вы уже в зоне риска.
- Контролировать, что исполняется. Любой скрипт на странице — потенциальный скиммер. Запрещать небезопасные источники, подписывать разрешённые скрипты через Subresource Integrity, использовать строгую Content Security Policy без unsafe-inline и unsafe-eval, а также внедрять Trusted Types для защиты от DOM XSS.
- Защищать от контекстных атак. Если платёжная форма встроена в iframe на чужом сайте (кликджекинг), атакующий может перехватить ввод, подменив визуальный слой.
Эти три принципа трансформируются в конкретные технические меры, которые мы разберём далее.
Часть 1. Защита от XSS с помощью CSP и Trusted Types
Cross‑Site Scripting (XSS) — одна из самых распространённых и опасных уязвимостей в веб-приложениях. Атакующий, внедривший свой скрипт на платёжную страницу, может перехватывать вводимые номера карт, CVV и отправлять их на свой сервер в реальном времени. Это превращает любую, даже самую современную форму в инструмент массового скимминга.1.1. Что такое XSS и почему CSP — ключевая защита
DOM‑based XSS возникает, когда код читает данные из управляемого атакующим источника (например, location.hash, document.referrer или URLSearchParams) и записывает их на страницу небезопасным способом. Абсолютное правило: никогда не передавать непроверенные данные в sinks, интерпретирующие HTML или выполняющие код.Небезопасные sinks, которых следует избегать с динамическими данными:
JavaScript:
// ❌ Все эти конструкции могут выполнить внедрённые скрипты
element.innerHTML = userInput;
element.outerHTML = userInput;
document.write(userInput);
eval(userInput);
setTimeout(userInput, 0); // только строковая форма
new Function(userInput)();
Content Security Policy (CSP) — это механизм безопасности браузера, который позволяет контролировать, какие внешние источники может загружать ваша страница, и блокирует выполнение неавторизованного кода. В контексте платёжных форм CSP решает три задачи:
- Запрещает inline-скрипты и javascript: URL (unsafe-inline)
- Запрещает динамическую генерацию кода через eval() (unsafe-eval)
- Ограничивает загрузку скриптов доверенными доменами
Принцип работы CSP: браузер проверяет каждый скрипт перед выполнением. Если скрипт не соответствует политике — он блокируется, не доходя до исполнения.
1.2. Строгая CSP с nonce‑ами
Наиболее эффективная конфигурация CSP для платёжных страниц — использование nonce (одноразовых чисел), которые генерируются сервером для каждого запроса и вставляются в атрибут nonce каждого легитимного script и style. Nonce уникален для каждой загрузки страницы и бесполезен для атакующего на следующем запросе.Пример CSP-заголовка:
Code:
Content-Security-Policy: default-src 'self'; script-src 'self' https://js.stripe.com 'nonce-{RANDOM}'; style-src 'self' 'nonce-{RANDOM}';
1.3. Рекомендации Adyen по построению CSP
Adyen, один из крупнейших платёжных процессоров, предлагает практический подход к внедрению CSP:- Начать с Content-Security-Policy-Report-Only, который логирует нарушения, но не блокирует загрузку ресурсов. Это позволяет выявить все необходимые источники без риска сломать платёжную форму.
- Включить директиву default-src 'self', разрешив загрузку ресурсов только с собственного домена.
- Добавить исключения для платёжного провайдера: script-src 'self' https://*.adyen.com.
- После валидации всех нарушений в отчётах заменить Report-Only на активную блокирующую политику.
При использовании платёжных провайдеров необходимо добавить их домены в CSP. Для Trust Payments, например, требуется разрешить домен https://*.cardinaltrusted.com.
1.4. Новая угроза 2026 года: обход CSP через WebRTC
В марте 2026 года исследователи Sansec обнаружили новый тип скиммера, использующего WebRTC DataChannels для обхода CSP. WebRTC-соединения, как выяснилось, не регулируются стандартными правилами CSP, что позволяет атакующим обходить защиту даже на hardened-сайтах. Трафик уходит через зашифрованные DTLS-пакеты по UDP, которые не инспектируются большинством сетевых средств безопасности.Эта атака стала широко эксплуатироваться с 19 марта 2026 года, затронув более половины уязвимых магазинов. Скиммер крадёт валидный CSP nonce из существующих скриптов для внедрения своей полезной нагрузки, выполняя её в периоды бездействия браузера для снижения вероятности обнаружения.
Защита от этого вектора включает:
- Блокировку неожиданного исходящего UDP-трафика на сетевом уровне
- Расширение мониторинга за пределы HTTP-инспекции для покрытия зашифрованных UDP/DTLS потоков
- Аудит генерации и обработки nonce для предотвращения их кражи
1.5. Trusted Types API — новая линия обороны от DOM XSS
С февраля 2026 года Trusted Types API считается широко доступным (Baseline 2026) во всех современных браузерах. Этот API заставляет разработчика явно указывать, что данные прошли через политику очистки перед передачей в опасные sinks (innerHTML, eval, src и т.д.).Разработчик определяет политику, содержащую методы очистки для разных типов sinks. Например, политика может использовать библиотеку DOMPurify для очистки HTML или полностью запрещать выполнение динамического кода.
Trusted Types не заменяет CSP, а дополняет его: CSP ограничивает источники скриптов, а Trusted Types контролирует то, как эти скрипты манипулируют DOM. Лучшая практика — комбинировать строгую CSP без unsafe-inline с включённым заголовком Require-Trusted-Types-For 'script'.
1.6. Уязвимые реализации и их исправление
Пример уязвимой реализации: WordPress плагин оплаты для Stripe версии ≤1.4.6 содержал уязвимость stored XSS (CVE-2026-0751). Исправление заключается в экранировании вывода всех пользовательских данных, сохранённых в базе, перед их отображением на странице.Пример небезопасного кода:
JavaScript:
// ❌ Опасно: пользовательские данные записываются напрямую
document.getElementById('payment-message').innerHTML = userInput;
// ✅ Безопасно: использование textContent
document.getElementById('payment-message').textContent = userInput;
// ✅ Безопасно: очистка через DOMPurify перед вставкой
element.innerHTML = DOMPurify.sanitize(userInput);
CSP должна применяться в дополнение к экранированию и санитизации. Ни один из методов не является панацеей — для обеспечения многоуровневой защиты необходима комбинация всех трёх.
Часть 2. Защита от кликджекинга (Clickjacking)
Кликджекинг — это атака, при которой хакер встраивает платёжную форму жертвы в прозрачный iframe на своём сайте и обманом заставляет пользователя кликнуть в нужное место, не подозревая, что он совершает платёж.2.1. X-Frame-Options и CSP frame-ancestors
Защита от кликджекинга строится на двух механизмах:- X-Frame-Options. Старый, но всё ещё работающий заголовок. Значение DENY полностью запрещает встраивание страницы в любой iframe; SAMEORIGIN разрешает встраивание только с того же домена.
- CSP frame-ancestors. Более современный и гибкий механизм. Директива указывает, какие родительские источники могут встраивать страницу через <frame>, <iframe>, <object> или <embed>. CSP frame-ancestors считается более мощным, чем X-Frame-Options, поскольку поддерживает список разрешённых доменов и вложенные проверки для всех предков во фреймворке.
Пример надёжной защиты:
Code:
Content-Security-Policy: frame-ancestors 'none';
X-Frame-Options: DENY
Если платёжная форма интегрируется через iframe с поддоменов того же сайта:
Code:
Content-Security-Policy: frame-ancestors 'self' https://checkout.example.com;
2.2. Разница между frame-ancestors и frame-src
CSP различает два типа ограничений встраивания:- frame-ancestors контролирует, кто может встраивать вашу страницу. Это защита от кликджекинга — кто может поместить ваш iframe на свой сайт.
- frame-src контролирует, какие источники ваша страница может загружать в собственные iframe. Это ограничение того, куда указывают ссылки на вашей странице.
2.3. Ограничения и рекомендации
Согласно документации MDN, директива frame-ancestors не поддерживается в элементе <meta> и должна задаваться через HTTP-заголовок.Кроме того, существуют различия в обработке frame-ancestors и X-Frame-Options при использовании вложенных фреймов. Если страница встроена в несколько уровней iframe, каждый предок должен быть разрешён директивой frame-ancestors конечного фрейма, иначе загрузка будет отменена.
2.4. Уязвимые реализации и их исправление
Уязвимая реализация: платёжная страница не задаёт заголовки X-Frame-Options и CSP frame-ancestors. Атакующий встраивает её в невидимый iframe на своём сайте и перехватывает клики.
HTML:
<!-- Уязвимо: хакер встраивает вашу форму -->
<iframe src="https://your-shop.com/checkout" style="opacity:0; position:absolute;"></iframe>
Исправление: добавить HTTP-заголовки на сервере (Nginx):
Code:
add_header X-Frame-Options "DENY" always;
add_header Content-Security-Policy "frame-ancestors 'none';" always;
Часть 3. Защита от внедрения скриптов через Subresource Integrity (SRI)
Subresource Integrity (SRI) — это механизм безопасности, позволяющий браузеру проверить, что загружаемый ресурс (например, скрипт из CDN или стиль) не был изменён атакующим. Браузер вычисляет криптографическую хэш-сумму загруженного файла и сравнивает с заданным в атрибуте integrity.3.1. Принцип работы SRI
SRI защищает от атак на цепочку поставок (supply chain attacks), когда хакер взламывает CDN и подменяет библиотеку на вредоносную, перехватывающую платёжные данные.Пример использования:
HTML:
<script src="https://js.stripe.com/v3/"
integrity="sha384-..."
crossorigin="anonymous"></script>
Если загруженный скрипт отличается от указанной хэш-суммы, браузер отказывается его выполнять и сообщает об ошибке в консоли.
3.2. SRI и соблюдение стандарта PCI DSS 6.4.3
Требование 6.4.3 стандарта PCI DSS (обязательное с марта 2025 года) обязывает организации гарантировать целостность скриптов, загружаемых на платёжные страницы, и однозначно определять, какие именно скрипты должны выполняться, почему они необходимы и как отслеживать их изменения.Многие платёжные провайдеры предоставляют официальные хэш-суммы для своих скриптов в документации. Например, Braintree, Adyen и PaymentsOS рекомендуют использовать SRI с версионированными SDK вместо автоматического обновления до последней версии.
3.3. Пример для Braintree Hosted Fields
Braintree предоставляет официальные хэш-суммы в документации. Используя SRI с версионированными скриптами, вы получаете двойную защиту: даже если хакер взломает CDN и подменит файл, браузер откажется его загружать, и платёж не пройдёт.3.4. Важное отличие SRI от CSP
CSP определяет откуда можно загружать скрипты (контроль источников). SRI проверяет какой именно скрипт загружается (контроль содержимого). Эти механизмы работают совместно, создавая эшелонированную защиту.3.5. Интеграция с PCI DSS: требования 6.4.3 и 11.6.1
Важно понимать, что CSP и SRI — это технические механизмы, но они не являются полным решением для соблюдения требований PCI DSS. QSA-аудиторы задают три ключевых вопроса, которые CSP не может покрыть:- Что именно исполняется на вашей платёжной странице прямо сейчас? (не просто политика CSP, а фактическое содержимое в браузере)
- Как каждый скрипт попал на страницу и кто его утвердил?
- Как вы узнаете, если authorised вендор изменит свой скрипт завтра?
Поэтому помимо CSP и SRI необходимы:
- Инвентаризация всех скриптов на платёжной странице с указанием их назначения и источника
- Документированное одобрение каждого скрипта перед добавлением
- Системы непрерывного мониторинга изменений скриптов (например, Feroot Security)
Часть 4. Токенизация на стороне клиента: убираем платёжные данные из вашей инфраструктуры
4.1. Архитектурный принцип: никогда не прикасаться к данным карты
Самый эффективный способ защитить платёжные данные — не обрабатывать их вообще. Ключевой архитектурный принцип: номера карт, CVV и сроки действия никогда не должны попадать на ваш сервер. Вся обработка должна происходить на стороне клиента, в iframe или hosted-полях, принадлежащих платёжному провайдеру.Если платёжные данные не достигают вашего сервера, ваша инфраструктура исключается из области PCI DSS (out-of-scope), что снимает 80%+ требований комплаенса. Правильно спроектированная система имеет 5–10 систем в области PCI, тогда как неправильная — 50+ при сопоставимых затратах на порядок выше.
4.2. Stripe Elements
Stripe Elements — это коллекция предварительно стилизованных компонентов UI в виде безопасных iframe, которые собирают и токенизируют платёжные данные. Данные карты передаются напрямую Stripe через iframe; ваш сервер получает только одноразовый токен (PaymentMethod ID), который можно безопасно использовать для проведения платежа, возвратов и рекуррентных списаний.Стандартный поток работы: клиент заполняет поля внутри iframe → Stripe токенизирует данные и возвращает payment_method_id → ваш сервер получает токен → создаёт PaymentIntent через API Stripe с этим токеном. Токенизированные данные означают, что ваши системы никогда не работают с номерами карт, что снижает риски и упрощает соответствие стандартам безопасности.
Преимущества Stripe Elements:
- Ваш сервер остаётся вне PCI-периметра
- Карточные данные никогда не попадают в логи, базы данных или кеши
- Стилизация остаётся под вашим контролем
4.3. Braintree Hosted Fields
Braintree Hosted Fields реализует аналогичную концепцию: каждое поле ввода (номер карты, срок действия, CVV) размещается в собственном прозрачном iframe, хостед на домене Braintree (PayPal). При отправке формы Braintree возвращает одноразовый payment_method_nonce, который ваш сервер может безопасно использовать для проведения транзакции.Критическое требование: Чтобы соответствовать упрощённому уровню PCI DSS SAQ A, платёжные поля не могут находиться на вашей платёжной странице непосредственно. Они должны быть захостедены (hosted) — размещены в iframe на домене платёжного провайдера.
4.4. Почему токенизация не решает все проблемы безопасности платёжной формы
Даже при использовании Hosted Fields зона ответственности продавца включает:- Контроль того, какие скрипты загружаются на страницу, содержащую iframe.
- Мониторинг целостности этих скриптов (требования 6.4.3 и 11.6.1 PCI DSS).
- Проверку подписи вебхуков платёжного провайдера перед обработкой уведомлений.
4.5. Пример уязвимой реализации и её исправление
Уязвимая реализация: Ваш сервер принимает номер карты напрямую через POST, передаёт его в Stripe API и создаёт PaymentIntent.
Code:
// ❌ Опасно: сервер обрабатывает номер карты напрямую
app.post('/create-payment', (req, res) => {
const { cardNumber, expMonth, expYear, cvc } = req.body;
stripe.paymentIntents.create({
amount: 1000,
currency: 'usd',
payment_method_data: { type: 'card', card: { number: cardNumber, exp_month: expMonth, exp_year: expYear, cvc } }
});
});
Исправление: Используйте Stripe Elements (или аналоги) для токенизации на фронтенде и передавайте на сервер только payment_method_id.
Часть 5. Сквозной чек-лист безопасности платёжной формы
CSP и XSS защита:- Строгая CSP без unsafe-inline и unsafe-eval. Использование nonce или хэшей для разрешённых скриптов.
- Включён заголовок Content-Security-Policy со списком разрешённых источников, включая домены платёжного провайдера.
- Настроена директива frame-ancestors 'none' или 'self' для предотвращения кликджекинга.
- Trusted Types включены через заголовок Require-Trusted-Types-For 'script' в поддерживаемых браузерах.
- Внедрена санитизация через DOMPurify для всех динамически вставляемых HTML-фрагментов.
SRI и целостность скриптов:
- Атрибут integrity добавлен ко всем CDN-скриптам (Stripe, Braintree, Adyen, аналитика).
- Использованы версионированные, а не динамические latest ссылки.
- Создан и поддерживается инвентарь всех скриптов на платёжной странице с указанием назначения, источника и даты утверждения.
- Разработана процедура документированного утверждения каждого нового скрипта перед его добавлением.
- Настроен непрерывный мониторинг изменений скриптов с автоматическими оповещениями.
Токенизация и защита данных:
- Платёжные данные собираются через hosted-поля (Stripe Elements, Braintree Hosted Fields, Adyen iframes), а не в обычные input.
- На сервер передаются только одноразовые токены (payment_method_id, payment_nonce), а не данные карты.
- Вебхуки платёжного провайдера проверяются на подлинность подписи (HMAC).
Защита от кликджекинга:
- Заголовок X-Frame-Options: DENY или SAMEORIGIN установлен на сервере.
- Директива CSP frame-ancestors настроена для предотвращения внешнего встраивания.
- Проверено, что страница не встраивается на сторонние домены (тест через инструменты разработчика).
Заключение: безопасность платёжной формы — это не одна технология, а эшелонированная защита
Безопасная платёжная форма не строится на одном инструменте. Это эшелонированная защита, где каждый слой прикрывает слабости других:- CSP + Trusted Types защищают от внедрения скриптов через XSS.
- SRI гарантирует, что загруженные скрипты не были подменены на уровне CDN.
- frame-ancestors + X-Frame-Options предотвращают кликджекинг.
- Токенизация на стороне клиента полностью убирает платёжные данные из вашей инфраструктуры, радикально снижая поверхность атаки и упрощая PCI-комплаенс.
Три главных вывода этой статьи:
- CSP без unsafe-inline и unsafe-eval обязательна. Это единственный способ предотвратить выполнение несанкционированных скриптов на платёжной странице.
- SRI обязателен для всех CDN-ресурсов. Без него хакер, взломавший CDN, может перехватить все ваши транзакции.
- Токенизация на стороне клиента — это не опция, а необходимость. Если номера карт проходят через ваш сервер, вы находитесь в зоне высокого риска и широкой PCI-периметра.
Быстрая памятка на одну строку:
«Строгая CSP без unsafe‑inline, SRI для всех CDN, trust‑framing через X‑Frame‑Options и CSP frame‑ancestors, hosted‑токенизация через Elements или Hosted Fields — и никаких карточных данных на твоём сервере»
