Как сгенерировать проверочный код/номер?

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

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

Вот некоторые из требований:

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

Учитывая эти требования, как бы вы сгенерировали такое число?

РЕДАКТИРОВАТЬ :

@Haaked: код должен быть числовым, потому что пользователь вводит его на своем телефоне.

@matt b: На первом этапе код отображается на веб-странице, на втором этапе необходимо позвонить и ввести код. Я не знаю номер телефона пользователя.

Дополнение: я нашел несколько алгоритмов для проверки достоверности чисел (см. этот интересный проект Google Code: checkDigits).


person Costo    schedule 05.09.2008    source источник
comment
Алгоритм в телефоне можно легко перепроектировать. Поэтому его нельзя использовать в качестве средства защиты от мошенничества или спама.   -  person Siavoshkc    schedule 03.02.2019


Ответы (9)


После некоторых исследований я думаю, что выберу формулу ISO 7064 Mod 97,10. Это кажется довольно надежным, поскольку используется для проверки IBAN (международный номер банковского счета).

Формула очень проста:

  1. Возьми номер : 123456
  2. Примените следующую формулу, чтобы получить контрольную сумму из 2 цифр: mod(98 - mod(number * 100, 97), 97) => 76
  3. Concat номер и контрольная сумма для получения кода => 12345676
  4. Чтобы подтвердить код, убедитесь, что mod(code, 97) == 1

Контрольная работа :

  • mod(12345676, 97) = 1 => ХОРОШО
  • mod(21345676, 97) = 50 => ПЛОХО!
  • mod(12345678, 97) = 10 => ПЛОХО!

Судя по всему, этот алгоритм отлавливает большинство ошибок.

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

person Costo    schedule 05.09.2008
comment
Если ожидаются враждебные пользователи (что, кажется, подразумевается вопросом), пользователю будет очень легко сгенерировать действительный идентификатор с помощью этого алгоритма. - person Nick Johnson; 09.07.2009
comment
Враждебные пользователи не были проблемой в этом приложении. Тем не менее, я добавил некоторую логику для смешивания и разделения битов, чтобы упростить угадывание следующих чисел. - person Costo; 14.07.2009
comment
При вычислении контрольной суммы с длиной, подобной IBAN, имейте в виду, что промежуточная сумма часто не помещается в 32-битное целое число со знаком. - person AndreKR; 05.07.2011
comment
checksum может быть 1 цифрой. поэтому в этом случае вы должны префикс cero. например для 256 код будет 25609. не путайте с 2569 - person Jaider; 12.10.2013
comment
@nick Johnson Я не думаю, что в вопросе подразумеваются враждебные пользователи. кроме того, контрольные цифры предназначены не для предотвращения враждебных пользователей, а для уменьшения ошибок при вводе данных. - person Sprague; 12.10.2016
comment
@Sprague ОП спросил о «проверочном коде», что для меня означает, что они не хотят, чтобы пользователи могли придумать его, не предоставив его. - person Nick Johnson; 12.10.2016
comment
Я нашел эту ссылку с алгоритмом Верхоффа en.wikibooks.org/wiki/Algorithm_Implementation /Контрольные суммы/ - person Ricardo da Rocha Vitor; 30.06.2017

Для 1 млн комбинаций вам понадобится 6 цифр. Чтобы убедиться, что случайных кодов нет, я предлагаю 9 цифр с вероятностью 1/1000, что случайный код сработает. Я также предлагаю использовать другую цифру (всего 10) для выполнения проверки целостности. Что касается шаблонов распределения, случайного будет достаточно, а контрольная цифра гарантирует, что одна ошибка не приведет к правильному коду.

Редактировать: видимо я не полностью прочитал ваш запрос. Используя номер кредитной карты, вы можете выполнить хеширование (MD5 или SHA1 или что-то подобное). Затем вы усекаете в соответствующем месте (например, 9 символов) и конвертируете в базу 10. Затем вы добавляете контрольную цифру (цифры), и это должно более или менее работать для ваших целей.

person Kyle Cronin    schedule 05.09.2008

Вы хотите сегментировать свой код. Часть этого должна быть 16-битной CRC остальной части кода.

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

