Криптографическая защита Captcha hash cookie - PullRequest
2 голосов
/ 26 октября 2009

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

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

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

РЕДАКТИРОВАТЬ: Дальнейшее обсуждение привело к обновленной реализации, поэтому я заменил исходный код на новую версию и отредактировал описание, чтобы поговорить с этой ревизией.

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

// Generate a "session" cookie unique to a particular machine and timeframe
String generateSessionHash(timestamp) {
    return sha256( "" 
        + (int)(timestamp / CAPTCHA_VALIDITY_SECONDS)
        + "|" + request.getRemoteAddr() 
        + "|" + request.getUserAgent() 
        + "|" + BASE64_8192BIT_SECRET_A
    );
}

// Generate a hash of the captcha, salted with secret key and session id
String generateCaptchaHash(captchaValue, session_hash) {
    return sha256( ""
        + captchaValue
        + "|" + BASE64_8192BIT_SECRET_B
        + "|" + session_hash
    );
}

// Set cookie with hash matching the provided captcha image
void setCaptchaCookie(CaptchaGenerator captcha) {
    String session_hash = generateSessionHash(time());
    String captcha_hash = generateCaptchaHash(captcha.getValue(), session_hash);
    response.setCookie(CAPTCHA_COOKIE, captcha_hash + session_hash);
}

// Return true if user's input matches the cookie captcha hash
boolean isCaptchaValid(userInputValue) {
    String cookie = request.getCookie(CAPTCHA_COOKIE);
    String cookie_captcha_hash = substring(cookie, 0, 64);
    String cookie_session_hash = substring(cookie, 64, 64);
    String session_hash = generateSessionHash(time());
    if (!session_hash.equals(cookie_session_hash)) {
        session_hash = generateSessionHash(time() - CAPTCHA_VALIDITY_SECONDS);
    }
    String captcha_hash = generateCaptchaHash(userInputValue, session_hash);
    return captcha_hash.equals(cookie_captcha_hash);
}

Концепция:

  1. "session_hash" предназначен для предотвращения использования одного и того же cookie на нескольких машинах и обеспечивает период времени, после которого он становится недействительным.
  2. И "session_hash", и "captcha_hash" имеют свои собственные секретные соленые ключи.
  3. Эти соленые ключи BASE64_8192BIT_SECRET_A и _B являются частями закрытого ключа RSA, хранящегося на сервере.
  4. В «captcha_hash» добавляется как секрет, так и «session_hash».
  5. Разделители добавляются, когда используются предоставленные клиентом данные, чтобы избежать сплайс-атак.
  6. "captcha_hash" и "session_hash" оба хранятся в файле cookie на стороне клиента.

РЕДАКТИРОВАТЬ: re: Kobi Спасибо за отзыв!

(я бы ответил в комментариях, но, похоже, он не принимает форматирование, которое работает в вопросах?)

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

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

Эта идея хэширования некоторых данных и включения другого хэша в эти данные кажется мне подозрительной. Есть ли какая-то выгода, или нам лучше использовать один хеш, содержащий все соответствующие данные (время, IP, User-agent, значение Captcha и секретный ключ). В этой реализации мы в основном говорим пользователю о хэшированном открытом тексте.

Вопросы:

  1. Есть ли явные недостатки?
  2. Есть ли тонкие недостатки?
  3. Есть ли более надежный подход?
  4. Поможет ли что-нибудь добавить хэш с другим хешем?
  5. Есть ли более простой и не менее надежный подход?

Новый вопрос:

Лично я считаю, что нам лучше оставить это как сеанс на стороне сервера; Кто-нибудь может указать мне какие-либо документы или статьи, доказывающие или опровергающие неотъемлемый риск отправки всех данных проверки только на стороне клиента?

1 Ответ

3 голосов
/ 26 октября 2009

Предполагая, что нет другой безопасности, чем указано здесь:
Похоже, что злоумышленник может решить один раз и сохранить cookie.
Затем она имеет свои постоянные session_hash и captcha_hash. Ничто не мешает ей отправить тот же файл cookie с теми же хэшированными данными, что может привести к повреждению вашей системы. Этого можно избежать, используя time как часть captcha_hash (вам нужно округлить его до четного времени, возможно, нескольких минут - и проверить две опции - текущее и предыдущее)

Чтобы засечь, ты сказал:

"session_hash" предназначен для предотвращения использования одного и того же cookie на нескольких машинах.

Это правда?
На isCaptchaValid вы делаете String session_hash = substring(cookie, 64, 64); - то есть: вы полагаетесь на данные в файле cookie . Как вы можете сказать, что он не был скопирован с другого компьютера? - вы не хешируете данные клиента снова, чтобы подтвердить это (на самом деле, у вас есть случайное число, поэтому это может быть невозможно). Как вы можете узнать, что это новый запрос, и он не использовался?

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

  1. Ева открыла страницу входа.
  2. Получает новый файл cookie и новую капчу.
  3. Заменяет его на свое старое печенье с хешированными данными своей старой cptcha.
  4. Отправляет старое печенье и userInputValue со старым словом капчи.
  5. С этим вводом isCaptchaValid проверяет запрос - captcha_hash, session_hash, userInputValue и BASE64_8192BIT_SECRET все те же, что и в первом запросе.

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

...