浏览器登录内网应用正常,curl/wget/Python请求返回401错误排查
访问异常根本原因定位
- 服务端未开启Basic/Digest认证,仅支持Negotiate(Kerberos/NTLM集成Windows认证)
从所有响应的WWW-Authenticate响应头可以确认,服务端只下发了Negotiate、NTLM两种认证方式的标识,完全没有提及Basic、Digest协议。你之前尝试的所有Basic、Digest认证请求,都属于服务端不支持的认证方式,会被直接拦截返回401。 - 现有工具的认证流程配置错误,未匹配到服务端要求的NTLM交互逻辑
- curl调用时优先尝试了Negotiate分支的Kerberos认证,因本机无有效Kerberos凭据缓存(日志中
gss_init_sec_context() failed就是该问题的直接表现)报错中断,没有回退到NTLM认证的交互流程 - 系统自带版本的wget默认不支持NTLM协议,收到401响应后只会反复尝试Basic认证,最终判定授权失败
- Python requests原生不支持NTLM认证,你调用的Basic、DigestAuth适配器无法和服务端完成NTLM的多步认证握手
- curl调用时优先尝试了Negotiate分支的Kerberos认证,因本机无有效Kerberos凭据缓存(日志中
- Cookie流程是认证通过后的附属环节,不是独立的登录入口
浏览器的访问逻辑是先完成HTTP层的NTLM/Negotiate认证,拿到有效身份后,服务端才会下发ASP.NET_SessionId、appname_Cookie、.application_SSO_Cookie等会话Cookie,后续请求携带这些Cookie即可保持会话。所有脚本请求因为第一步认证就被拦截,服务端根本不会下发会话Cookie,自然无法复现浏览器的200访问流程。
对应解决方案
curl 正确调用方式
强制跳过Kerberos优先分支,直接走NTLM认证,配合Cookie持久化模拟浏览器行为,执行命令:
curl --ntlm --user "DOMAIN\你的账号:密码" \ --cookies-jar cookies.txt \ --cookie cookies.txt \ https://application.intranet.net/appname/Reporting/ReportListStart.aspx
参数说明:
--ntlm:指定走NTLM认证流程,避免优先触发Kerberos认证报错--cookies-jar/--cookie:自动存储、携带服务端下发的会话Cookie,和浏览器行为一致- 用户名需要补全AD域前缀,格式和浏览器弹窗输入的完全一致(例如
公司域\zhangsan)
wget 正确调用方式
系统默认wget不带NTLM支持,可使用以下参数强制匹配认证流程:
wget --http-user="DOMAIN\你的账号" \ --http-password="你的密码" \ --auth-no-challenge \ --keep-session-cookies \ --save-cookies cookies.txt \ --load-cookies cookies.txt \ https://application.intranet.net/appname/Reporting/ReportListStart.aspx
如果执行后仍反复返回401,说明当前wget版本未编译NTLM支持,建议直接使用Python方案替代。
Python requests 正确调用方式
原生requests不支持NTLM认证,需要安装对应扩展完成认证握手:
- 安装依赖:
pip install requests-ntlm - 参考实现代码:
import requests from requests_ntlm import HttpNtlmAuth from urllib.parse import urlparse target_url = "https://application.intranet.net/appname/Reporting/ReportListStart.aspx" # 用户名格式和浏览器输入保持一致,使用原始字符串避免转义问题 auth_user = r"DOMAIN\你的账号" auth_pwd = "你的密码" # 用Session自动管理Cookie,和浏览器会话逻辑一致 session = requests.Session() session.auth = HttpNtlmAuth(auth_user, auth_pwd) # 首次请求,关闭自动重定向以手动处理302流程 resp = session.get(target_url, allow_redirects=False, verify=True) # 如果收到302重定向,自动携带Cookie请求跳转地址 if resp.status_code == 302: redirect_path = resp.headers.get("Location") if redirect_path.startswith("/"): parsed_url = urlparse(target_url) redirect_url = f"{parsed_url.scheme}://{parsed_url.netloc}{redirect_path}" else: redirect_url = redirect_path resp = session.get(redirect_url) print(f"最终响应状态码:{resp.status_code}") print(resp.text)
通用排查注意事项
- 用户名格式必须和浏览器弹窗输入的完全一致,不能只写短用户名、也不能替换为邮箱格式,格式不匹配就算密码正确也会返回401
- 如果按上述方法仍返回401,检查请求的Host头是否和浏览器访问的域名完全一致,部分内网应用会根据Host头校验认证合法性,错配Host头就算认证通过也会被拦截
- 如果服务端强制启用Kerberos(Negotiate)认证、拒绝NTLM降级,需要先在客户端执行
kinit 你的账号@公司域.COM缓存Kerberos票据,再通过curl的--negotiate参数、配合Python的requests-negotiate扩展完成认证,不需要传明文密码。
内容的提问来源于stack exchange,提问作者jsf80238
相关产品推荐
相关产品推荐

