Python 中的 NTLM 身份验证

Posted

技术标签:

【中文标题】Python 中的 NTLM 身份验证【英文标题】:NTLM authentication in Python 【发布时间】:2011-02-27 12:12:41 【问题描述】:

我正在尝试使用 python 从 Windows 7 在 IIS (Windows Server 2003) 上实现 NTLM 身份验证。 LAN Manager 身份验证级别:仅发送 NTLM 响应。 客户端机器和服务器在同一个域中。 域控制器 (AD) 位于另一台服务器上(也运行 Windows Server 2003)。

我收到 401.1 - 未经授权:由于凭据无效,访问被拒绝。 您能否帮我找出这段代码有什么问题和/或向我展示解决此问题的其他可能方向(使用 NTLM 或 Kerberos)?

import sys, httplib, base64, string
import urllib2
import win32api
import sspi 
import pywintypes
import socket

class WindoewNtlmMessageGenerator:
    def __init__(self,user=None):
        import win32api,sspi
        if not user:
            user = win32api.GetUserName()
        self.sspi_client = sspi.ClientAuth("NTLM",user)   

    def create_auth_req(self):
        import pywintypes
        output_buffer = None
        error_msg = None
        try:
            error_msg, output_buffer = self.sspi_client.authorize(None)            
        except pywintypes.error:
            return None
        auth_req = output_buffer[0].Buffer
        auth_req = base64.encodestring(auth_req)
        auth_req = string.replace(auth_req,'\012','')
        return auth_req 

    def create_challenge_response(self,challenge):
        import pywintypes
        output_buffer = None
        input_buffer = challenge
        error_msg = None        
        try:
            error_msg, output_buffer = self.sspi_client.authorize(input_buffer)
        except pywintypes.error:
            return None
        response_msg = output_buffer[0].Buffer       
        response_msg = base64.encodestring(response_msg)
        response_msg = string.replace(response_msg,'\012','')
        return response_msg 


fname='request.xml'
request = file(fname).read()
ip_host = '10.0.3.112'

ntlm_gen = WindoewNtlmMessageGenerator()
auth_req_msg = ntlm_gen.create_auth_req()
auth_req_msg_dec = base64.decodestring(auth_req_msg)
auth_req_msg = string.replace(auth_req_msg,'\012','')
webservice = httplib.HTTPConnection(ip_host) 
webservice.putrequest("POST", "/idc/idcplg")
webservice.putheader("Content-length", "%d" % len(request)) 
webservice.putheader('Authorization', 'NTLM'+' '+auth_req_msg) 
webservice.endheaders()
resp = webservice.getresponse()
resp.read()

challenge = resp.msg.get('WWW-Authenticate')
challenge_dec = base64.decodestring(challenge.split()[1])

msg3 = ntlm_gen.create_challenge_response(challenge_dec)
webservice = httplib.HTTP(ip_host) 
webservice.putrequest("POST", "/idc/idcplg?IdcService=LOGIN&Auth=Intranet")
webservice.putheader("Host", SHOD)
webservice.putheader("Content-length", "%d" % len(request))
webservice.putheader('Authorization', 'NTLM'+' '+msg3) 
webservice.putheader("Content-type", "text/xml; charset=\"UTF-8\"")
webservice.putheader("SOAPAction", "\"\"")
webservice.endheaders()
webservice.send(request)
statuscode, statusmessage, header = webservice.getreply()
res = webservice.getfile().read()
res_file = file('result.txt','wb')
res_file.write(res)
res_file.close()

sspi.py 在这里可用: https://ironpython.svn.codeplex.com/svn/IronPython_Main/External.LCA_RESTRICTED/Languages/IronPython/27/Lib/site-packages/win32/lib/sspi.py

谢谢!

【问题讨论】:

【参考方案1】:
import win32com.client

url = 'https://....'

h = win32com.client.Dispatch('WinHTTP.WinHTTPRequest.5.1')
h.SetAutoLogonPolicy(0)
h.Open('GET', url, False)
h.Send()
result = h.responseText
result

【讨论】:

这个答案很棒。这个对我有用!我在 Windows 环境中,想使用域身份验证。它有效!!! POST 请求怎么样?我想知道如何处理 Open 函数中的参数... 如何让这种方法在 Flask 环境中进行多线程证明?【参考方案2】:

我发现出了什么问题。 我应该保持连接活跃。 那就是货! 现在这个问题解决了。

