Manage credentials for Invoke-RestMethod request - powershell

I'm writing a PowerShell script to query an application API (Qualys), and the first step is to authenticate. I can only do it unsecurely:
[string]$username = "username"
[string]$password = "superpassword"
$hdrs= #{"X-Requested-With"="Powershell"}
$base = "https://application-base-url/api/2.0/fo"
$body= "action=login&username=$username&password=$password"
Invoke-RestMethod -SessionVariable sess -Headers $hdrs -URI "$base/session/" -Method POST -Body $body
It works, but as you can see, the username & password are stored in the file. I would like Powershell to prompt me the username and password, and use it to authenticate while securely manage them.
I tried to use Get-Credential but I couldn't make it work. Could anyone help?

Apparently you need to put the plain-text password in the body.
To retrieve that from a credential object you get from using Get-Credential, use:
$cred = Get-Credential
$username = $cred.UserName
$password = $cred.GetNetworkCredential().Password # --> the password as plain-text

Related

Encrypting Password parameters in Powershell for GET request to ServiceNow

has anyone found a good solution to encrypting the password parameters in Powershell for ServiceNow API?
This is the default version :
`$user = "admin"
$pass = "admin"
# Build auth header
$base64AuthInfo = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(("{0}:{1}" -f $user, $pass)))
# Set proper headers
$headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
$headers.Add('Authorization',('Basic {0}' -f $base64AuthInfo))
$headers.Add('Accept','application/json')
$response = Invoke-WebRequest -Header $headers -Method $method -Uri $uri
`
`Then I tried the most common way of encrypting the password using "ConvertTo-SecureString":
`
`# Convert the plain text password to an encrypted secure string
$securePassword = ConvertTo-SecureString '' -AsPlainText -Force
# Convert the encrypted password back to a plain text password
$pass = ConvertFrom-SecureString $securePassword
# Set the username
$username = "admin"
`
But this didnt work. Gave me an unauthorised(401) error.
One method I found that did worked was using CredentialManager but I am hesitant to use it because it is not that secure at all. Anyone here has a better solution? Let me know and thanks for the help

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

Normally for scheduled scripts I save a hash file to disk for the credentials to be used by the script as follows:
$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)
The following Oath token request works if the password element in the Body is entered in plain text, but it is not working if I use the variable $Credential.Password . Is there a way to get this to work, or secure the password otherwise?
The Error that the below token request generatedes:
Error: Invoke-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
##########
===========================
Updated script based on answer from thepip3r, and Microsoft support:
Password and secret are passed in plain text on the wire, but are not
exposed in the script, and have a degree of security saved as hash
files
Adjusted to not save the password or secret into variables to
increase security from attacks that can gain access to memory (MS
support recommended)
Option to use a certificate for the Azure Registered App rather than
an App Secret, to increase security on the wire
Alternative option is to use "Azure Automation", which allows to
run scripts from within O365, which should be much more secure.
Another possible alternative may be 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"
So... I'm going to provide this as an answer because I want people to understand this being an example the problem with storing a password in a file (even as a ciphertext string value) with Get-Credential.
#mbromb, this will give you a way to test whether or not the value you're retrieving is the proper value:
on your $Credential object (last line), run: $Credential.GetNetworkCredential().Password
This will be the PLAINTEXT value of whatever you put into the prompts with Get-Credential initially. So, you can verify if after getting it initially, writing it to a file, reading it back in, and converting it to a securestring object worked as intended.
To try and draw a more straight-line to this problem: if I find your 'admin.pwd' file, it's extremely trivial to produce the plaintext from it.
Caveat: You can secure this value by supplying a protected key for this encryption process using either the -Key or -SecureKey properties on the ConvertTo/From-SecureString cmdlets. Key takes a byte array (preferably cryptographically random with sufficient entropy for your needs) and SecureKey accepts a string (password) and generates the byte array from your password.
Caveat-to-the-caveat: If you're already trying to store the password to a file, password protecting the stored-password probably isn't the right answer...

how to authenticate Invoke-RestMethod to list artifactory repositories

