使用 Oauth 的 Powershell Exchange EWS 脚本身份验证无法使用保存密码哈希文件

Powershell Exchange EWS script authentication using Oauth unable to use a save password hash file

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

$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) 

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

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

错误:调用 RestMethod:{"error":"invalid_grant","error_description":"AADSTS50126: Error validating credentials due to invalid username or password..."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

##########

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

根据 pip3r 的回答更新脚本,微软支持

密码和机密以纯文本形式在线上传递,但不是 暴露在脚本中,并有一定程度的安全性保存为哈希 文件

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

选择为 Azure 注册应用程序使用证书而不是 一个 App Secret,以提高在线安全性

替代选项是使用“Azure Automation”,它允许 运行 来自 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"

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

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

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

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

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

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

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