Затем вы добавляете к последовательности префикс CRC-16 этого порядкового номера И некоторый закрытый ключ. Вы можете использовать что угодно для закрытого ключа, если вы держите его закрытым. Сделайте это чем-то большим, по крайней мере, GUID, но это может быть текст для Война и мир из проекта Гутенберг. Просто нужно быть тайным и постоянным. Наличие закрытого ключа не позволяет людям подделать ключ, но использование 16-битного CR облегчает взлом.

Для проверки вы просто разделяете номер на две части, а затем берете CRC-16 порядкового номера и закрытого ключа.

Если вы хотите больше скрыть последовательную часть, разделите CRC на две части. Поместите 3 цифры в начале и 2 в конце последовательности (нулевой знак, чтобы длина CRC была постоянной).

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

person Jim McKeeth    schedule 05.09.2008

Это должны быть только цифры? Вы можете создать случайное число от 1 до 1M (хотя я бы предложил даже больше), а затем Base32 закодировать его. Следующее, что вам нужно сделать, это хешировать это значение (используя секретное значение соли) и base32 закодировать хеш. Затем соедините две строки вместе, возможно, разделив их тире.

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

person Haacked    schedule 05.09.2008

  • У меня должно быть разумное количество возможных комбинаций (скажем, 1 миллион)
  • Код должен быть максимально коротким, чтобы избежать ошибок со стороны пользователя

Ну, если вы хотите, чтобы в нем было хотя бы миллион комбинаций, то вам нужно как минимум шесть цифр. Это достаточно коротко?

person Chris Upchurch    schedule 05.09.2008

Когда вы создаете код подтверждения, есть ли у вас доступ к номеру телефона вызывающего абонента?

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

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

Конечно, это не сработает, если номер телефона, использованный на шаге 1, отличается от номера, с которого они звонят на шаге 2.

person matt b    schedule 05.09.2008

Предполагая, что вы уже знаете, как определить, какую клавишу нажал пользователь, это должно быть достаточно легко выполнимо. В мире безопасности существует понятие «одноразового» пароля. Иногда его называют «одноразовым паролем». Обычно они ограничены (легко печатаемыми) значениями ASCII. Итак, [a-zA-z0-9] и куча легко набираемых символов. как запятая, точка, точка с запятой и скобки. Однако в вашем случае вы, вероятно, захотите ограничить диапазон до [0-9] и, возможно, включить * и #.

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

Вместо того, чтобы самому объяснять детали реализации, я направлю вас к 9-страничной статье, где вы сами сможете прочитать об этом: https://www.grc.com/ppp.htm

person Rob Rolnick    schedule 05.09.2008

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

Есть несколько способов, которыми люди делали это в прошлом.

  1. Сделайте открытый ключ и закрытый ключ. Закодируйте числа от 0 до 999 999 с помощью закрытого ключа и раздайте результаты. Вам нужно будет ввести несколько случайных чисел, чтобы результат вышел в более длинной версии, и вам нужно будет преобразовать результат из базы 64 в базу 10. Когда вы введете число, преобразуйте его обратно в базу 64, примените закрытый ключ и посмотрите, не меньше ли интересных чисел 1 000 000 (отбросьте случайные числа).
  2. Используйте обратимую хеш-функцию
  3. Используйте первый миллион номеров из PRN, заполненных определенным значением. Функция «проверки» может получить начальное значение и узнать, что следующий миллион значений является хорошим. Он может либо генерировать их каждый раз и проверять по одному при получении кода, либо при запуске программы хранить их все в таблице, сортировать, а затем использовать бинарный поиск (максимум сравнений), так как миллион целых чисел — это не много пространства.

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

-Адам

person Adam Davis    schedule 05.09.2008

Вы связались с проектом check digits, и использование функции "encode" кажется хорошим решение. В нем говорится:

encode может генерировать исключение, если ему передаются «плохие» данные (например, нечисловые), в то время как verify возвращает только true или false. Идея здесь заключается в том, что encode обычно получает данные из «доверенных» внутренних источников (например, из ключа базы данных), поэтому передача неверных данных должна быть довольно обычной, а на самом деле исключительной.

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

person Michael Sharek    schedule 05.09.2008