ADFS 验证流程

PowerShell访问基于ADFS认证的Web应用程序 2


什么是ADFS?

在今天的OAuth协议风靡全球之前,微软2005年12月在Windows 2003 R2开始引入了活动目录联合服务 (ADFS),可以为多个Web应用程序提供单点登录。而OAtuh的历史才是2006 年11月 由Blaine Cook开发Twitter OpenID 时开始的。

传统的Windows认证

在早期的Windows域环境中,我们可能见过针对于企业内网的Web应用程序实现的Windows 认证。目标网站部署在域环境中某台服务器上,用户在域内的机器上访问目标网站时,目标网站可以拿到该访客的域用户名,形如domain\user,然后通过访问活动目录AD,辨别用户的身份权限是否属于某个特定的用户组。这样的部署有两个限制:

  1. 目标网站如果要能在internet上提供访问,它必须先能访问内网的活动目录,同时还得接入互联网。如果有多个网站,每个网站的服务器都得做出这样的苛刻配置。
  2. 如果是多个网站,用户在访问第一个网站时,需要输入用户凭据。在访问第下一个网站时,仍旧需要输入用户凭据。

ADFS的基本原理

于是,存在一种更优化的部署可能。专门部署一个Web代理服务器位于网关位置,既可以访问内网的活动目录,同时负责帮助部署在内网或者外网上面的其它Web应用程序提供认证功能。这样让目标网站的认证和部署更加灵活。ADFS就是这样实现的。

ADFS部署拓扑图

ADFS部署拓扑图

ADFS验证的基本流程

  1. 用户第一次访问目标网站,目标网站发现是匿名用户,将请求重定向至ADFS认证网站。
  2. 用户在ADFS认证网站上输入自己的域账号和密码,ADFS服务器会请求活动目录来验证用户身份。
  3. 如果验证通过ADFS会得到一个包含用户信息的用户访问令牌,直接发送给目标网站(这里是本文使用PowerShell调用的重点,下文会提到
  4. 目标网站得到合法的用户令牌,会在返回给用户的请求中写入Federation Cookie。
  5. 接下来用户和目标网站之间的所有请求都会包含Cookie作为身份验证,直到Cookie过期。
ADFS 验证流程

ADFS 验证流程

什么是访问令牌?

访问令牌是一段包含用户信息的配置文件,可以把它看成一张登机牌,既包含了乘客(用户)是谁,也包含了乘客在什么时间段(有效期)可以乘坐某一次航班(目标网站)。

PowerShell获取ADFS的访问令牌环

PowerShell获取ADFS访问令牌环的意义在于可以通过编程的方式,而不是浏览器跳转完成ADFS验证。再实际一点的意义比如我们的某个service是基于ADFS验证的,在写API的自动化测试时,通过测试账号以静默方式拿到访问令牌环,功德无量啊!

ADFS访问令牌环可以直接调用.NET中的System.ServiceModel.dllSystem.IdentityModel.dll来获取。

这里我引用了 Josh Gavant的文章:Request ADFS Security Token with PowerShell

大家可以去微软脚本中心下载这段Invoke-ADFSSecurityTokenRequest脚本实现,下面是如何调用,非常方便吧!

$mySite= "我的目标网站"
$token=Invoke-ADFSSecurityTokenRequest `
-ClientCredentialType UserName `
-ADFSBaseUri https://corp.sts.pstips.net `
-AppliesTo $mySite `
-UserName 'mosser' `
-Password '' `
-Domain 'pstips.net' `
-OutputType Token `
-SAMLVersion 1 `
-IgnoreCertificateErrors

$createdTime = $token.ValidFrom.ToString("o")
$expiresTime = $token.ValidTo.ToString("o")
$binarySecurityToken = $token.TokenXml.OuterXml

 

PowerShell携带ADFS令牌环来发送请求

$securityToken=@'
<t:RequestSecurityTokenResponse xmlns:t="http://schemas.xmlsoap.org/ws/2005/02/trust">
<t:Lifetime>
<wsu:Created xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">{0}</wsu:Created>
<wsu:Expires xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">{1}</wsu:Expires>
</t:Lifetime>
<wsp:AppliesTo xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy">
<wsa:EndpointReference xmlns:wsa="http://www.w3.org/2005/08/addressing">
<wsa:Address>{2}</wsa:Address>
</wsa:EndpointReference>
</wsp:AppliesTo>
<t:RequestedSecurityToken>{3}</t:RequestedSecurityToken>
<t:TokenType>urn:oasis:names:tc:SAML:1.0:assertion</t:TokenType>
<t:RequestType>http://schemas.xmlsoap.org/ws/2005/02/trust/Issue</t:RequestType>
<t:KeyType>http://schemas.xmlsoap.org/ws/2005/05/identity/NoProofKey</t:KeyType>
</t:RequestSecurityTokenResponse>
'@ -f $createdTime,$expiresTime,$mySite,$binarySecurityToken

$encodedSecurityToken = [System.Web.HttpUtility]::UrlEncode($securityToken)
$requestTokenBody = "wa=wsignin1.0&wresult=$encodedSecurityToken&wctx=rm=0&id=passive&ru=%2f"
$response = Invoke-WebRequest -Uri $mySite -Body $requestTokenBody -Method Post -ContentType 'application/x-www-form-urlencoded' -SessionVariable session
$fedAuthCookie = ($session.Cookies.GetCookies($mySite) | 
Where-Object { $_.name -eq 'FedAuth'}).Value 

Invoke-WebRequest $mySite -WebSession $session

我们先把security token转换成RequestSecurityTokenResponse这样的XML格式,然后把它发送给目标网站。目标网站会在响应中包含一段cookie。在接下来的一连串请求中,我们只需携带cookie即可。

PowerShell发送请求时如何传递Cookie?

Cookie在HTTP请求中,位于请求的header中,但是在使用Invoke-WebRequest时却不能直接在header参数中指定Cookie。按照Invoke-WebRequest的设计来讲,Cookie应当是被保护的,所有的Cookie应当是来自目标网站的主动设置,而非客户端可以指定,随意摊派,随意篡改。

于是在发第一次请求时,我们通过-SessionVariable参数来记录当前会话,在接下来的请求中我们使用-WebSession参数来复用这一会话。稍微了解HTTP协议的您应当清楚,HTTP是无状态的,每一条请求都是孤立的,服务器端要认识客户端的某几条请求来自同一个会话,只能依赖于Cookie,而幸运的是上面的脚本中的$session刚好履行了保存Cookie来保持会话这一职责。

关于上面发送请求的代码,参考自:Making requests using WebClient and security tokens

注意

  • 在拿令牌环时,我们有传递一个-SAMLVersion 1参数,其实也可以传递2,具体根据目标网站期望的版本。
  • 不论用C#还是Powershell在测试时,遇到任何问题,都不要着急。您可以打开fiddler工具,在浏览器中打开目标网站,监测请求的参数,和链接地址。我们的目标是通过code发送的请求要和网站自己重定向的请求保持一致。
本文链接: https://www.pstips.net/access-web-app-that-authenticated-with-adfs.html
请尊重原作者和编辑的辛勤劳动,欢迎转载,并注明出处!

关于 Mooser Lee

我是一个Powershell的爱好者,创建了PowerShell中文博客,热衷于Powershell技术的搜集和分享。本站部分内容来源于互联网,不足之处敬请谅解,并欢迎您批评指正。

回复 jailman 取消回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注

2 条评论 “PowerShell访问基于ADFS认证的Web应用程序