Создание и защита кодов подарочных карт

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

Мне интересно, каков наиболее безопасный способ создания этих кодов подарочных карт. Длина должна быть 16 символов (хотя это обсуждается) и может быть буквенно-цифровой (хотя числовой будет удобнее для пользователя).

Насколько я понимаю, наиболее безопасный способ сделать это - сгенерировать код подарочной карты определенной длины с помощью следующего кода Java:

static final String AB = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
static SecureRandom rnd = new SecureRandom();

String randomString( int len ){
   StringBuilder sb = new StringBuilder( len );
   for( int i = 0; i < len; i++ ) 
      sb.append( AB.charAt( rnd.nextInt(AB.length()) ) );
   return sb.toString();
}

Это взято из ответа SO здесь. Я удалил строчные буквы из строки, чтобы сделать ее более удобной для пользователя. Таким образом получается 36 ^ 16 комбинаций. Только числовые значения составят 10 ^ 16 комбинаций. Я считаю, что одного числа было бы достаточно, но часто подчеркивается, что, учитывая растущую распространенность мошенничества с подарочными картами, строка должна быть буквенно-цифровой.

Итак, вопрос первый: числовой или буквенно-цифровой?

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

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

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

Итак, вот второй вопрос: как защитить коды подарочных карт, которые используются только частично, но на них еще сохраняется ценность?


person Mark    schedule 03.04.2018    source источник
comment
Что касается частично погашенных ключей: почему бы не отказаться от исходного ключа и не создать новый случайный ключ для частично использованной подарочной карты? Фактически - вы повторно получаете подарочную карту с меньшим балансом.   -  person Rann Lifshitz    schedule 03.04.2018
comment
Похоже, здесь также хорошо обсуждается проблема создания безопасного ключа: stackoverflow.com/questions/7111651/   -  person Rann Lifshitz    schedule 03.04.2018
comment
@ErwinBolwidt Я помещу это на security.stackexchange.com, поэтому. Да, это стартап, в котором всего 3 разработчика, поэтому мы можем решать это сами.   -  person Mark    schedule 03.04.2018
comment
@RannLifshitz да, одно из решений, которые я рассматриваю, - это отказаться от исходного ключа и создать новый для баланса (описанного выше в вопросе)   -  person Mark    schedule 03.04.2018
comment
@Mark: Я бы определенно отказался от поддержки и восстановления ключа, основываясь на изменении баланса. Это уменьшение вашей проблемы - ключ связан с балансом, а не с пользователем (хотя вы можете использовать БД, чтобы связать пользователя с несколькими ключами для записи действий пользователя). Если удобство использования является проблемой - инкапсулируйте уникальный ключ - используйте токен пользователя вместо ключа. Токен будет уникальным для пользователя (отношение 1-1), зашифрованная строка будет уникальной для баланса, а связь между ними будет выполняться на вашем сервере.   -  person Rann Lifshitz    schedule 03.04.2018
comment
Надеюсь, я правильно понимаю вашу концепцию. Но я бы не стал отправлять такую ​​информацию, как подарочные коды, через сторонние серверы. Я бы использовал такую ​​систему, как PayPal или Paysafecard. Если пользователь хочет заплатить подарочной картой, он будет перенаправлен на ваш сервер для ввода кода. Затем вы уведомляете третью сторону, что платеж был успешным или нет.   -  person Lars    schedule 03.04.2018
comment
@Lars К сожалению, это не вариант. Один из вариантов - зашифровать код в браузере клиента, когда они вводят его в интернет-магазине, попасть на наши серверы и вернуть какой-то ключ, который мы храним в интернет-магазине и связываем с платежом.   -  person Mark    schedule 03.04.2018
comment
@Mark Вы контролируете интернет-магазин? В противном случае это не сильно поможет. В интернет-магазине по-прежнему есть код в виде обычного текста, а не только зашифрованная версия. (Другая проблема) Что делать, если интернет-магазин просит пользователя заплатить 10 долларов. Теперь пользователь вводит код стоимостью 50 $. (Злой) магазин может просто отправить запрос на ваш сервер заплатить 50 $ с подарочным кодом, который ввел пользователь. Вы не можете проверить, что запрос содержит только те действия, которые пользователь согласился выполнить. Задайте этот вопрос на security.stackexchange.com. Мне очень интересно найти решения этого вопроса.   -  person Lars    schedule 03.04.2018
comment
@Lars мы делаем в определенной степени, мы создали плагин, который они устанавливают, чтобы мы могли контролировать определенные вещи. У нас всего 2 клиента, и они еще не запущены, но у нас есть юридические контракты. Я воссоздал это на Security Stack Exchange и уже получил интересный ответ security.stackexchange.com/questions/182840/   -  person Mark    schedule 03.04.2018
comment
@Lars, это хороший момент - после того, как пользователь успешно сделал заказ, интернет-магазин отправляет запрос на наши серверы с суммой выкупа - это делается через наш плагин, который прослушивает успешные заказы. Хм   -  person Mark    schedule 03.04.2018
comment
@Mark: Для хеша я бы предложил использовать идентификатор пользователя + баланс + дату, чтобы обеспечить уникальность.   -  person Rann Lifshitz    schedule 03.04.2018


