Схема односимвольной подписи (минимальная безопасность)

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

ситуация

система A:

У меня есть система A который обслуживает запросы пользователей. Этот сервер что-то делает, а затем перенаправляет пользователя в систему B, Во время этого перенаправления сервер A может предоставить пользователю 32-символьную буквенно-цифровую строку информации для передачи в систему B, Требуется 31 символ этой информации, но один может использоваться в качестве контрольной суммы. Эта строка может более или менее рассматриваться как идентификатор запроса.

система B:

Когда система B Получив запрос от пользователя, он может проверить, что запрос (и строка, подобная ID) действительны, проанализировав 31-символьную строку, запросив базу данных и поговорив с системой А. Эта система может с абсолютной уверенностью проверить, что запрос действителен и не был подделан, но это очень дорого в вычислительном отношении.

Нападающие:

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

Что мне нужно

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

Что я пробовал

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

В качестве первой попытки создания схемы подписи я поразрядно хранил 31-байтовый идентификатор с 31-байтовым ключом, суммируя полученные байты, преобразуя их в десятичные и добавляя цифры до значения меньше 62, а затем отображая его персонажу в наборе [a-bA-Z0-9] (псевдокод ниже). Проблема в том, что, хотя я почти уверен, что это не откажется от любых допустимых запросов, я не уверен, как определить, как часто это будет пропускать недействительные идентификаторы или можно ли получить ключ, используя окончательное значение.

Set alphabet to (byte[]) "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
Set keystring to "aLaklj14sdLK87kxcvalskj7asfrq01";

Create empty byte[] key;
FOR each letter in keystring
  Push (index of letter in alphabet) to key;

Create empty byte[] step1;

FOR each (r, k) in (request, key)
  Push r XOR s to step1;

Set step2 to 0;
FOR each b in step1
  Add (int) b to step2;

WHILE step2 > 62
  Copy step2 to step3;
  Set step2 to 0;
  Convert step3 to String;
  Split step3 between characters;
  FOR each digit in step3
    Add (int) digit to step2;
END WHILE

RETURN alphabet[step2]

Заявлено официально

Детерминированная хеш-функция, в которой при наличии закрытого ключа и входных данных длиной 31 байт получаются выходные данные в наборе {x | x ∈ ℕ, x < 62}где угадывание вывода будет более эффективным, чем вычисление закрытого ключа. (Бонусные баллы за ввод переменной длины)

В конечном итоге это будет реализовано в NodeJS/JavaScript, но не зависит от языка.


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

2 ответа

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

sha256(input+key).toString('hex');

Затем возьмите последние несколько битов значения хеша, преобразуйте его из шестнадцатеричной строки в целое, разделите целое число на 62, получите остаток и определите символ на основе остатка.

Это не даст вам идеальной вероятности распределения 1/62 (шестнадцатеричная строка должна иметь равномерное распределение для каждого значения, но не остатки после деления на 62) для каждого символа, но должна быть очень близкой.

Одним из подходов было бы создать Blob URL когда пользователь посещает начальный document, Blob URL должен быть уникальным для document который создал URL. Пользователь может затем использовать Blob URL в качестве идентификатора запроса к серверу "B". Когда пользователь делает запрос на "B", отозвать Blob URL,

Blob URL уникален для каждого звонка URL.createObjectURL() пользователь создает уникальный идентификатор, где время жизни Blob URL время жизни document где Blob URL создан, или Blob URL отменяется Существует минимальная возможность для Blob URL копироваться из браузера посетителей любым лицом, кроме пользователя, который создал Blob URL, если другие проблемы не существуют на компьютере людей.

const requestA = async() => {
  const blob = new Blob();
  const blobURL = URL.createObjectURL(blob);
  const A = await fetch("/path/to/server/A", {
              method:"POST", body:JSON.stringify({id:blobURL})
            });
  const responseA = await A.text(); 
  // do stuff with response
  return [blobURL, responseA];
}

Сервер "А" связывается созданным Blob URL к серверу "B"

const requestB = async(blobURL) => {
  const blob = new Blob();
  const blobURL = URL.createObjectURL(blob);
  const B = await fetch("/path/to/server/B", {
              method:"POST", body:JSON.stringify({id:blobURL})
            });
  const responseB = await B.text(); 
  return responseB
}

requestA()
.then(([blobURL, responseA] => {
  // do stuff with `responseA`
  console.log(responseA);
  // return `requestB` with `blobURL` as parameter
  return requestB(blobURL)
})
.then(responseB => console.log(responseB) // do stuff with `responseB`)
.catch(err => console.error(err));
Другие вопросы по тегам