R. Barrett
R. Barrett

Reputation: 859

How to fix security within WinSCP SFTP scripts in PowerShell with hard-coded passwords

So my organization has tasked me with cleaning up some of the security issues in regards to some automated scripts that have hard coded passwords within the scripts that are running as automated tasks. One such task contains SFTP scripts that export and import files to and from with the password, host name, credentials, port, and everything exposed within the script. As a result, I would like to first see about how to call such credentials within a separate file that can be hidden and two see about encryption and salting it later. But my main focus is getting them out of the script in case traffic is every intercepted. Here is what the PowerShell code looks like:

param (
    $localPath = 'E:\FTP\SchooLinks\course_requests.csv',
    $remotePath = '/schoolinks_exports/course_planning/course_requests.csv'
)

try
{
    # Load WinSCP .NET assembly
    Add-Type -Path "C:\Program Files (x86)\WinSCP\WinSCPnet.dll"

    # Setup session options
    $sessionOptions = New-Object WinSCP.SessionOptions -Property @{
        Protocol = [WinSCP.Protocol]::Sftp
        HostName = "<domain_name>"
        UserName = "<username>"
        Password = "<password>"
        SshHostKeyFingerprint = "<fingerprint>"
    }

    $session = New-Object WinSCP.Session

    try
    {
        # Connect
        $session.Open($sessionOptions)

        # Upload files
        $transferOptions = New-Object WinSCP.TransferOptions
        $transferOptions.TransferMode = [WinSCP.TransferMode]::Binary

        $transferResult =
            $session.GetFiles($remotePath, $localPath, $False, $transferOptions)

        # Throw on any error
        $transferResult.Check()

        # Print results
        foreach ($transfer in $transferResult.Transfers)
        {
            Write-Host "Download of $($transfer.FileName) succeeded"
        }
    }
    finally
    {
        # Disconnect, clean up
        $session.Dispose()
    }

    exit 0
}
catch
{
    Write-Host "Error: $($_.Exception.Message)"
    exit 1
}

Another one that we have looks like this:

param (
    $localPath = 'E:\FTP\TalentEd\SkywardApplicantExportSQL.txt',
    $remotePath = '/SkywardApplicantExportSQL.txt'
)

try
{
    # Load WinSCP .NET assembly
    Add-Type -Path "C:\Program Files (x86)\WinSCP\WinSCPnet.dll"

    # Setup session options
    $sessionOptions = New-Object WinSCP.SessionOptions -Property @{
        Protocol = [WinSCP.Protocol]::Sftp
        HostName = "<domain>"
        UserName = "<username>"
        Password = "<password>"
        SshHostKeyFingerprint = "<sha_fingerprint>"
    }

    $session = New-Object WinSCP.Session

    try
    {
        # Connect
        $session.Open($sessionOptions)

        # Upload files
        $transferOptions = New-Object WinSCP.TransferOptions
        $transferOptions.TransferMode = [WinSCP.TransferMode]::Binary

        $transferResult =
            $session.GetFiles($remotePath, $localPath, $False, $transferOptions)

        # Throw on any error
        $transferResult.Check()

        # Print results
        foreach ($transfer in $transferResult.Transfers)
        {
            Write-Host "Download of $($transfer.FileName) succeeded"
        }
    }
    finally
    {
        # Disconnect, clean up
        $session.Dispose()
    }

    exit 0
}
catch
{
    Write-Host "Error: $($_.Exception.Message)"
    exit 1
}

I am familiar with Python and json and calling stuff within a json file similar to the following:

import json
with open('secrets.json','r') as f:
      config = json.load(f)

and calling it with (config['object']['nested_element']) within the Python script. I would like to do something similar with PowerShell, however I have very limited knowledge to PowerShell.

Upvotes: 1

Views: 1361

Answers (2)

R. Barrett
R. Barrett

Reputation: 859

So many thanks to @postanote and @Martin Prikryl I was able to figure this out. You can basically use a config.xml file with contents similar to this:

<Configuration>
  <localPath>insert_local_file_path</localPath>
  <remotePath>insert_remote_file_path</remotePath>
  <Protocol>[WinSCP.Protocol]::Sftp</Protocol>
  <HostName>insert_hostname</HostName>
  <UserName>username</UserName>
  <Password>mypassword</Password>
  <SshHostKeyFingerPrint>fingerprint</SshHostKeyFingerPrint>
</Configuration>

From here you can use the following at the beginning of your template:

# Read XML configuration file
[xml]$config = Get-Content ".\config.xml"

param (
    $localPath = $config.Configuration.localPath
    $remotePath = $config.Configuration.remotePath
)

try
{
    # Load WinSCP .NET assembly
    Add-Type -Path "C:\Program Files (x86)\WinSCP\WinSCPnet.dll"

    # Setup session options
    $sessionOptions = New-Object WinSCP.SessionOptions -Property @{
        Protocol = $config.Configuration.Protocol
        HostName = $config.Configuration.HostName
        UserName = $config.Configuration.UserName
        Password = $config.Configuration.Password 
        SshHostKeyFingerprint = $config.Configuration.SshHostKeyFingerprint
    }

I have more SFTP templates here people can use at
https://github.com/Richard-Barrett/ITDataServicesInfra/tree/master/SFTP

Upvotes: 1

postanote
postanote

Reputation: 16086

Yeppers, of course, never store creds in clear text in files.

There are several ways to store credentials for use. Secure file (xml, etc..), the registry, or Windows Credential Manager and this is well documented on Microsoft sites, as well as in many articles all over the web and via Q&A's on StackOverflow.

Just search for 'securely store credentials PowerShell'

Sample results...

Working with Passwords, Secure Strings and Credentials in Windows PowerShell

How to run a PowerShell script against multiple Active Directory domains with different credentials

Accessing Windows Credentials Manager from PowerShell

Save Encrypted Passwords to Registry for PowerShell

...and/or the modules via the MS powershellgallery.com directly installable from your PowerShell environments.

Find-Module -Name '*cred*' | 
Format-Table -AutoSize
<#
# Results

Version        Name                            Repository Description                                                                                          
-------        ----                            ---------- -----------                                                                                          
2.0            CredentialManager               PSGallery  Provides access to credentials in the Windows Credential Manager                                     
2.0.168        VcRedist                        PSGallery  A module for lifecycle management of the Microsoft Visual C++ Redistributables. Downloads the supp...
1.3.0.0        xCredSSP                        PSGallery  Module with DSC Resources for WSMan CredSSP.                                                         
1.1            VPNCredentialsHelper            PSGallery  A simple module to set the username and password for a VPN connection through PowerShell. Huge tha...
1.0.11         pscredentialmanager             PSGallery  This module allows management and automation of Windows cached credentials.                          
4.5            BetterCredentials               PSGallery  A (compatible) major upgrade for Get-Credential, including support for storing credentials in Wind...
1.0.4          WindowsCredential               PSGallery  Management module for Windows Credential Store.                                                      
... 
#>

Upvotes: 2

Related Questions