Подавить диалоговое окно аутентификации NTLM

#c# #asp.net #forms-authentication #windows-authentication

#c# #asp.net #формы-аутентификация #проверка подлинности Windows

Вопрос:

Код

Я создал страницу входа, которая сочетает в себе проверку подлинности Forms со встроенной проверкой подлинности Windows.

 public partial class Login : System.Web.UI.Page
        {
        // http://www.innovation.ch/personal/ronald/ntlm.html
        // http://curl.cofman.dk/rfc/ntlm.html
        // http://blogs.msdn.com/b/chiranth/archive/2013/09/21/ntlm-want-to-know-how-it-works.aspx
        protected void Page_Load(object sender, EventArgs e)
            {
            if (!IsPostBack)
                {
                if (Request.Headers["Authorization"].IsNullOrEmpty())
                    {
                    Response.StatusCode = 401;
                    Response.AddHeader("WWW-Authenticate", "NTLM");
                    Email.SendMailToDebugger("Auth", "No Auth");
                    //Response.End();
                    }
                else if (Request.Headers["Authorization"].StartsWith("Negotiate"))
                    {
                    Response.StatusCode = 401;
                    Response.AddHeader("WWW-Authenticate", "NTLM");
                    Email.SendMailToDebugger("Auth", "Negotiate Auth");
                    Response.End();
                    }
                else if (Request.Headers["Authorization"].StartsWith("NTLM"))
                    {
                    string base64text = Request.Headers["Authorization"].Remove(0, 5); //Remove NTLM<space>
                    byte[] bytes = Convert.FromBase64String(base64text);
                    byte typebyte = bytes[8];

                    if (typebyte.ToString("X2") == "01") //type 1 message received
                        {
                        //send type 2 message
                        List<byte> responsebytes = new List<byte> { 0x4e, 0x54, 0x4c, 0x4d, 0x53, 0x53, 0x50, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 00, 0x02, 0x02, 0x00, 0x00, 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef };
                        string type2message = Convert.ToBase64String(responsebytes.ToArray());
                        Response.StatusCode = 401;
                        Response.AddHeader("WWW-Authenticate", "NTLM "   type2message);
                        Email.SendMailToDebugger("Auth", "Type 1 Received, Type 2 Sent");
                        Response.End();
                        }
                    else if (typebyte.ToString("X2") == "03") //type3 message received
                        {
                        var dv = Database.GetDataView("select UPPER('termana'||REPLACE(P.EMAIL,'@termana.com','')||p.init) displayname, 'termana\'||REPLACE(P.EMAIL,'@termana.com','') username  from tercons.phonebook p where P.COMPANY_ID=40");
                        string username = ""; //magic to get the username from the type3 response
                        Email.SendMailToDebugger("Auth", "Type 3 Received, logging in: "   username);
                        FormsAuthentication.RedirectFromLoginPage(username, false);
                        }
                    else
                        {
                        Email.SendMailToDebugger("Auth", "Unknown Type Received");
                        }
                    }
                else
                    {
                    Email.SendMailToDebugger("Auth", "Unknown Authentication Received: "   Request.Headers["Authorization"]);
                    }
                }
            }
        }
  

Вопрос

Пока это работает довольно хорошо. Он правильно регистрирует пользователя, если он поддерживает IWA. Если их браузер не настроен на прием IWA, я хочу вернуться к проверке подлинности с помощью форм. К сожалению, я вижу, что происходит, если браузер не настроен на прием IWA, появляется уродливое диалоговое окно аутентификации NTLM (выглядит как основное диалоговое окно). Как мне сделать так, чтобы это не отображалось?

Предыстория

Основная причина, по которой я это делаю, заключается в том, что к одному и тому же сайту можно получить доступ через пользователей настольных компьютеров (в домене) или мобильных устройств (iPhone / Windows Phone). И iPhone не поддерживает сохранение паролей для аутентификации NTLM, что создает проблемы для моих пользователей.

Для проверки

Если вы хотите протестировать этот код в своей собственной среде, настройте сайт для проверки подлинности с помощью форм, убедитесь, что в IIS установлена анонимная проверка подлинности, а не IWA.

Также

Этот код не полностью протестирован / доработан. Если вы случайный человек, который натыкается на мой вопрос, не думайте, что он абсолютно безопасен, а затем внедряйте его на своем сайте. Этот код находится на ранних стадиях разработки. Тем не менее, если вы хотите оставить комментарий о том, как его улучшить, не стесняйтесь.

Обновить

Я обновил свой код и вопрос, чтобы отразить тот факт, что мне удалось получить его, чтобы, когда пользователь отменяет некрасивое диалоговое окно аутентификации, он мог войти в систему с помощью проверки подлинности forms. Но я все равно хочу, чтобы это уродливое диалоговое окно было подавлено.

Ответ №1:

