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

Код

Я создал страницу входа, которая сочетает в себе проверку подлинности с помощью форм и встроенную проверку подлинности 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.

Также

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

Обновить

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

2 ответа

Решение

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

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

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

Код в Global.asx будет в том же духе.

Обрабатывать Application_AuthenticateRequest явно:

protected void Application_AuthenticateRequest(object sender, EventArgs e)
{
    try
    {
        if (IsAutomation() && 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() && 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>");
    }
}

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

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

Другие вопросы по тегам