取消NTLM身份验证对话框

本文关键字:对话框 身份验证 NTLM 取消 | 更新日期: 2023-09-27 18:09:05

代码

我创建了一个登录页面,它将窗体身份验证与集成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,我想依靠Forms Authentication。不幸的是,我看到的情况是,如果浏览器没有配置为接受IWA,它会弹出丑陋的NTLM身份验证对话框(看起来像基本对话框(。我怎样才能让它不出现?

背景

我这样做的主要原因是,同一个网站可以通过桌面用户(在域上(或手机(iPhone/Windows Phone(访问。iPhone不支持保存NTLM身份验证的密码,这对我的用户来说是一个麻烦。

要测试

如果您想在自己的环境中测试此代码,请为表单身份验证配置一个站点,确保在IIS中检查了匿名身份验证,而不是IWA。

此外

此代码未经过充分测试/充实。如果你是一个偶然发现我的问题的人,不要认为它是完全安全的,然后去你的网站上实现它。此代码处于早期开发阶段。也就是说,如果你想留言说如何改进,请随意。

更新

我已经更新了我的代码和问题,以反映我设法获得它的事实,这样当用户取消丑陋的身份验证对话框时,他们就可以使用表单身份验证登录。但我仍然希望这种丑陋的对话被压制。

取消NTLM身份验证对话框

我怀疑不需要的弹出窗口源于最初的请求,在浏览器收到401之前,该请求不包含Authorization标头。相反,如果你预测需要表格授权,你需要选择避免发放401。

考虑这种方法:

  • 启用表单身份验证作为默认模式(而不是NTLM(,以及
  • 如果您的用户代理不是移动代理(或者您认为构成NTLM浏览器的IP/用户代理限制的任何组合(,请修改Global.asax以模拟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作为身份验证方法之前。答案是响亮的"不"。控制这种行为的区域和安全设置完全在用户的机器上。

现在,如果您在intranet环境中,并且您可以识别某些IP地址范围属于您已经知道将执行自动IWA的机器,那么这当然有效。在我看来,这听起来像是你在试图概括,因此,你无法做到这一点。