Я подозреваю, что нежелательное всплывающее окно возникает из-за первоначального запроса, НЕ содержащего Authorization заголовка, пока браузер не получит 401. Вместо этого вам нужно выбрать, чтобы избежать выдачи 401, если вы прогнозируете, что требуется авторизация форм.

Рассмотрите этот подход:

  • Включить проверку подлинности форм в качестве режима по умолчанию (не NTLM), а также
  • Измените Global.asax, чтобы имитировать аутентификацию NTLM, если ваш пользовательский агент не является мобильным агентом (или любой комбинацией ограничений IP / user agent, которые вы рассматриваете как составляющие браузеры NTLM).

Код в Global.asx будет примерно таким.

Явно обрабатывать запрос Application_AuthenticateRequest:

 protected void Application_AuthenticateRequest(object sender, EventArgs e)
{
    try
    {
        if (IsAutomation() amp;amp; Request.Headers["Authorization"] != null)
        {
            // Your NTML handling code here; below is what I use for Basic auth
            string[] parts = Request.Headers["Authorization"].Split(' ');
            string credentials = System.Text.Encoding.UTF8.GetString(Convert.FromBase64String(parts[1]));
            string[] auth = credentials.Split(':');
            if (Membership.ValidateUser(auth[0], auth[1]))
            {
                Context.User = Membership.GetUser(auth[0]);
            }
            else
            {
                Response.Clear();

                Response.StatusCode = 401;
                Response.StatusDescription = "Access Denied";
                Response.RedirectLocation = null;
                // Switch to NTLM as you see fit; just my sample code here
                Response.AddHeader("WWW-Authenticate", "Basic realm={my realm}");
                Response.ContentType = "text/html";
                Response.Write(@"
<html>
<head>
<title>401 Access Denied</title>
</head>
<body>
<h1>Access Denied</h1>
<p>The credentials supplied are invalid.</p>
</body>
</html>");
            }
        }
    }
    catch (System.Exception ex)
    {
        throw ex;
    }
}
  

Где IsAutomation определяет, хотите ли вы аутентифицировать формы или нет.

В моем случае IsAutomation выглядит следующим образом:

 protected bool IsAutomation()
{
    // In your case, I'd config-drive your desktop user agent strings
    if (!string.IsNullOrEmpty(Properties.Settings.Default.BasicAuthenticationUserAgents))
    {
        string[] agents = Properties.Settings.Default.BasicAuthenticationUserAgents.Split(';');
        foreach (string agent in agents)
            if (Context.Request.Headers["User-Agent"].Contains(agent)) return true;
    }
    return false;
}
  

Наконец, вам нужно перехватить перенаправление 302 и отправить вызов NTLM:

 protected void Application_EndRequest(object sender, EventArgs e)
{
    if (IsAutomation() amp;amp; Context.Response.StatusCode == 302)
    {
        Response.Clear();

        Response.StatusCode = 401;
        Response.StatusDescription = "Access Denied";
        Response.RedirectLocation = null;
        // Switch to NTLM as you see fit; just my sample code here
        Response.AddHeader("WWW-Authenticate", "Basic realm={your realm}");
        Response.ContentType = "text/html";
        Response.Write(@"
<html>
<head>
<title>401 Authorization Required</title>
</head>
<body>
<h1>Authorization Required</h1>
<p>This server could not verify that you are authorized to access the document requested.  Either you supplied the wrong credentials (e.g., bad password), or your browser doesn't understand how to supply the credentials required.</p>
</body>
</html>");
    }
}
  

Комментарии:

1. основная задача не совпадает с задачей ntlm.

2. согласен; Я был немного ленив и просто скопировал свою основную задачу вместо NTLM. Я добавил строку комментария над ней. 🙂

Ответ №2:

Я думаю, вы путаетесь между концепциями аутентификации NTLM / IWA и тонкостями автоматического входа в систему браузера для надежного сайта. Если бы я перефразировал этот вопрос, вы на самом деле спрашиваете, может ли сервер определить, будет ли браузер автоматически регистрировать кого-либо, не запрашивая учетные данные с помощью IWA, прежде чем предлагать IWA в качестве метода аутентификации. Ответом на это является решительное «нет». Зоны и параметры безопасности, которые управляют этим поведением, полностью находятся на компьютере пользователя.

Теперь, если вы находитесь в среде интрасети и можете распознать определенные диапазоны IP-адресов как принадлежащие машинам, которые, как вы уже знаете, будут выполнять автоматическую проверку подлинности IWA, тогда, конечно, это работает. Мне кажется, что вы пытаетесь обобщить, и для этого вы не можете заставить это работать.

Комментарии:

1. Этот парень , похоже, согласен с вами по поводу предварительной проверки на основе IP-адреса. Мне придется попробовать это завтра на работе. Я нахожусь в среде интрасети, но я не уверен, могу ли я доверять браузерам, не поддерживающим IWA, из определенного диапазона IP.

2. Возможно, вы можете использовать комбинацию заголовков HTTP и IP.