Ответы (4)


Итак, проблема, с которой вы столкнулись, - интересная проблема. Я прочитал решение проблемы @ Therac и должен согласиться с ним, что в конечном итоге вы создадите протокол, похожий на криптовалюту. Я также согласен со всеми его предложениями по криптографии.

Я не буду повторять решение @ Therac, однако посмотрю, смогу ли я помочь, объяснив некоторые идеи из криптовалют. Я не буду вдаваться в технические подробности, но на поверхностном уровне, и вы можете судить сами, подходит ли эта идея для вашего варианта использования.

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

Итак, есть два типа транзакций.

  1. CreateGiftCode
  2. SpendGiftCode

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

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

Затем SpendGiftCode полностью использует код подарочной карты (уничтожает код подарочной карты и сохраняет информацию о том, что он был использован) и выполняет одно из двух действий:

  1. Если вся сумма потрачена, допустим, подарочная карта на 50 долларов полностью израсходована на транзакцию, тогда новый giftcardcode создается для публичного адреса человека, которому он платит (который является номером счета другой стороны).
  2. Если сумма, которую нужно потратить, меньше, скажем, 10 долларов из 50, то генерируются два giftcardcodes. Один, который составляет 10 долларов, отправляется другой стороне, а новый giftcardcode из оставшейся суммы отправляется обратно на его счет.

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

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

Ваше здоровье!

person Haris Nadeem    schedule 07.04.2018
comment
Это хороший подход, который вы предложили здесь, но, скорее всего, он слишком далек от области применения продукта. - person Andrei Mărcuţ; 11.04.2018
comment
@AndreiMarcut Я согласен. Но поскольку кто-то на другом форуме уже дал очень хороший технический ответ, я подумал, что этот ответ может вызвать некоторые идеи. Например, создайте систему поверх цепочки блоков Ethereum и самостоятельно управляйте компьютерными узлами (это тоже, вероятно, далеко от области применения продукта и создало бы ненужную зависимость). Я все же думаю, что идея модели с двумя типами транзакций реализуема. - person Haris Nadeem; 12.04.2018

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

person egorlitvinenko    schedule 10.04.2018

Я просмотрел все ответы и нашел решение, приняв во внимание предложения:

  • Мы генерируем ссылку для отправки пользователю. Ключ, отправленный в ссылке, представляет собой случайную буквенно-цифровую строку, и она хешируется (MD5 или что-то подобное), поэтому не может быть обнаружена перед сохранением в БД.

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

  • В договоре с интернет-магазином мы указываем, что они не могут регистрировать или сохранять код подарочной карты где-либо (наши 2 клиента - крупные известные интернет-магазины)

  • На странице оформления заказа в интернет-магазине нашего клиента для оплаты с помощью кода подарочной карты пользователь предоставляет 16-значный код подарочной карты. Он отправляется на наши серверы, а баланс и случайный идентификатор платежа. возвращается в интернет-магазин. Этот платежный идентификатор сохраняется в интернет-магазине как часть заказа. По завершении заказа интернет-магазин отправляет запрос API (с идентификатором платежа) на наши серверы, чтобы выкупить сумму с подарочной карты (эта функция была создана нами и предоставляется интернет-магазину через устанавливаемый ими плагин).

  • связь между интернет-магазином и нашим API аутентифицируется с использованием OAuth 2.0

  • Если на подарочной карте есть остаток, создается новый код подарочной карты (пользователю отправляется новая ссылка, чтобы получить новый код подарочной карты для баланса)

  • Когда интернет-магазин выставляет нам счет, он предоставляет нам список идентификаторов платежа, которые мы затем сопоставляем с кодами подарочных карт в нашей серверной части (а затем сопоставляем с нашим эмитентом).

Защищает:

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

Сообщите мне, если будут какие-либо комментарии.

person Mark    schedule 30.04.2018

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

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

Я рекомендую использовать альфа (с учетом регистра) + числовой

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

Чтобы предотвратить это, вы можете использовать существующие механизмы / концепции безопасности, такие как:

(1) в случае (на ваш выбор) 1-2-3-5 неудачных вызовов купона вы можете ввести экспоненциальную задержку повтора для ограничения количества последовательных атак, которые один и тот же источник может установить на ваш API.

(2) установите механизм captcha через перенаправления http, чтобы предотвратить злоупотребление ботами после X (на ваш выбор) неудачных обращений API

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

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

person Andrei Mărcuţ    schedule 11.04.2018