Система 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);
}
Концепция:
- "session_hash" предназначен для предотвращения использования одного и того же cookie на нескольких машинах и обеспечивает период времени, после которого он становится недействительным.
- И "session_hash", и "captcha_hash" имеют свои собственные секретные соленые ключи.
- Эти соленые ключи BASE64_8192BIT_SECRET_A и _B являются частями закрытого ключа RSA, хранящегося на сервере.
- В «captcha_hash» добавляется как секрет, так и «session_hash».
- Разделители добавляются, когда используются предоставленные клиентом данные, чтобы избежать сплайс-атак.
- "captcha_hash" и "session_hash" оба хранятся в файле cookie на стороне клиента.
РЕДАКТИРОВАТЬ: re: Kobi Спасибо за отзыв!
(я бы ответил в комментариях, но, похоже, он не принимает форматирование, которое работает в вопросах?)
Каждый раз, когда они получают доступ к странице входа, капча заменяется; Это, однако, предполагает, что они не просто повторно отправляют без перезагрузки страницы формы входа. Реализация на основе сеансов использует время истечения, чтобы избежать этой проблемы. Мы также могли бы добавить одноразовый номер на страницу входа, но для этого нам также понадобится серверное хранилище сеансов.
Согласно предложению Коби, временной интервал истечения теперь включен в хешированные данные, но единодушным является его добавление в session_hash вместо этого, поскольку для сеанса интуитивно понятно наличие тайм-аута.
Эта идея хэширования некоторых данных и включения другого хэша в эти данные кажется мне подозрительной. Есть ли какая-то выгода, или нам лучше использовать один хеш, содержащий все соответствующие данные (время, IP, User-agent, значение Captcha и секретный ключ). В этой реализации мы в основном говорим пользователю о хэшированном открытом тексте.
Вопросы:
- Есть ли явные недостатки?
- Есть ли тонкие недостатки?
- Есть ли более надежный подход?
- Поможет ли что-нибудь добавить хэш с другим хешем?
- Есть ли более простой и не менее надежный подход?
Новый вопрос:
Лично я считаю, что нам лучше оставить это как сеанс на стороне сервера; Кто-нибудь может указать мне какие-либо документы или статьи, доказывающие или опровергающие неотъемлемый риск отправки всех данных проверки только на стороне клиента?