class WindoewNtlmMessageGenerator:
   def __init__(self,user=None):
       import win32api,sspi
       if not user:
           user = win32api.GetUserName()
       self.sspi_client = sspi.ClientAuth("NTLM",user)   

   def create_auth_req(self):
       import pywintypes
       output_buffer = None
       error_msg = None
       try:
           error_msg, output_buffer = self.sspi_client.authorize(None)             
       except pywintypes.error:           
            return None
       auth_req = output_buffer[0].Buffer
       auth_req = base64.b64encode(auth_req)
       return auth_req 


  def create_challenge_response(self,challenge):
      import pywintypes
      output_buffer = None
      input_buffer = challenge
      error_msg = None
      try:
          error_msg, output_buffer = self.sspi_client.authorize(input_buffer)
      except pywintypes.error:
          return None
      response_msg = output_buffer[0].Buffer        
      response_msg = base64.b64encode(response_msg) 
      return response_msg 


SHOD='qqq.yyy.dev'
answer='result.xml'
fname='request.xml'
try:
    a_file = open(fname, 'r')
    f=open(fname, 'r')
except IOError:
    sys.exit()
size = os.path.getsize(fname)
i=0
for line in f:
    i=i+1
count_string=i
f.close()
size=size-count_string+1


print '1' 

try:
    webservice = httplib.HTTPConnection(SHOD)     
    webservice.putrequest("POST", "/idc/idcplg?IdcService=LOGIN&Auth=Intranet")
    webservice.putheader("Content-length", "%d" % 0)
    webservice.putheader("Content-type", "text/xml")
    #webservice.putheader("User-Agent", 'Python-urllib/2.6')
    webservice.endheaders()
    res=webservice.getresponse()
except:
    msg= "unable to connect to URL:  "+ SHOD
    sys.exit()
if res.status == 401:
    auth_methods = [s.strip() for s in 
                    res.msg.get('WWW-Authenticate').split(",")]
    print auth_methods
if res.status <> 401:
    msg= "unable to connect to URL:  "+ SHOD_
    log_error(msg,answer)
    sys.exit()



print '2' 

ntlm_gen = WindoewNtlmMessageGenerator()
auth_req_msg = ntlm_gen.create_auth_req()
webservice.putrequest("POST", "/idc/idcplg?IdcService=LOGIN&Auth=Intranet")
webservice.putheader("Content-length", "%d" % 0)
webservice.putheader("Connection", "Keep-Alive")
#webservice.putheader("User-Agent", 'Python-urllib/2.6')
webservice.putheader('Authorization', 'NTLM'+' '+auth_req_msg) 
webservice.endheaders()
resp = webservice.getresponse()
resp.read()
print resp.status
challenge = resp.msg.get('WWW-Authenticate')
challenge_dec = base64.b64decode(challenge.split()[1])



print '3' 

msg3 = ntlm_gen.create_challenge_response(challenge_dec)
webservice.putrequest("POST", "/idc/idcplg?IdcService=LOGIN&Auth=Intranet")
webservice.putheader("Content-type", "text/xml; charset=UTF-8")
webservice.putheader("Content-length", "%d" %(size))
webservice.putheader("Connection", "Close")
webservice.putheader('Authorization', 'NTLM'+' '+msg3)
#webservice.putheader("User-Agent", 'Python-urllib/2.6') 
webservice.endheaders()
sable = a_file.read()     
webservice.send(sable)
resp = webservice.getresponse()
res=resp.read()

【讨论】:

您应该将此答案标记为已接受(单击旁边的绿色复选标记),这样它就不会被列为未回答的问题。 这对 Linux 有什么作用? (相信有些库不可用) @Dimitris:它使用本地 Windows 函数对域进行身份验证 - 没有办法从非 Windows 机器上做到这一点。您必须在本地重新实现 Netlogon 协议 (MS-NRPC),这涉及实现底层 MS-RPC 层。不是很有趣。 @bobince,您可以使用 PyAuthenNTLM2 和 Apache 针对 NTLM 身份验证存储区对 linux Web 服务进行身份验证...参见 this blog 示例 @Svetlana 您绝对应该将此标记为答案,该方法效果很好

以上是关于Python 中的 NTLM 身份验证的主要内容,如果未能解决你的问题,请参考以下文章

ZAP 中的 NTLM 身份验证

如何支持 NTLM 身份验证并回退到 ASP.NET MVC 中的表单?

如何使用参数(通过 HTTP)向 NTLM 进行身份验证?

使用 HTTP Web 请求发送 HTTP 标头以进行 NTLM 身份验证

HTTP 请求未经客户端身份验证方案“Ntlm”授权

HTTP 请求未使用客户端身份验证方案“Ntlm”未经授权从服务器接收到的身份验证标头为“NTLM”