Аутентификация SPNEGO работает из пользовательского Java-клиента, но НЕ из веб-браузера
У меня проблемы с аутентификацией через SPNEGO из веб-браузера (Internet Explorer 11) в веб-сервис, предлагаемый пользовательским сервером приложений Java.
Я могу успешно пройти проверку подлинности с использованием SPNEGO на том же сервере приложений, используя пользовательское клиентское приложение Java.
Подробности реализации пользовательского Java-клиента и сервера приложений можно найти ниже.
Я подозреваю, что SPNEGO из веб-браузера не работает, потому что:
а) Является ли токен из Internet Explorer действительным токеном SPNEGO?
Токен GSSAPI, предоставляемый веб-браузером, отличается от того, который предоставлен моим клиентом Java, и может не являться действительным токеном SPNEGO / Kerberos. Java-клиент предоставляет заголовок авторизации, начинающийся с "Negotiate YIMMQA..." (OK), тогда как веб-браузер предоставляет заголовок авторизации, начинающийся с "Negotiate oYIMRz..." (вероятно, НЕ OK).
и / или
б) Формат основного имени сервера
По историческим причинам сервер приложений работает с использованием имени участника службы, которое фактически является принципалом пользователя Microsoft Active Directory (format = "user@DOMAIN"), в то время как я сильно подозреваю, что реализации SPNEGO в веб-браузере используют запрошенный URL-адрес для создания службы Основное имя. Действительно, это именно то, что делает мой пользовательский Java-клиент при работе в Linux с бэкэндом Linux.
Детали реализации:
Сервер приложений Java работает на Windows Server 2012. Реализация Kerberos / SPNEGO - это чистый Java JAAS + GSSAPI.
Java-клиент работает в Windows (7 / 10) и может быть настроен на использование Java SSPI (через Waffle) или JAAS + GSSAPI. Обе реализации создают токены GSS, принятые сервером.
Сгенерированные токены GSS / SPNEGO переносятся в заголовки запросов веб-службы (клиент) и ответов (сервер).
Сервер использует Oids "1.3.6.1.5.5.2" (SPNEGO) и "1.2.840.113554.1.2.2" (Kerberos).
Тестирование с использованием Custom Java Client (ОК):
Сервер может аутентифицировать Java-клиент за одно рукопожатие. Клиент Java напрямую вызывает веб-сервис с заголовком авторизации, начинающимся с "Согласовать YIMMQA...". После декодирования Base64 на сервере длина gssapiData 3140 байтов и вызов acceptSecContext() выполнен успешно.
Если я преобразовываю gssapiData из этого вызова в строку и ищу в ней что-нибудь удобочитаемое человеком, то в начале я нахожу "EXAMPLE.COM" и "user-DEV". Это похоже на имя участника-службы, которое использует сервер, принципал пользователя Active Directory ("user-Dev@EXAMPLE.COM").
Тестирование с использованием Internet Explorer 11 (НЕ ОК):
Первый вызов из браузера имеет пустой заголовок авторизации. Мой сервер предлагает "Согласовать" -> ОК.
Второй вызов из браузера имеет заголовок авторизации, начинающийся с "Переговоры по YH4GBis...". После декодирования Base64 длина gssapiData составляет 128 байт. Очевидно, что это не содержит служебный билет.
Если я преобразую gssapiData в строку, в середине я найду символы "NTLMSSP". Я думаю, что браузер предлагает NTLM. Мой сервер отклоняет этот вызов.
Третий вызов из браузера имеет заголовок авторизации, начинающийся с "Negotiate oYIMRz...". После декодирования Base64 длина gssapiData составляет 3147 байт (очень близко к длине, получаемой от клиента Java).
Однако, когда мой сервер выполняет acceptSecContext(), он выдает ошибку. "Исключение GSS: обнаружен дефектный токен (уровень механизма: заголовок GSS не нашел правильный тег). -> НЕ ОК.
Это говорит о том, что токен недействителен, или я использую неправильные Oids для его чтения.
Если я преобразую gssapiData из этого вызова в строку, то в начале я найду "HTTP" и "APPSERVER.example.com". Это похоже на имя участника-службы Kerberos (SPN), созданное с использованием URL-адреса в качестве основы. -> Это подсказывает мне, что мой сервер приложений должен работать с именем участника-службы в формате, например, "HTTP/APPSERVER.example.com" или "HTTP/appserver.example.com@EXAMPLE.COM" (второй формат моя конфигурация Linux / FreeIPA использует).
В качестве примечания: На платформе Windows, на которой сосредоточен этот вопрос, у меня нет прав на создание / изменение имен SPN или псевдонимов на одно и то же или на использование других веб-браузеров. На моей среде разработки Linux я делаю, что может обеспечить дополнительный вклад.,,
1 ответ
Быстрый ответ
Требуются два исправления:
1) Internet Explorer (IE) создает имя участника-службы (SPN) на основе URL-адреса. например, https://appserver.example.com/foo приводит к имени SPN "HTTP/APPSERVER.example.com".
Следовательно, в Active Directory в указанном формате необходимо было установить правильные имена участников службы в качестве псевдонимов имени участника-пользователя (UPN), используемого сервером приложений.
а также
2) Токен из Internet Explorer является действительным токеном SPNEGO, но не принят GSS API на сервере.
Однако с некоторыми простыми манипуляциями со строкой входящего токена можно извлечь токен Kerbeos, который GSS примет и успешно аутентифицирует.
Более длинный ответ
1) Основные имена службы...
После публикации этой проблемы здесь мы настроили 2 имени участника-службы HTTP / APPSERVER.example.com и HTTP / APPSERVER в качестве псевдонимов для имени пользователя сервера UPN user-Dev@EXAMPLE.COM.
Сервер продолжает работать, используя UPN user-Dev@EXAMPLE.COM. (мое первоначальное предположение, что сервер должен был работать с одним из новых имен SPN, было неверным.)
Мой Java-клиент теперь может использовать новые имена участников-служб для приобретения билетов службы Kerberos и для создания токенов, которые успешно аутентифицируются моим сервером.
Однако токены из Internet Explorer продолжают отклоняться.
2) Токены из Internet Explorer против моего клиента Java...
Токен из моего Java-клиента начинается следующим образом:
Переговоры YIIMdwYGKwYBBQUCoI....
Base64 декодируется и представляется в виде шестнадцатеричных байтов:
60 82 0C 77 06 06 2B 06 01 05 05 02 A0 ………
из которых 06 06 2B 06 01 05 05 02 является OID SPNEGO 1.3.6.1.5.5.2 .
Токен из Internet Explorer начинается следующим образом:
Переговоры oYIMPjCCDDqgAwoBAaKCDDEEggwtYIIMKQYJKoZIhvcSAQIC
Base64 декодируется и представляется в виде шестнадцатеричных байтов:
A1 82 0C 3E 30 82 0C 3A A0 03 0A 01 01 A2 82 0C 31 04 82 0C 2D 60 82 0C 29 06 09 2A 86 48 86 F7 12 01 02 02….
Это Spnego NegTokenTarg, так как он начинается с "А1". Однако класс java sun.security.jgss.GSSHeader будет отклонять любой токен GSS, который не начинается с "60".
Изучение байта IE NegTokenTarg за байтом показывает, что после первых 21 байта у меня есть серия байтов, очень близкая к токену из моего приложения:
60 82 0C 29 06 09 2A 86 48 86 F7 12 01 02 02....
из которых 06 09 2A 86 48 86 F7 12 01 02 02 является OID Kerberos 1.2.840.113554.1.2.2
Если я извлечу этот токен, отбросив первые 21 байт исходного токена (или предоставлю gssContext.acceptSecContext (gssapiData, offset, gssapiData.length) со смещением 21), то GSS API сможет прочитать новый токен и извлечь основной пользователь и, таким образом, аутентифицировать запрос из Internet Explorer.
В приведенном ниже примере кода используется манипулирование строкой строки авторизации с конечным кодированием base64 для достижения того же:
String auth =req.headers("Authorization");
if ( auth != null && auth.startsWith("Negotiate ")) {
//smells like an SPNEGO request, so get the token from the http headers
String authBody = auth.substring("Negotiate ".length());
if (authBody.startsWith("oY")) {
// This is a NegTokenTarg from IE, which GSS API does not properly handle.
// However if we chop of the first (28) chars, we find a Kerberos Token starting with "60 82 0C" that GSS can handle.
authBody=authBody.substring(authBody.indexOf("YI", 2));
}
try {
byte gssapiData[] = Base64.getDecoder().decode(authBody);
gssContext = initGSSContext(MyUtils.SPNEGOOID, MyUtils.KRB5OID);
byte token[] = gssContext.acceptSecContext(gssapiData, offset, gssapiData.length);
..etc.
В заключение я думаю, что мы имеем
a) Слабость API Java GSS: GSS не принимает напрямую токен SPNEGO, который является NegTokenTarg.
или же
б) Взаимодействие между Internet Explorer и моим сервером, в результате чего IE отправляет NegTokenTarg, чего не ожидает GSS API.
IE - взаимодействие сервера:
1) Запрос от IE (без согласования)
2) Отклонить с моего сервера, с согласованием
3) 2-й запрос от IE, с Negotiate Header + Token, который выглядит как NTLM, а не Kerberos. -> Это может быть причиной возникновения проблемы.
4) Отклонить с моего сервера с токеном согласования + SPNEGO
5) 3-й запрос от IE, с заголовком согласования + SPNEGO NegTokenTarg
Справочная информация:
Мой сервер приложений использует Java JAAS + GSS для функциональности Kerberos / Spnego. Мой пользовательский клиент может использовать либо Java JAAS + GSS, либо Microsoft SSPI + Waffle.
Я нашел этот документ Microsoft очень полезным для понимания формата токена SPNEGO.
https://msdn.microsoft.com/en-us/library/ms995330.aspx
и этот блог, чтобы понять, как "обрабатывать" отрицательные десятичные байты. (Числа от -128 до -1 переводятся как от 128 до 255).
http://sketchytech.blogspot.com/2015/11/bytes-for-beginners-representation-of.html
Поскольку стандартным браузером для моих целевых конечных пользователей является Internet Explorer, у которого нет реалистичной возможности использовать что-то "лучше", в то время как поиск в Google я наткнулся на код обработки SPN для Chromium и Firefox. Ссылки ниже. Код Chromium содержит обширные комментарии и ссылки на статьи базы знаний и блоги малого и среднего бизнеса.
Chromiumn Code
https://cs.chromium.org/chromium/src/net/http/http_auth_handler_negotiate.cc?type=cs&l=142
Код FireFox
https://dxr.mozilla.org/mozilla-central/source/extensions/auth/nsAuthSSPI.cpp#98