调用Microsoft SCOM REST API时缺失防伪造Cookie的问题求助
问题场景
尝试从内部部署的Microsoft System Center Operations Manager(SCOM)服务器获取数据,已通过NTLM认证成功,响应头返回SCOMSessionId和SCOM-CSRF-TOKEN,但调用/OperationsManager/data/state接口时,返回反伪造令牌缺失错误。
认证请求(成功)
curl --location 'https://xxxx/OperationsManager/authenticate' \ --header 'Content-Type: application/json; charset=utf-8' \ --header 'Authorization: NTLM xxxx' --data '"base64_encoded_data"'
数据请求(失败)
curl --location 'https://xxxx/OperationsManager/data/state' \ --header 'Content-Type: application/json; charset=utf-8' \ --header 'Cookie: SCOM-CSRF-TOKEN=xxxx; SCOMSessionId=xxxx' \ --data '{ "classId": "Microsoft.SystemCenter.Library!Microsoft.SystemCenter.HealthService", "criteria": "DisplayName LIKE '\''xxxx%'\''", "displayColumns": [ "healthstate", "displayname" ] }'
错误信息
{ "errorMessage": "The required anti-forgery cookie \"__RequestVerificationToken_xxxx\" is not present.", "errorTrace": " at System.Web.Helpers.AntiXsrf.TokenValidator.ValidateTokens(HttpContextBase httpContext, IIdentity identity, AntiForgeryToken sessionToken, AntiForgeryToken fieldToken)\r\n at System.Web.Helpers.AntiXsrf.AntiForgeryWorker.Validate(HttpContextBase httpContext, String cookieToken, String formToken)\r\n at Microsoft.EnterpriseManagement.OMDataService.Filters.ValidateAntiForgeryTokenAttribute.OnActionExecuting(HttpActionContext actionContext)\r\n at System.Web.Http.Filters.ActionFilterAttribute.OnActionExecutingAsync(HttpActionContext actionContext, CancellationToken cancellationToken)\r\n--- End of stack trace from previous location where exception was thrown ---\r\n at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()\r\n at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n at System.Web.Http.Filters.ActionFilterAttribute.<ExecuteActionFilterAsyncCore>d__0.MoveNext()\r\n--- End of stack trace from previous location where exception was thrown ---\r\n at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()\r\n at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n at System.Web.Http.Filters.ActionFilterAttribute.<CallOnActionExecutedAsync>d__5.MoveNext()\r\n--- End of stack trace from previous location where exception was thrown ---\r\n at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()\r\n at System.Web.Http.Filters.ActionFilterAttribute.<CallOnActionExecutedAsync>d__5.MoveNext()\r\n--- End of stack trace from previous location where exception was thrown ---\r\n at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()\r\n at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n at System.Web.Http.Filters.ActionFilterAttribute.<ExecuteActionFilterAsyncCore>d__0.MoveNext()\r\n--- End of stack trace from previous location where exception was thrown ---\r\n at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()\r\n at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n at System.Web.Http.Controllers.ActionFilterResult.<ExecuteAsync>d__2.MoveNext()\r\n--- End of stack trace from previous location where exception was thrown ---\r\n at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()\r\n at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n at System.Web.Http.Controllers.ExceptionFilterResult.<ExecuteAsync>d__0.MoveNext()" }
原因说明
__RequestVerificationToken是ASP.NET框架提供的反跨站请求伪造(CSRF)令牌,SCOM的API层基于ASP.NET构建,因此要求请求同时携带该令牌的cookie(有时还需在请求头/体中匹配)才能通过验证。你之前只提取了SCOMSessionId和SCOM-CSRF-TOKEN,遗漏了这个关键令牌。
解决步骤
1. 重新捕获完整的认证响应
使用curl -v参数查看认证请求的完整响应头,检查Set-Cookie字段中是否包含__RequestVerificationToken_xxxx开头的cookie:
curl -v --location 'https://xxxx/OperationsManager/authenticate' \ --header 'Content-Type: application/json; charset=utf-8' \ --header 'Authorization: NTLM xxxx' \ --data '"base64_encoded_data"'
注意:该令牌的名称可能带有后缀(如__RequestVerificationToken_Lw__),需要完整复制。
2. 补充所有必要的Cookie到请求中
将获取到的__RequestVerificationToken_xxxx加入Cookie请求头,与已有的SCOM-CSRF-TOKEN、SCOMSessionId一起携带:
curl --location 'https://xxxx/OperationsManager/data/state' \ --header 'Content-Type: application/json; charset=utf-8' \ --header 'Cookie: __RequestVerificationToken_xxxx=xxxx; SCOM-CSRF-TOKEN=xxxx; SCOMSessionId=xxxx' \ --data '{ "classId": "Microsoft.SystemCenter.Library!Microsoft.SystemCenter.HealthService", "criteria": "DisplayName LIKE '\''xxxx%'\''", "displayColumns": [ "healthstate", "displayname" ] }'
3. 尝试添加XSRF请求头(可选)
部分ASP.NET API要求同时在请求头中携带令牌值,可尝试添加X-XSRF-TOKEN或__RequestVerificationToken头,值为SCOM-CSRF-TOKEN或__RequestVerificationToken_xxxx的内容:
curl --location 'https://xxxx/OperationsManager/data/state' \ --header 'Content-Type: application/json; charset=utf-8' \ --header 'Cookie: __RequestVerificationToken_xxxx=xxxx; SCOM-CSRF-TOKEN=xxxx; SCOMSessionId=xxxx' \ --header 'X-XSRF-TOKEN: xxxx' \ --data '{ "classId": "Microsoft.SystemCenter.Library!Microsoft.SystemCenter.HealthService", "criteria": "DisplayName LIKE '\''xxxx%'\''", "displayColumns": [ "healthstate", "displayname" ] }'
4. 模拟浏览器完整请求流程(若上述方法无效)
如果直接调用authenticate端点未返回令牌,先请求SCOM Web控制台首页,该页面通常会初始化所有反伪造令牌:
# 第一步:请求首页获取令牌Cookie curl -v --location 'https://xxxx/OperationsManager' \ --header 'Authorization: NTLM xxxx' # 第二步:从响应Set-Cookie中提取__RequestVerificationToken_xxxx等Cookie,执行认证 curl -v --location 'https://xxxx/OperationsManager/authenticate' \ --header 'Content-Type: application/json; charset=utf-8' \ --header 'Cookie: __RequestVerificationToken_xxxx=xxxx; [其他从首页获取的Cookie]' \ --header 'Authorization: NTLM xxxx' \ --data '"base64_encoded_data"' # 第三步:携带所有Cookie发起数据请求 curl --location 'https://xxxx/OperationsManager/data/state' \ --header 'Content-Type: application/json; charset=utf-8' \ --header 'Cookie: __RequestVerificationToken_xxxx=xxxx; SCOM-CSRF-TOKEN=xxxx; SCOMSessionId=xxxx' \ --data '{...}'
内容的提问来源于stack exchange,提问作者Saikat

