首页 > 解决方案 > 使用 Oauth 的 Powershell Exchange EWS 脚本身份验证无法使用保存密码哈希文件

问题描述

通常对于预定的脚本,我将哈希文件保存到磁盘以供脚本使用,如下所示:

$Credential = Get-Credential Admin@domain.com 
$Credential.Password | ConvertFrom-SecureString | Set-Content "C:\admin.pwd" $Username = "Admin@domain.com" 
$Password = Get-Content "C:\admin.pwd" -ErrorAction stop | ConvertTo-SecureString
$Credential = New-Object System.Management.Automation.PSCredential($Username,$Password) 

如果正文中的密码元素以纯文本形式输入,则以下 Oath 令牌请求有效,但如果我使用变量 $Credential.Password 则无效。有没有办法让它工作,或者以其他方式保护密码?

以下令牌请求生成的错误:

错误:Invoke-RestMethod : {"error":"invalid_grant","error_description":"AADSTS50126: 由于用户名或密码无效,验证凭据时出错..."error_uri":"login.microsoftonline.com/error?code=50126 "}

## Request an access token

# Define AppId, secret and scope, your tenant name and endpoint URL
$AppId = 'AppIdHere'
$AppSecret = 'AppSecretHere'
$Scope = "https://outlook.office365.com/.default"
$TenantName = "Domain.onmicrosoft.com"
$Url = "https://login.microsoftonline.com/$TenantName/oauth2/v2.0/token"

# Add System.Web for urlencode
Add-Type -AssemblyName System.Web

# Create body
$Body = @{
    client_id = $AppId
    client_secret = $AppSecret
    scope = $Scope
    grant_type = 'password'
    username = 'Admin@domain.com'
    password = $Credential.Password
}

# Splat the parameters for Invoke-Restmethod for cleaner code
$PostSplat = @{
    ContentType = 'application/x-www-form-urlencoded'
    Method = 'POST'

    # Create string by joining bodylist with '&'
    Body = $Body
    Uri = $Url
}

# Request the token for user!
$Request = Invoke-RestMethod @PostSplat

$Request.access_token

##########

============================

根据 thepip3r 的回答和 Microsoft 支持更新了脚本

密码和秘密在网络上以纯文本形式传递,但不会在脚本中公开,并具有一定程度的安全性,保存为哈希文件

调整为不将密码或秘密保存到变量中,以提高安全性,免受可以访问内存的攻击(推荐 MS 支持)

为 Azure 注册应用程序使用证书而不是应用程序密钥的选项,以提高网络安全性

另一种选择是使用“ Azure 自动化”,它允许从 O365 中运行脚本,这应该更安全。另一种可能的替代方法可能是 Azure Functions。

# One time AppID\Secret hash save to file:
## $AppCredential = Get-Credential 'AppIdHere'
## $AppCredential.Password | ConvertFrom-SecureString | Set-Content "C:\App.pwd" 

# One time Admin hash save to file:
## $Credential = Get-Credential admin@domain.com
## $Credential.Password | ConvertFrom-SecureString | Set-Content "C:\admin.pwd" 

$AppId = 'AppIdHere'
$AppS = Get-Content "C:\App.pwd"  | ConvertTo-SecureString
$AppCredential = New-Object System.Management.Automation.PSCredential($AppId,$AppS)

$Username = "admin@domain.com"
$Password = Get-Content "C:\admin.pwd" | ConvertTo-SecureString
$Credential = New-Object System.Management.Automation.PSCredential($Username,$Password)        

### Request an access token ###

$Scope = "https://outlook.office365.com/.default"
$TenantName = "usablelife.onmicrosoft.com"
$Url = "https://login.microsoftonline.com/$TenantName/oauth2/v2.0/token"

# Add System.Web for urlencode
Add-Type -AssemblyName System.Web

# Request the token!
$Request = Invoke-RestMethod -Body @{
    client_id = $AppId
    client_secret = $AppCredential.GetNetworkCredential().Password
    scope = $Scope
    grant_type = 'password'
    username = $Username 
    password = $Credential.GetNetworkCredential().Password
} `
-ContentType 'application/x-www-form-urlencoded' `
-Method 'POST' `
-Uri "https://login.microsoftonline.com/$TenantName/oauth2/v2.0/token"

标签: powershellauthenticationexchange-serverexchangewebservices

解决方案


所以......我将提供这个作为答案,因为我希望人们理解这是一个示例,即使用 Get-Credential 将密码存储在文件中(甚至作为密文字符串值)的问题。

@mbromb,这将为您提供一种方法来测试您检索的值是否是正确的值:

在您的 $Credential 对象(最后一行)上,运行: $Credential.GetNetworkCredential().Password

这将是您最初使用 Get-Credential 在提示中输入的任何内容的PLAINTEXT值。因此,您可以验证在最初获取它之后是否将其写入文件、读回文件并将其转换为安全字符串对象是否按预期工作。

尝试更直接地解决这个问题:如果我找到您的“admin.pwd”文件,从中生成明文非常简单。

警告:您可以通过使用 ConvertTo/From-SecureString cmdlet 上的 -Key 或 -SecureKey 属性为此加密过程提供受保护的密钥来保护此值。Key 接受一个字节数组(最好是加密随机的,具有足够的熵来满足您的需要),SecureKey 接受一个字符串(密码)并根据您的密码生成字节数组。

注意事项:如果您已经尝试将密码存储到文件中,那么保护存储密码的密码可能不是正确的答案......


推荐阅读