web-dev-qa-db-fra.com

Powershell Invoke-RestMethod sur HTTPS

J'essaie de communiquer avec un service sur PowerShell mais j'échoue lamentablement. Je soupçonne que c'est le certificat et j'ai cherché sur Google la réponse et trouvé deux options, dont aucune ne fonctionnait pour moi. J'ai également essayé de combiner les deux sans succès.

Option 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"

Option 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"

Voici le message d'erreur:

Invoke-RestMethod : The underlying connection was closed: An unexpected error occurred on a send.
At C:\Users\user\AppData\Local\Temp\46eaa6f7-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

Je pourrais ajouter:

  • surfer directement sur le service fonctionne avec un navigateur Web
  • J'ai également essayé de m'ouvrir à HTTP, et cela a fonctionné
  • Le certificat utilisé par le service est auto-signé mais approuvé par ma machine via un certificat racine (aucun avertissement n’est émis dans IE ou Chrome).
  • J'ai effectué des captures réseau et vérifié que des paquets parviennent effectivement au serveur.

Toutes les suggestions appréciées!

Cordialement, Patrik

Message mis à jour concernant les suggestions de M. Tree ci-dessous:

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              :

Mise à jour 2 basée sur un commentaire de M. Tree:

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
13
PatrikJ

J'ai résolu le mystère tout en résolvant autre chose. Le serveur Web en question ne supportait que TLS1.1 et TLS1.2. Powershell ne semble pas soutenir cela. Si j'ai activé TLS1.0 cela a fonctionné. 

Pour forcer TLS1.2, vous pouvez utiliser cette ligne:

[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12

J'espère que cela aide quelqu'un d'autre et merci pour tous les commentaires utiles!

/ Patrik

34
PatrikJ

Récemment, j'ai beaucoup souffert pour surmonter une situation similaire. Je créais une preuve de concept à l'aide d'un service d'essai SaaS. Le service avait un certificat SSL auto-signé et je voulais donc ignorer les erreurs de certificat lorsque j'essayais d'y appeler une méthode POST (similaire au paramètre "-k" de curl). Après beaucoup de difficultés, j'ai découvert qu'il fallait les deux _ (a) l'appel pour ignorer les erreurs de validation du certificat et (b) un paramètre explicite pour TLS 1.2 en tant que protocole de sécurité. Je pense que ce dernier parce que le service était peut-être en train de réduire les tentatives de connexion en utilisant l'un des autres protocoles. (J'ai passé trop de temps à essayer les différentes variantes possibles, comme suggéré sur différents threads SOF mais indépendamment ...)

Voici le code qui a fonctionné ... 

Important: le contournement de la validation du certificat est purement destiné au prototype/PoC. Nous n’avons pas l’intention de le faire en production (et vous ne devriez pas non plus!). 

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

Vous souhaitez également ajouter que les protocoles de sécurité préférés continuent de changer à mesure que différentes vulnérabilités sont découvertes et que des correctifs sont mis en œuvre. De plus, différents systèmes (piles SSL/TLS dans les systèmes d'exploitation clients et les serveurs/services) ont souvent leur propre rattrapage à faire avec les options les plus récentes/les plus sécurisées. Ainsi, le drapeau qui fonctionnera exactement dépendra des systèmes client et serveur ainsi que du temps (en ce sens que TLS1.2 pourrait ne pas rester préféré quelques mois plus tard). Idéalement, il ne devrait pas être nécessaire de spécifier un drapeau. Veuillez consulter la section "Remarques" dans de ce document MSDN pour plus d'informations.

0
mprabhu11

Il semble que vous tentiez d’appeler une API json avec Invoke-RestMethod. Selon la documentation :

-ContentType

Spécifie le type de contenu de la demande Web.

Si ce paramètre est omis et que la méthode request est POST, Invoke-RestMethod définit le type de contenu sur "application/x-www-form-urlencoded". Sinon, le type de contenu est non spécifié dans l'appel.

Pour utiliser un corps json, vous devrez utiliser Invoke-RestMethod -ContentType 'application/json' <other args>

0
Eris