通过 HTTPS 的 Powershell Invoke-RestMethod
Powershell Invoke-RestMethod over HTTPS
我正在尝试通过 powershell 与服务通信,但失败得很惨。我怀疑它是证书,我用谷歌搜索了答案并找到了两个选项,none 其中对我有用。我也尝试过将两者结合起来,但没有成功。
选项 1:
add-type @"
using System.Net;
using System.Security.Cryptography.X509Certificates;
public class TrustAllCertsPolicy : ICertificatePolicy {
public bool CheckValidationResult(
ServicePoint srvPoint, X509Certificate certificate,
WebRequest request, int certificateProblem) {
return true;
}
}
"@
[System.Net.ServicePointManager]::CertificatePolicy = New-Object TrustAllCertsPolicy
$urlJSON = "https://internal.ad.local/path/api_jsonrpc.php"
#Create authentication JSON object using ConvertTo-JSON
$objAuth = (New-Object PSObject | Add-Member -PassThru NoteProperty jsonrpc '2.0' |
Add-Member -PassThru NoteProperty method 'user.authenticate' |
Add-Member -PassThru NoteProperty params @{user="user";password="password"} |
Add-Member -PassThru NoteProperty id '2') | ConvertTo-Json
Invoke-RestMethod -Uri $urlJSON -body $objAuth -method "Post"
选项 2:
[System.Net.ServicePointManager]::ServerCertificateValidationCallback = {$true}
$urlJSON = "https://internal.ad.local/path/api_jsonrpc.php"
#Create authentication JSON object using ConvertTo-JSON
$objAuth = (New-Object PSObject | Add-Member -PassThru NoteProperty jsonrpc '2.0' |
Add-Member -PassThru NoteProperty method 'user.authenticate' |
Add-Member -PassThru NoteProperty params @{user="user";password="password"} |
Add-Member -PassThru NoteProperty id '2') | ConvertTo-Json
Invoke-RestMethod -Uri $urlJSON -body $objAuth -method "Post"
错误信息如下:
Invoke-RestMethod : The underlying connection was closed: An unexpected error occurred on a send.
At C:\Users\user\AppData\Local\Tempeaa6f7-62a0-4c10-88d1-79212d652bc9.ps1:24 char:1
+ Invoke-RestMethod -Uri $urlJSON -body $objAuth -method "Post"
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (System.Net.HttpWebRequest:HttpWebRequest) [Invoke-RestMethod], WebException
+ FullyQualifiedErrorId : WebCmdletWebResponseException,Microsoft.PowerShell.Commands.InvokeRestMethodCommand
我可能会补充:
- 使用 Web 浏览器直接浏览该服务
- 我也尝试打开 HTTP,并且成功了
- 该服务使用的证书是自签名的,但我的机器通过根证书信任它(在 IE 或 Chrome 中没有警告问题)
- 我已经完成网络捕获并确保数据包确实到达了服务器。
感谢任何建议!
亲切的问候,
帕特里克
已更新 post 树先生提出的以下建议:
Name : lambda_method
DeclaringType :
ReflectedType :
Module : RefEmit_InMemoryManifestModule
MethodHandle :
Attributes : PrivateScope, Public, Static
CallingConvention : Standard
IsSecurityCritical : False
IsSecuritySafeCritical : False
IsSecurityTransparent : True
ReturnType : System.Boolean
ReturnParameter :
ReturnTypeCustomAttributes : System.Reflection.Emit.DynamicMethod+RTDynamicMethod+EmptyCAHolder
MemberType : Method
MethodImplementationFlags : NoInlining
IsGenericMethodDefinition : False
ContainsGenericParameters : False
IsGenericMethod : False
IsPublic : True
IsPrivate : False
IsFamily : False
IsAssembly : False
IsFamilyAndAssembly : False
IsFamilyOrAssembly : False
IsStatic : True
IsFinal : False
IsVirtual : False
IsHideBySig : False
IsAbstract : False
IsSpecialName : False
IsConstructor : False
CustomAttributes :
MetadataToken :
根据 Tree 先生的评论更新 2:
Invoke-RestMethod : The underlying connection was closed: An unexpected error occurred on a send.
At C:\Users\user\AppData\Local\Temp\ff47910e-fd8e-4be8-9241-99322144976a.ps1:13 char:1
+ Invoke-RestMethod -Uri $urlJSON -body $objAuth -method "Post"
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (System.Net.HttpWebRequest:HttpWebRequest) [Invoke-RestMethod], WebException
+ FullyQualifiedErrorId : WebCmdletWebResponseException,Microsoft.PowerShell.Commands.InvokeRestMethodCommand
您似乎正在尝试使用 Invoke-RestMethod
调用 json API。根据 documentation:
-ContentType
Specifies the content type of the web request.
If this parameter is omitted and the request method is POST,
Invoke-RestMethod sets the content type to
"application/x-www-form-urlencoded". Otherwise, the content type is
not specified in the call.
要使用 json 正文,您需要使用 Invoke-RestMethod -ContentType 'application/json' <other args>
我在解决另一件事的同时解开了谜团。有问题的 Web 服务器仅支持 TLS1.1 和 TLS1.2。 Powershell 似乎不支持这一点。如果我启用了 TLS1.0,它就可以工作。
要强制使用 TLS1.2,您可以使用此行:
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
希望对其他人有所帮助,感谢所有有用的评论!
/帕特里克
最近我经历了很多痛苦,为了度过类似的情况。我正在使用试用 SaaS 服务创建概念证明。该服务有一个自签名的 SSL 证书,因此我想在尝试对其调用 POST 方法时忽略证书错误(类似于 curl 的“-k”参数)。经过一番努力,我发现它需要 both - (a) 忽略证书验证错误的调用和 (b) 明确设置 TLS 1.2 作为安全协议。我认为是后者,因为该服务可能拒绝尝试使用任何其他协议进行连接。 (我花了太多时间尝试不同的变体来按照各种 SOF 线程上的建议进行每个操作,但独立...)
这是有效的代码...
重要:证书验证绕过纯粹是针对 prototype/PoC。我们不打算在生产中这样做(你也不应该这样做!)。
$defaultSecurityProtocol = $null
try
{
#BUGBUG, TODO: Disabling cert validation for the duration of this call...('trial' version cert is self-signed.)
#Remove this after the PoC.
[System.Net.ServicePointManager]::ServerCertificateValidationCallback = { $true }
#Cache the previous protocol setting and explicitly require TLS 1.2
$defaultSecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol
[System.Net.ServicePointManager]::SecurityProtocol = `
[System.Net.SecurityProtocolType]::Tls12
if (-not [String]::IsNullOrWhiteSpace($authZHeaderValue))
{
$response = Invoke-WebRequest `
-Uri $webhookUrl `
-Method "Post" `
-Body $eventJson `
-Header @{ $authZHeaderName = $authZHeaderValue}
}
else
{
$response = Invoke-WebRequest `
-Uri $webhookUrl `
-Method "Post" `
-Body $eventJson
}
}
catch
{
$msg = $_.Exception.Message
$status = $_.Exception.Status
$hr = "{0:x8}" -f ($_.Exception.HResult)
$innerException = $_.Exception.InnerException
#Just issue a warning about being unable to send the notification...
Write-Warning("`n`t[$status] `n`t[0x$hr] `n`t[$msg] `n`t[$innerException]")
}
finally
{
# Set securityProtocol and CertValidation behavior back to the previous state.
[System.Net.ServicePointManager]::SecurityProtocol = $defaultSecurityProtocol
[System.Net.ServicePointManager]::ServerCertificateValidationCallback = $null
}
还想补充一点,首选的安全协议会随着各种漏洞的发现和修复的实施而不断变化。此外,不同的系统(客户端操作系统中的 SSL/TLS 堆栈和 servers/services)通常在 latest/most 安全选项方面有自己的追赶。因此,究竟哪个标志可能起作用将取决于客户端和服务器系统以及时间(因为几个月后 TLS1.2 可能不再是首选)。理想情况下,根本不需要指定标志。有关更多信息,请参阅 this MSDN document 中的 "Remarks" 部分。
这是对我有用的完整 Powershell 脚本:
try {
# Configure API request payload
$url = "https://my.server.com/api/countries"
$method = "POST"
$headers = @{
"Content-Type" = "application/json"
}
$body = '{
"name": "India"
}'
# Reading request body from json file
# $body = Get-Content request.json | Out-String
$certificate = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2 "C:\ClientCert.pfx", "Password1"
# Call the API
Invoke-RestMethod -Uri $url -Method $method -Headers $headers -Body $body -Certificate $certificate
Write-Host "`n#################### SUCCESS ####################`n"
} catch {
Write-Host "`n#################### ERROR ####################`n"
# Getting response code
$responseCode = $_.Exception.Message
# Getting response body
if ($_.Exception.Response -ne $null) {
$responseStream = $_.Exception.Response.GetResponseStream()
$streamReader = New-Object System.IO.StreamReader($responseStream)
$streamReader.BaseStream.Position = 0
$streamReader.DiscardBufferedData()
$responseBody = $streamReader.ReadToEnd()
}
Write-Host "Response Code - " $responseCode
Write-Host "Response Body - " $responseBody
Write-Host "`n###############################################`n"
}
我正在尝试通过 powershell 与服务通信,但失败得很惨。我怀疑它是证书,我用谷歌搜索了答案并找到了两个选项,none 其中对我有用。我也尝试过将两者结合起来,但没有成功。
选项 1:
add-type @"
using System.Net;
using System.Security.Cryptography.X509Certificates;
public class TrustAllCertsPolicy : ICertificatePolicy {
public bool CheckValidationResult(
ServicePoint srvPoint, X509Certificate certificate,
WebRequest request, int certificateProblem) {
return true;
}
}
"@
[System.Net.ServicePointManager]::CertificatePolicy = New-Object TrustAllCertsPolicy
$urlJSON = "https://internal.ad.local/path/api_jsonrpc.php"
#Create authentication JSON object using ConvertTo-JSON
$objAuth = (New-Object PSObject | Add-Member -PassThru NoteProperty jsonrpc '2.0' |
Add-Member -PassThru NoteProperty method 'user.authenticate' |
Add-Member -PassThru NoteProperty params @{user="user";password="password"} |
Add-Member -PassThru NoteProperty id '2') | ConvertTo-Json
Invoke-RestMethod -Uri $urlJSON -body $objAuth -method "Post"
选项 2:
[System.Net.ServicePointManager]::ServerCertificateValidationCallback = {$true}
$urlJSON = "https://internal.ad.local/path/api_jsonrpc.php"
#Create authentication JSON object using ConvertTo-JSON
$objAuth = (New-Object PSObject | Add-Member -PassThru NoteProperty jsonrpc '2.0' |
Add-Member -PassThru NoteProperty method 'user.authenticate' |
Add-Member -PassThru NoteProperty params @{user="user";password="password"} |
Add-Member -PassThru NoteProperty id '2') | ConvertTo-Json
Invoke-RestMethod -Uri $urlJSON -body $objAuth -method "Post"
错误信息如下:
Invoke-RestMethod : The underlying connection was closed: An unexpected error occurred on a send.
At C:\Users\user\AppData\Local\Tempeaa6f7-62a0-4c10-88d1-79212d652bc9.ps1:24 char:1
+ Invoke-RestMethod -Uri $urlJSON -body $objAuth -method "Post"
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (System.Net.HttpWebRequest:HttpWebRequest) [Invoke-RestMethod], WebException
+ FullyQualifiedErrorId : WebCmdletWebResponseException,Microsoft.PowerShell.Commands.InvokeRestMethodCommand
我可能会补充:
- 使用 Web 浏览器直接浏览该服务
- 我也尝试打开 HTTP,并且成功了
- 该服务使用的证书是自签名的,但我的机器通过根证书信任它(在 IE 或 Chrome 中没有警告问题)
- 我已经完成网络捕获并确保数据包确实到达了服务器。
感谢任何建议!
亲切的问候, 帕特里克
已更新 post 树先生提出的以下建议:
Name : lambda_method
DeclaringType :
ReflectedType :
Module : RefEmit_InMemoryManifestModule
MethodHandle :
Attributes : PrivateScope, Public, Static
CallingConvention : Standard
IsSecurityCritical : False
IsSecuritySafeCritical : False
IsSecurityTransparent : True
ReturnType : System.Boolean
ReturnParameter :
ReturnTypeCustomAttributes : System.Reflection.Emit.DynamicMethod+RTDynamicMethod+EmptyCAHolder
MemberType : Method
MethodImplementationFlags : NoInlining
IsGenericMethodDefinition : False
ContainsGenericParameters : False
IsGenericMethod : False
IsPublic : True
IsPrivate : False
IsFamily : False
IsAssembly : False
IsFamilyAndAssembly : False
IsFamilyOrAssembly : False
IsStatic : True
IsFinal : False
IsVirtual : False
IsHideBySig : False
IsAbstract : False
IsSpecialName : False
IsConstructor : False
CustomAttributes :
MetadataToken :
根据 Tree 先生的评论更新 2:
Invoke-RestMethod : The underlying connection was closed: An unexpected error occurred on a send.
At C:\Users\user\AppData\Local\Temp\ff47910e-fd8e-4be8-9241-99322144976a.ps1:13 char:1
+ Invoke-RestMethod -Uri $urlJSON -body $objAuth -method "Post"
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (System.Net.HttpWebRequest:HttpWebRequest) [Invoke-RestMethod], WebException
+ FullyQualifiedErrorId : WebCmdletWebResponseException,Microsoft.PowerShell.Commands.InvokeRestMethodCommand
您似乎正在尝试使用 Invoke-RestMethod
调用 json API。根据 documentation:
-ContentType
Specifies the content type of the web request.
If this parameter is omitted and the request method is POST, Invoke-RestMethod sets the content type to "application/x-www-form-urlencoded". Otherwise, the content type is not specified in the call.
要使用 json 正文,您需要使用 Invoke-RestMethod -ContentType 'application/json' <other args>
我在解决另一件事的同时解开了谜团。有问题的 Web 服务器仅支持 TLS1.1 和 TLS1.2。 Powershell 似乎不支持这一点。如果我启用了 TLS1.0,它就可以工作。
要强制使用 TLS1.2,您可以使用此行:
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
希望对其他人有所帮助,感谢所有有用的评论!
/帕特里克
最近我经历了很多痛苦,为了度过类似的情况。我正在使用试用 SaaS 服务创建概念证明。该服务有一个自签名的 SSL 证书,因此我想在尝试对其调用 POST 方法时忽略证书错误(类似于 curl 的“-k”参数)。经过一番努力,我发现它需要 both - (a) 忽略证书验证错误的调用和 (b) 明确设置 TLS 1.2 作为安全协议。我认为是后者,因为该服务可能拒绝尝试使用任何其他协议进行连接。 (我花了太多时间尝试不同的变体来按照各种 SOF 线程上的建议进行每个操作,但独立...)
这是有效的代码...
重要:证书验证绕过纯粹是针对 prototype/PoC。我们不打算在生产中这样做(你也不应该这样做!)。
$defaultSecurityProtocol = $null
try
{
#BUGBUG, TODO: Disabling cert validation for the duration of this call...('trial' version cert is self-signed.)
#Remove this after the PoC.
[System.Net.ServicePointManager]::ServerCertificateValidationCallback = { $true }
#Cache the previous protocol setting and explicitly require TLS 1.2
$defaultSecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol
[System.Net.ServicePointManager]::SecurityProtocol = `
[System.Net.SecurityProtocolType]::Tls12
if (-not [String]::IsNullOrWhiteSpace($authZHeaderValue))
{
$response = Invoke-WebRequest `
-Uri $webhookUrl `
-Method "Post" `
-Body $eventJson `
-Header @{ $authZHeaderName = $authZHeaderValue}
}
else
{
$response = Invoke-WebRequest `
-Uri $webhookUrl `
-Method "Post" `
-Body $eventJson
}
}
catch
{
$msg = $_.Exception.Message
$status = $_.Exception.Status
$hr = "{0:x8}" -f ($_.Exception.HResult)
$innerException = $_.Exception.InnerException
#Just issue a warning about being unable to send the notification...
Write-Warning("`n`t[$status] `n`t[0x$hr] `n`t[$msg] `n`t[$innerException]")
}
finally
{
# Set securityProtocol and CertValidation behavior back to the previous state.
[System.Net.ServicePointManager]::SecurityProtocol = $defaultSecurityProtocol
[System.Net.ServicePointManager]::ServerCertificateValidationCallback = $null
}
还想补充一点,首选的安全协议会随着各种漏洞的发现和修复的实施而不断变化。此外,不同的系统(客户端操作系统中的 SSL/TLS 堆栈和 servers/services)通常在 latest/most 安全选项方面有自己的追赶。因此,究竟哪个标志可能起作用将取决于客户端和服务器系统以及时间(因为几个月后 TLS1.2 可能不再是首选)。理想情况下,根本不需要指定标志。有关更多信息,请参阅 this MSDN document 中的 "Remarks" 部分。
这是对我有用的完整 Powershell 脚本:
try {
# Configure API request payload
$url = "https://my.server.com/api/countries"
$method = "POST"
$headers = @{
"Content-Type" = "application/json"
}
$body = '{
"name": "India"
}'
# Reading request body from json file
# $body = Get-Content request.json | Out-String
$certificate = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2 "C:\ClientCert.pfx", "Password1"
# Call the API
Invoke-RestMethod -Uri $url -Method $method -Headers $headers -Body $body -Certificate $certificate
Write-Host "`n#################### SUCCESS ####################`n"
} catch {
Write-Host "`n#################### ERROR ####################`n"
# Getting response code
$responseCode = $_.Exception.Message
# Getting response body
if ($_.Exception.Response -ne $null) {
$responseStream = $_.Exception.Response.GetResponseStream()
$streamReader = New-Object System.IO.StreamReader($responseStream)
$streamReader.BaseStream.Position = 0
$streamReader.DiscardBufferedData()
$responseBody = $streamReader.ReadToEnd()
}
Write-Host "Response Code - " $responseCode
Write-Host "Response Body - " $responseBody
Write-Host "`n###############################################`n"
}