Аутентификация пользователей DotNetNuke в ColdFusion
Есть ли способ аутентифицировать пользователей из других веб-приложений, используя DNN-логины?
У нас есть основной сайт, который использует DNN, а логины пользователей хранятся в таблице членства в asp. Из того, что я читал, пароли зашифрованы с использованием ключа машины, а затем засолены. Я вижу, где эта информация, но не могу правильно зашифровать пароли с помощью этого метода.
Я пытаюсь использовать веб-приложение Coldfusion на том же сервере, где находится наш сайт DNN, но оно не хочет работать. Можно подумать, что с функцией шифрования ColdFusion было бы немного проще:
Encrypt(passwordstring, key [, algorithm, encoding, IVorSalt, iterations])
Независимо от того, что я пытаюсь, я никогда не получаю подходящее значение.
Любая помощь, понимание или указание мне в правильном направлении будет принята с благодарностью!
3 ответа
(Изменить: оригинальный ответ не работал во всех случаях. Существенно пересмотрено...)
Из того, что я прочитал, DNN по умолчанию использует хэш SHA1. barnyr ветка barnyr показывает, что она просто хэширует объединенную соль и пароль, но с несколькими изменениями.
- DNN использует UTF-16LE для извлечения байтов пароля, а не типичный для CF UTF-8.
- Он также извлекает солт и байты пароля отдельно, что может привести к отличным результатам, чем просто декодирование всего в одну строку, что
hash()
делает. (См. Демо ниже)
Учитывая, что CF9Hash
функция не принимает двоичные файлы (поддерживается в CF11), я не думаю, что возможно дублировать результаты только с собственными функциями CF. Вместо этого я бы предложил расшифровать строки в двоичный файл, а затем напрямую использовать Java:
Код:
<cfscript>
thePassword = "DT!@12";
base64Salt = "+muo6gAmjvvyy5doTdjyaA==";
// extract bytes of the salt and password
saltBytes = binaryDecode(base64Salt, "base64");
passBytes = charsetDecode(thePassword, "UTF-16LE" );
// next combine the bytes. note, the returned arrays are immutable,
// so we cannot use the standard CF tricks to merge them
ArrayUtils = createObject("java", "org.apache.commons.lang.ArrayUtils");
dataBytes = ArrayUtils.addAll( saltBytes, passBytes );
// hash binary using java
MessageDigest = createObject("java", "java.security.MessageDigest").getInstance("SHA-1");
MessageDigest.update(dataBytes);
theBase64Hash = binaryEncode(MessageDigest.digest(), "base64");
WriteOutput("theBase64Hash= "& theBase64Hash &"<br/>");
</cfscript>
Демо Отличия:
<cfscript>
theEncoding = "UTF-16LE";
thePassword = "DT!@12";
base64Salt = "+muo6gAmjvvyy5doTdjyaA==";
// extract the bytes SEPARATELY
saltBytes = binaryDecode(base64Salt, "base64");
passBytes = charsetDecode(thePassword, theEncoding );
ArrayUtils = createObject("java", "org.apache.commons.lang.ArrayUtils");
separateBytes = ArrayUtils.addAll( saltBytes, passBytes );
// concatenate first, THEN extract the bytes
theSalt = charsetEncode( binaryDecode(base64Salt, "base64"), theEncoding );
concatenatedBytes = charsetDecode( theSalt & thePassword, theEncoding );
// these are the raw bytes BEFORE hashing
WriteOutput("separateBytes= "& arrayToList(separateBytes, "|") &"<br>");
WriteOutput("concatenatedBytes"& arrayToList(concatenatedBytes, "|") );
</cfscript>
Результаты:
separateBytes = -6|107|-88|-22|0|38|-114|-5|-14|-53|-105|104|77|-40|-14|104|68|0|84|0|33|0|64|0|49|0|50|0
concatenatedBytes = -6|107|-88|-22|0|38|-114|-5|-14|-53|-105|104|-3|-1|68|0|84|0|33|0|64|0|49|0|50|0
Скорее всего, пароль не зашифрован, он хешируется. Хеширование отличается от шифрования, потому что оно не обратимо.
Для этого вы бы не использовали функцию encrypt() ColdFusion, вы бы использовали функцию hash().
Итак, на вопросы, на которые вам нужно ответить, чтобы выяснить, как хэшировать пароли в CF, чтобы иметь возможность авторизации против пользователей DNN:
- Какой алгоритм использует DNN для хэширования паролей?
- Как соль используется с паролем перед хэшированием?
- DNN итерирует по хэшу X количество раз, чтобы улучшить безопасность?
На все эти вопросы необходимо ответить, чтобы определить, как CF должен использовать функцию hash() в сочетании с солью и паролями, предоставленными пользователем.
Я сделаю некоторые предположения, чтобы дать ответ.
Если мы предположим, что выполняется нотерация и что соль просто добавляется к паролю перед использованием SHA1 для хеширования пароля, то вы сможете воспроизвести хэш-дайджест, например, так:
<cfset hashDigest = hash(FORM.usersubmittedPassword & saltFromDB, "SHA1") />
(Отправка нового ответа, чтобы отделить "зашифрованный" процесс от "хеширования")
Для "зашифрованных" ключей сторона DNN использует стандартные алгоритмы, т.е. DES, 3DES или AES - в зависимости от настроек вашего machineKey. Но с некоторыми отличиями вы должны соответствовать в вашем коде CF. Не зная ваших фактических настроек, я предполагаю, что вы используете настройки по умолчанию 3DES
теперь.
Данные для шифрования
Зашифрованное значение представляет собой комбинацию соли и пароля. Но, как и в случае хеширования, DNN использует UTF-16LE. К сожалению, ColdFusion's Encrypt()
Функция всегда предполагает UTF-8, что даст совсем другой результат. Так что вам нужно использовать EncryptBinary
функция вместо
// sample valus
plainPassword = "password12345";
base64Salt = "x7le6CBSEvsFeqklvLbMUw==";
hexDecryptKey = "303132333435363738393031323334353637383930313233";
// first extract the bytes of the salt and password
saltBytes = binaryDecode(base64Salt, "base64");
passBytes = charsetDecode(plainPassword, "UTF-16LE" );
// next combine the bytes. note, the returned arrays are immutable,
// so we cannot use the standard CF tricks to merge them
ArrayUtils = createObject("java", "org.apache.commons.lang.ArrayUtils");
dataBytes = ArrayUtils.addAll( saltBytes, passBytes );
Алгоритм шифрования
С блочными шифрами ColdFusion по умолчанию переходит в режим ECB. (См. " Сильное шифрование в ColdFusion"). В то время как.NET по умолчанию использует режим CBC, который требует дополнительного значения IV. Таким образом, вы должны настроить свой код CF, чтобы соответствовать.
// convert DNN hex key to base64 for ColdFusion
base64Key = binaryEncode(binaryDecode( hexDecryptKey, "hex"), "base64");
// create an IV and intialize it with all zeroes
// block size: 16 => AES, 8=> DES or TripleDES
blockSize = 8;
iv = javacast("byte[]", listToArray(repeatString("0,", blocksize)));
// encrypt using CBC mode
bytes = encryptBinary(dataBytes, base64Key, "DESede/CBC/PKCS5Padding", iv);
// result: WBAnoV+7cLVI95LwVQhtysHb5/pjqVG35nP5Zdu7T/Cn94Sd8v1Vk9zpjQSFGSkv
WriteOutput("encrypted password="& binaryEncode( bytes, "base64" ));