Как подключить единый вход с помощью автономного браузера cefSharp к веб-серверу, который размещается на самоподписанном ПК? - PullRequest
0 голосов
/ 09 октября 2018

TL; DR: Я хватаюсь за соломинку, кто-нибудь получил SSO с CefSharp и может указать мне, что я делаю неправильно?Я пытаюсь подключиться к странице SSL-SSO через CefSharp, но это не работает - как и в Chrome-Browser.С IE это просто работает .Я добавил к доверенным сайтам (Proxy / Security), я попытался изменить политику URL-адреса chrome в реестре и внести изменения в настройки CefSharp - ничего не помогло.


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

Просмотр с обычным IE это просто работает :

  • Я получаю 302 ответа
  • перенаправленный сайт дает мне401 (неавторизованный) с NTLM, Negotiate
  • IE автоматически отправляет NTLM Auth и получает NTLM WWW-Authenticate
  • после еще 302, он заканчивается 200 и зарегистрированным состоянием на веб-сайте

Ошибка Browsig с Chrome 69.0.3497.100:

not secure

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


F12-Отладка в IE / Chrome:

  • В IE я вижу 302, за которыми следуют два 401 ответа, и заканчивается на авторизованном сайте.

  • В Chrome я вижу только 302 и 200 ответов иконец на "резервном" сайте входа в систему для входа пользователя / pw.

Основное различие в (одном из 302) заголовков запросов: NEGOTIATE против NTLM

// IE:
Authorization: NTLM TlRMT***==

// Chrome:
Authorization: Negotiate TlRMT***==
Upgrade-Insecure-Requests: 1
DNT: 1

Пока что не удалось подключиться через CefSharp,Я просто приземляюсь в его RequestHandler.GetAuthCredentials() - я не хочу передавать какие-либо учетные данные с этим.


Что я пытался заставить его работать в Windows / Chrome:

, что в целомничего не сделал: я все еще не получаю единый вход с использованием Chrome: not secure


Что я пытался заставить его работать внутри CefSharp:

  • происходит от CefSharp.Handler.DefaultRequestHandler, переопределяет
    • OnSelectClientCertificate -> никогда не вызывается
    • OnCertificateError -> больше не вызывается
    • GetAuthCredentials -> вызывается, ноЯ не хочу таким образом передавать учетные данные для входа - у меня уже есть работающее решение для случая http: // при вызове сайтов с обычной страницей входа.
  • предоставление объекта настроек для Cef.Initialize(...), который содержит
var settings = new CefSettings { IgnoreCertificateErrors = true, ... };
settings.CefCommandLineArgs.Add ("auth-server-whitelist", "*host-url*");
settings.CefCommandLineArgs.Add ("auth-delegate-whitelist", "*host-url*");
  • при создании браузера, предоставляющего RequestContext:
var browser = new CefSharp.OffScreen.ChromiumWebBrowser (
    "", requestContext: CreateNewRequestContext (webContext.Connection.Name));