Trying to get a list of Repositories from an Artifactory Enterprise v6 instance, using PowerShell 5.1 Invoke-RestMethod from Win10 desktop, but cannot see to get it to authenticate.
Seems simple enough, but this
$myCred = Get-Credential notStanley
$lstART = Invoke-RestMethod -URI https://<myserver>/artifactory/api/repositories -Credential $myCred
only returns the items that allow Anonymous access.
If I open a browser and logon to that Artifactory instance, I can then paste the above URI and get the full list of all repositories my account has access to.
Any hints what the $myCred is missing?
I have tried in the past with artifactory and -Credential doesnt really worked for me.
I tried the API way which is much simpler and easier to use.
Connecting to Artifactory with API Key
Read here to find out how you can get the API key for your account on artifactory.
$header = #{"X-JFrog-Art-Api" = "yourAPIKey"}
Invoke-RestMethod -URI https://<myserver>/artifactory/api/repositories -Headers $header
Using Basic Auth and -Credential
If you do want to work with the Get-Credential prompt, make sure you use the username that works in Artifactory. Its not the same as the domain\user. from here
$login = Get-Credential -Message "Enter Credentials for Artifactory"
#invalid creds.. but its ok. Need to tell invoke-restmethod to use Basic Auth.
$headers = #{ Authorization = "Basic Zm9vOmJhcg==" }
# use -Credential to override the credentials.
$new = Invoke-RestMethod -URI https://<server>/artifactory/api/repositories -Headers $headers -Credential $login
Thanks Jawad. That got me working with the API (my first try was not formed quite properly). Following your links found a couple other questions (27951561 and 60325084) that helped me get Credential as well. I have gone with Credential to avoid futzing with obfuscating an API Key in the source code.
My base skeleton now looks like:
# get standard PowerShell credential
$myCred = Get-Credential -Message "just <name> for this server, not '<domain>\<name>'"
# format credential for Artifactory API
$credUser = $myCred.UserName # extract user name
$credPswd = $myCred.GetNetworkCredential().password # extract user password
$credPair = "${credUser}:${credPswd}" # concatenate into BasicAuth format
$credBytes = [System.Text.Encoding]::ASCII.GetBytes($credPair) # convert byte values to text
$cred64 = [System.Convert]::ToBase64String($credBytes) # condense a bit more secure string RFC2045-MIME
$credAuth = "Basic $cred64" # intermediate formatting
$restHeaders = #{ Authorization = $credAuth } # initialize web headers
# clear collection array
$cfgSite = #()
# locate server
$lstURL "https://<myserver>/artifactory/api/repositories"
# get list of repositories
$theseRepo = Invoke-RestMethod -Headers $restHeaders -Uri $lstURL
# collect each configuration
ForEach ($thisRepo in $theseRepo)
{
$thisURI = $lstURL + $thisRepo.key
$thisCfg = Invoke-RestMethod -Headers $restHeaders -Uri $thisURI
$thisCfg | Add-Member -NotePropertyName "SITE" -NotePropertyValue "$thisSite"
$cfgSite += $thisCfg
}
# output to file
$cfgAll | Export-Csv .\lstArtRepoConf.csv -NoTypeInformation

powershell authentication to Bamboo error

I am having a problem updating the release label to show if it is broken or Approved etc. I have got this working through postman so I know that the problem is not through that. I am using basic authentication with my username and password being entered and trying to get access to the uri using them details but for the powershell it is not working. It works fine with Postman so it's not the credentials. Here is what I am doing:
$user = Read-Host -Prompt "Enter Bamboo Username"
$pass =  Read-Host -Prompt "Enter password" -AsSecureString
$secpass = ConvertTo-SecureString $user -AsPlainText -Force
$credential = New-Object System.Management.Automation.PSCredential($pass,$secpass)
$baseuri = "http://bamboo.uk.myNetwork.net/rest/api/latest/deploy/version/VersionNumber/status/Approved"
$Headers = #{
'X-Atlassian-Token' = 'nochecK'
};
$json = "application/json"
Invoke-RestMethod -Uri $baseuri -Method Post -Credential $credential -Headers $Headers -ContentType $json
This is not working and I am getting the 401client must be authenticated to access this resource. I have also tried all of the following ways to authenticate the user but none have worked:
<#
$encoded = [System.Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($user+":"+$pass))
$EncodedUsernamePassword = [System.Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($('{0}:{1}' -f $Credential.UserName, $Credential.GetNetworkCredential().Password)))
$pair = "$($user):$($pass)"
$encodedCreds = [System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes($pair))
$basicAuthValue = "Basic $encodedCreds"
#>
Postman with return the following details:
{
"id": theID,
"userName": "myUserName",
"displayName": "myName",
"creationDate": 1481536833693,
"versionState": "APPROVED"
}
This shows that the uri with basic authentication and adding the X-Atlassian-Token: nocheck works but for the powershell I am getting Authentication Errors why is this?
The problem was when I used the password as a SecureString type, when I changed it just a string it would work, so all of these ways including the one Hackerman linked worked when using the credentials without a SecureString type.

Basic authentication with the GitHub Api using PowerShell

I have been trying to do basic authentication with the GitHub Api with PowerShell. The following do not work:
> $cred = get-credential
# type username and password at prompt
> invoke-webrequest -uri https://api.github.com/user -credential $cred
Invoke-WebRequest : {
"message":"Requires authentication",
"documentation_url":"https://developer.github.com/v3"
}
How do we do basic authentication using PowerShell with the GitHub Api?
Basic auth basically expects that you send the credentials in an Authorization header in the following form:
'Basic [base64("username:password")]'
In PowerShell that would translate to something like:
function Get-BasicAuthCreds {
param([string]$Username,[string]$Password)
$AuthString = "{0}:{1}" -f $Username,$Password
$AuthBytes = [System.Text.Encoding]::Ascii.GetBytes($AuthString)
return [Convert]::ToBase64String($AuthBytes)
}
And now you can do:
$BasicCreds = Get-BasicAuthCreds -Username "Shaun" -Password "s3cr3t"
Invoke-WebRequest -Uri $GitHubUri -Headers #{"Authorization"="Basic $BasicCreds"}