<pre>CefSharp.RequestContext CreateNewRequestContext (string connName) { var subDirName = Helper.Files.FileHelper.MakeValidFileSystemName (connName); var contextSettings = new RequestContextSettings { PersistSessionCookies = false, PersistUserPreferences = false, CachePath = Path.Combine (Cef.GetGlobalRequestContext ().CachePath, subDirName), IgnoreCertificateErrors = true, }; // ... return new CefSharp.RequestContext (contextSettings); }

Я знаю, что часть этих изменений является избыточной (например, 3 способа задания белых списков, из которых как минимум 2 должны работать для CefSharp, не уверен насчетреестр один, влияющий на него) и в случае IgnoreCertificateErrors опасен и не может оставаться внутри. Я просто хочу, чтобы он работал как-то, чтобы затем обрезать то, что нужно сделать, чтобы он работал в производстве.


Исследования:

и другие ... еще никто не знает.


Вопрос: Я цепляюсь за соломинку, кто-нибудь получил SSO с работающим CefSharp и может указать мне, что я делаю неправильно?

1 Ответ

0 голосов
/ 02 ноября 2018

TL; DR: Я столкнулся (как минимум) с 2 проблемами: недействительными сертификатами SSL и проблемами с токеном Kerberos.В моей тестовой настройке установлены локальные компьютеры с веб-сервером, на который я звоню.Эти локальные компьютеры являются в основном виртуальными машинами ОС Windows с самозаверяющими сертификатами.Некоторые из них являются серверами Windows.Последний работал, а морозил нет.С IE оба работали.


Переход на рассматриваемый сайт с использованием https://... приводит к тому, что CEFsharp обнаруживает самозаверяющий сертификат (который не является частью доверенной цепочки сертификатов) - поэтому он будет вызыватьбраузеры RequestHandler (если установлено) и вызов его

public override bool OnCertificateError (IWebBrowser browserControl, IBrowser browser, 
                                         CefErrorCode errorCode, string requestUrl, 
                                         ISslInfo sslInfo, IRequestCallback callback)
{
  Log.Logger.Warn (sslInfo.CertStatus.ToString ());
  Log.Logger.Warn (sslInfo.X509Certificate.Issuer);

  if (CertIsTrustedEvenIfInvalid (sslInfo.X509Certificate))
  {
    Log.Logger.Warn ("Trusting: " + sslInfo.X509Certificate.Issuer);

    if (!callback.IsDisposed)
      using (callback)
      {
        callback?.Continue (true);
      }

    return true;
  }
  else
  {
    return base.OnCertificateError (browserControl, browser, errorCode, requestUrl,
                                    sslInfo, callback);
  }
}

Для целей тестирования я жестко закодировал некоторые тесты в CertIsTrustedEvenIfInvalid (sslInfo.X509Certificate), которые возвращали бы true для моей тестовой среды - это можетбыть заменен простым return false, всплывающим пользовательским интерфейсом, представляющим сертификат и спрашивающим пользователя, хочет ли он продолжить, или он может принять во внимание определенные предоставленные пользователем файлы сертификата - не знаю пока:

bool CertIsTrustedEvenIfInvalid (X509Certificate certificate)
{
  var debug = new Dictionary<string, HashSet<string>> (StringComparer.OrdinalIgnoreCase)
  {
    ["cn"] = new HashSet<string>(StringComparer.OrdinalIgnoreCase) { "some", "data" },
    ["ou"] = new HashSet<string>(StringComparer.OrdinalIgnoreCase) { "other", "stuff" },
    ["o"] = new HashSet<string>(StringComparer.OrdinalIgnoreCase) { "..." },
    ["l"] = new HashSet<string>(StringComparer.OrdinalIgnoreCase) { "Atlantis" },
    ["s"] = new HashSet<string>(StringComparer.OrdinalIgnoreCase) { "Outer Space" },
    ["c"] = new HashSet<string>(StringComparer.OrdinalIgnoreCase) { "whatsnot" },
  };

  var x509issuer = certificate.Issuer
    .Split (",".ToCharArray ())
    .Select (part => part.Trim().Split("=".ToCharArray(), 2).Select (p => p.Trim()))
    .ToDictionary (t => t.First (), t => t.Last ());

  return x509issuer.All (kvp => debug.ContainsKey (kvp.Key) &&
                                debug[kvp.Key].Contains (kvp.Value));
}

Только если SSL-Step работает, будет предпринята попытка SSO.


После решения проблемы с SSL я столкнулся с различным поведением Chrome по сравнению с IE / Firefox и т. Д., Как описано здесь @ Выбор схемы аутентификации - суть ее:

  • если сервер сообщает о нескольких схемах аутентификации, IE / Firefox использует первую известную ему - доставленную сервером (предпочтение по порядку)
  • Chrome использует ту, которая ему известнасчитается наивысшим приоритетом (в порядке: Negotiate -> NTLM -> Digest -> Basic), игнорируя упорядочение серверов альтернативных схем.

Мои серверы сообщили NTLM,Negotiante(этот порядок) - с IE это просто работало.

С Chrome это привело к обмену токенов Kerberos - который работал только тогда, когда веб-сервер был размещен в ОС Windows Server, - а не для ОС Windows Client.Возможно, какая-то неудачная конфигурация для компьютеров с клиентской ОС в AD использовалась.Не уверен, хотя - но в отношении серверной ОС это работает.

Дополнительно я реализовал

public override bool GetAuthCredentials (IWebBrowser browserControl, IBrowser browser, 
                                         IFrame frame, bool isProxy, string host, 
                                         int port, string realm, string scheme, 
                                         IAuthCallback callback)
{
  // pseudo code - asks for user & pw 
  (string UserName, string Password) = UIHelper.UIOperation (() =>
  {
    // UI to ask for user && password: 
    // return (user,pw) if input ok else return (null,null)
  });

  if (UserName.IsSet () && Password.IsSet ())
  {
    if (!callback.IsDisposed)
    {
      using (callback)
      {
        callback?.Continue (UserName, Password);
      }
      return true;
    }
  }

  return base.GetAuthCredentials (browserControl, browser, frame, isProxy, 
                                  host, port, realm, scheme, callback);
}

, чтобы обеспечить восстановление после сбоя, если SSO не сработал.После предоставления учетных данных AD в этом диалоговом окне также возможен вход в систему).

Для хорошей меры я также внес в белый список хосты в контексте CEF-Browser при создании нового посредника, например:

CefSharp.RequestContext CreateNewRequestContext (string subDirName, string host,
                                                 WebConnectionType conType)
{
  var contextSettings = new RequestContextSettings
  {
    PersistSessionCookies = false,
    PersistUserPreferences = false,
    CachePath = Path.Combine (Cef.GetGlobalRequestContext ().CachePath, subDirName),
  };

  var context = new CefSharp.RequestContext (contextSettings);

  if (conType == WebConnectionType.Negotiate) # just an enum for UserPW + Negotiate
    Cef.UIThreadTaskFactory.StartNew (() =>
     {
       // see https://cs.chromium.org/chromium/src/chrome/common/pref_names.cc  for names

       var settings = new Dictionary<string, string>
       {
         ["auth.server_whitelist"] = $"*{host}*",
         ["auth.negotiate_delegate_whitelist"] = $"*{host}*",
         // only set-able via policies/registry :/
         // ["auth.schemes"] = "ntlm" // "basic", "digest", "ntlm", "negotiate"
       };

       // set the settings - we *trust* the host with this and allow negotiation
       foreach (var s in settings)
         if (!context.SetPreference (s.Key, s.Value, out var error))
           Log.Logger.Debug?.Log ($"Error setting '{s.Key}': {error}");
     });

  return context;
}
...