Security plays two important roles in PowerShell. The first role is the security of PowerShell itself. Scripting languages have long been a vehicle of email-based malware on Windows, so PowerShell's security features have been carefully designed to thwart this danger. The second role is the set of security-related tasks you are likely to encounter when working with your computer: script signing, certificates, and credentials, just to name a few.
When it comes to talking about security in the scripting and command-line world, a great deal of folklore and superstition clouds the picture. One of the most common misconceptions is that scripting languages and command-line shells somehow lets users bypass the security protections of the Windows graphical user interface.
somehow lets users --> somehow let users
The Windows security model protects resources—not the way you get to them. That is because programs that you run, in effect, are you. If you can do it, so can a program. If a program can do it, then you can do it without having to use that program. For example, consider the act of changing critical data in the Windows Registry. If you use the Windows Registry Editor graphical user interface, it provides an error message when you attempt to perform an operation that you do not have permission for, as shown in Figure 18.1, “Error message from the Windows Registry Editor”.
The Registry Editor provides this error message because it is unable to delete that key, not because it wanted to prevent you from doing it. Windows itself protects the registry keys, not the programs you use to access them.
Figure 18.1. Error message from the Windows Registry Editor

Likewise, PowerShell provides an error message when you attempt to perform an operation that you do not have permission for. Not because PowerShell contains extra security checks for that operation, but because it is also simply unable to perform the operation:
PS > New-Item "HKLM:\Software\Microsoft\Windows\CurrentVersion\Run\New" New-Item : Requested registry access is not allowed. At line:1 char:9 + New-Item <<<< "HKLM:\Software\Microsoft\Windows\CurrentVersion\Run\New"
While perhaps clear after explanation, this misunderstanding often gets used as a reason to prevent users from running command shells or scripting languages altogether.
PowerShell provides an error message when you try to run a script:
PS > .\Test.ps1 File C:\temp\test.ps1 cannot be loaded because the execution of scripts is disa bled on this system. Please see "get-help about_signing" for more details. At line:1 char:10 + .\Test.ps1 <<<<
Or, if you try to import a module:
PS> Import-Module PSDiagnostics
Import-Module : File C:Windowssystem32WindowsPowerShellv1.0Modules PSDiagnosticsPSDiagnostics.psm1 cannot be loaded because the execution of scripts is disabled on this system. Please see "get-help about_signing" for more details.
At line:1 char:14 + import-module <<<< PSDiagnostics
To prevent this error message, use the
Set-ExecutionPolicy cmdlet to change
the PowerShell execution policy to one of the policies that allow
scripts to run:
Set-ExecutionPolicy RemoteSignedAs normally configured, PowerShell operates strictly as an interactive shell. By disabling the execution of scripts by default, PowerShell prevents malicious PowerShell scripts from affecting users who have PowerShell installed, but who may never have used (or even heard of!) PowerShell.
You (as a reader of this book and PowerShell user) are not part of that target audience. You will want to configure PowerShell to run under one of the following five execution policies:
RestrictedPowerShell operates as an interactive shell only. Attempting to run a script generates an error message. This is PowerShell's default execution policy.
AllSignedPowerShell only runs scripts that contain a digital signature. When you attempt to run a script signed by a publisher that PowerShell hasn't seen before, PowerShell asks whether you trust that publisher to run scripts on your system.
RemoteSigned
(recommended)PowerShell runs most scripts without
prompting, but requires that scripts that originate from the
Internet contain a digital signature. As in AllSigned mode, PowerShell asks whether
you trust that publisher to run scripts on your system when you
run a script signed by a publisher it hasn't seen before.
PowerShell considers a script to have come from the Internet when
it has been downloaded to your computer by a popular
communications programs such as Internet Explorer, Outlook, or
Messenger.
UnrestrictedPowerShell does not require a digital signature on any script, but (like Windows Explorer) warns you when a script originates from the Internet.
BypassPowerShell places the responsibility of security validation entirely upon the user.
When it comes to evaluating script signatures, always remember that a signed script does not mean a safe script! The signature on a script gives you a way to verify who the script came from, but not that you can trust its author to run commands on your system. You need to make that decision for yourself, which is why PowerShell asks you.
What about the Undefined value? ("Removes the currently assigned execution policy from the current scope. This parameter will not remove an execution policy that is set in a Group Policy scope.") Maybe a new recipe for a problem "How can I remove the currently assigned execution policy?"?
Run the Set-ExecutionPolicy cmdlet to configure the
system's execution policy. It supports three scopes:
ProcessImpacts the current session, and any
that it launches. This scope modifies the
PSExecutionPolicy environment variable, and is
also supported through the -ExecutionPolicy
parameter to PowerShell.exe.
CurrentUserModifies the execution policy for the
current user, and stores its value in the
HKEY_CURRENT_USER hive of the Windows
registry.
LocalMachineModifies the execution policy for the
entire machine, and stores its value in the
HKEY_LOCAL_MACHINE hive of the Windows
registry. Modifying the execution policy at this scope requires
that you launch PowerShell with Administrator privileges. If you
want to configure your execution policy on Windows Vista or later,
right-click the Windows PowerShell link for the option to launch
PowerShell as Administrator.
If you specify the value of
Undefined for the execution policy at a specific
scope, PowerShell removes any execution policy you previously defined
for that scope.
enviornment variable --> environment variable
64-bit OS has 32-bit version of the PowerShell too, and 32-bit version uses different registry path to store the value of ExecutionPolicy—SOFTWAREWow6432NodeMicrosoftPowerShell1ShellIdsMicrosoft.PowerShell. It might be a good idea to mention that. People are usually confused that they need to set up execution policy for both versions, if they want to run scripts in 32-bit PowerShell as well.
Alternatively, you may directly modify the registry key that
PowerShell uses to store its execution policy. For the
Currentuser and LocalMachine
scopes, this is the ExecutionPolicy
property under the registry path SOFTWARE\Microsoft\PowerShell\1\ShellIds\Microsoft.PowerShell.
In an enterprise setting, PowerShell also lets you override this local preference through Group Policy. For more information about PowerShell's Group Policy support, see the section called “Manage PowerShell Security in an Enterprise”.
It is easy to understand the power of an
execution policy to prevent scripts from running, but administrators
often forget to consider from whom. They might
think that enforcing an AllSigned policy is a way
to prevent the user from running non-approved
applications, when it is designed as a way to prevent the
attacker from running scripts that the user
doesn't approve. This misconception is often wrongly reinforced by the
location of the ExecutionPolicy configuration key
in PowerShell version one – in a registry location that only machine
administrators have access to.
System-wide PowerShell execution policies cannot prevent the user from doing something they want to do. That job is left to the Windows Account Model, which is designed as a security boundary. It controls what a user can do: what files they can access, what registry keys they can access, and more. PowerShell is a user-mode application, and is therefore (by the Windows security model) completely under the user's control.
Instead, Execution Policies are a
user-focused feature like seatbelts or helmets. It's best to keep them
on, but you always have the option to take them off. PowerShell's
installer sets the execution policy to Restricted
as a safe default for the vast majority of Windows users that will
never run a PowerShell script in their life. A system administrator
might set the execution policy to AllSigned because
they want to define it as a best practice, or let non-technical users
run a subset of safe scripts.
At any time, the user can decide otherwise. They can type the commands by hand, paste the script into their PowerShell prompt, or any of a countless number of other work arounds. These are all direct results of one of Windows' core security principles: you have complete control over any application you are running. PowerShell version two makes this reality much more transparent through its fine-grained execution policy scopes.
Windows' core security tenet --> "Windows' core security principle", maybe?
At its core, execution policy scopes let
administrators and users tailor their safety harness. Jane might be
fluent and technical (and opt for a RemoteSigned
execution policy), while Bob (another user of the same machine with
different security preferences) can still get the benefits of an
AllSigned default execution policy. In addition,
agents or automation tools can invoke PowerShell commands without
having to modify the permanent state of the system.
the section called “Manage PowerShell Security in an Enterprise”
I would like to see here a link to an online version of the "about_Execution_Policies" help topic.
PowerShell warns you when it tries to load a script from an Intranet (UNC) path.
Enable Internet Explorer's
UncAsIntranet setting, or add the UNC path to the
list of trusted sites. Example 18.1, “Adding a server to the list of trusted hosts” adds
server to the list of trusted sites.
IMO, "the Trusted Sites zone" is more appropriate term here than "the list of trusted hosts" to avoid a confusion with the TrustedHosts list setting in WS-Management.
Example 18.1. Adding a server to the list of trusted hosts
$path = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Internet Settings\" +
"ZoneMap\Domains\server"
New-Item -Path $path | New-ItemProperty -Name File -PropertyType DWORD -Value 2When using an execution policy that detects Internet-based scripts, you may want to stop PowerShell from treating those scripts as remote.
In an enterprise setting, PowerShell sometimes warns of the dangers of Internet-based scripts even if they are located only on a network share. To remove this warning, first, ensure they have not actually been downloaded from the Internet. Right-click on the file from Windows Explorer, select Properties, and then click Unblock.
the internet --> the Internet
If unblocking the file does not resolve the issue (or is not an option), your machine has likely been configured to restrict access to network shares. This is common with Internet Explorer's Enhanced Security Configuration mode. To prevent this message, add the path of the network share to Internet Explorer's Intranet or Trusted Sites zone. For more information on managing Internet Explorer's zone mappings, see the section called “Add a Site to an Internet Explorer Security Zone”.
If you are using an
Unrestricted execution policy and still want to get
rid of this warning for remote files, you can use the
Bypass execution policy to bypass PowerShell's
security features entirely. For more information about execution
policies, see the section called “Enable Scripting Through an Execution Policy”.
You want to sign a PowerShell script, module, or formatting file so that it may be run on systems that have their execution policy set to require signed scripts.
IMO, "Module" is missing in a title.
To sign the script with your standard
code-signing certificate, use the Set-AuthenticodeSignature cmdlet:
What is correct? "code-signing" or "code signing"? This page contains both ("code-signing" (10 times) and "code signing" (6 times)).
I've changed it to be two "code-signing certificate", although there are instances where it isn't hyphenated due to a different context (such as "certificates that support code signing".
$cert = @(Get-ChildItem cert:\CurrentUser\My -CodeSigning)[0]
Set-AuthenticodeSignature file.ps1 $cert-CodeSigning --> -CodeSigningCert
Alternatively, you may also use other
traditional applications (such as signtool.exe) to sign PowerShell
.ps1, .psm1,
.psd1, and .ps1xml
files.
Alternatively,you --> Alternatively, you
.psm1 files should be mentioned too. Module files need to be signed, if we want to import them on systems that have their execution policy set to require signed scripts.
Signing a script or formatting file provides you and your customers with two primary benefits: publisher identification and file integrity. When you sign a script, module, or formatting file, PowerShell appends your digital signature to the end of that file. This signature verifies that the file came from you and also ensures that nobody can tamper with the content in the file without detection. If you try to load a file that has been tampered with, PowerShell provides the following error message:
IMO, every time when you talk about "signing a script and formatting file", module need to be added too-"signing a script, module and formatting file".
File C:\temp\test.ps1 cannot be loaded. The contents of file C:\temp\test.ps1 may have been tampered because the hash of the file does not match the hash stored in the digital signature. The script will not execute on the system. Please see "get-help about_signing" for more details. At line:1 char:10 + .\test.ps1 <<<<
When it comes to the signing of scripts,
modules, and formatting files, PowerShell participates in the standard
Windows Authenticode infrastructure. Because of that, techniques you may
already know for signing files and working with their signatures
continue to work with PowerShell scripts and formatting files. While the
Set-AuthenticodeSignature cmdlet is
primarily designed to support scripts and formatting files, it also
supports DLLs and other standard Windows executable file types.
To sign a file, the Set-AuthenticodeSignature cmdlet requires that
you provide it with a valid code-signing certificate. Most certification
authorities provide Authenticode code-signing certificates for a fee. By
using an Authenticode code-signing certificate from a reputable
certification authority (such as VeriSign or Thawte), you can be sure
that all users will be able to verify the signature on your script. Some
online services offer extremely cheap code-signing certificates, but be
aware that many machines may be unable to verify the digital signatures
created by those certificates.
You can still gain many of the benefits of code signing on your own computers by generating your own code-signing certificate. While other computers will not be able to recognize the signature, it still provides tamper-protection on your own computer. For more information about this approach, see the section called “Program: Create a Self-Signed Certificate”.
of code signing on --> of code-signing on
The –TimeStampServer parameter lets you sign your
script or formatting file in a way that makes the signature on your
script or formatting file valid even after your codesigning certificate
expires.
For more information about the Set-AuthenticodeSignature cmdlet, type
Get-Help
Set-AuthenticodeSignature.
cmdlet,type --> cmdlet, type
It is possible to benefit from the tamper-protection features of signed scripts without having to pay for an official code-signing certificate. You do this by creating a self-signed certificate. Scripts signed with a self-signed certificate will not be recognized as valid on other computers, but you can still sign scripts on your own computer.
When Example 18.2, “New-SelfSignedCertificate.ps1” runs, it prompts you for a password. Windows uses this password to prevent malicious programs from automatically signing files on your behalf.
Example 18.2. New-SelfSignedCertificate.ps1
##############################################################################
if(-not (Get-Command makecert.exe -ErrorAction SilentlyContinue))
{
$errorMessage = "Could not find makecert.exe. " +
"This tool is available as part of Visual Studio, or the Windows SDK."
Write-Error $errorMessage
return
}
$keyPath = Join-Path ([IO.Path]::GetTempPath()) "root.pvk"
makecert -n "CN=PowerShell Local Certificate Root" -a sha1 `
-eku 1.3.6.1.5.5.7.3.3 -r -sv $keyPath root.cer `
-ss Root -sr localMachine
makecert -pe -n "CN=PowerShell User" -ss MY -a sha1 `
-eku 1.3.6.1.5.5.7.3.3 -iv $keyPath -ic root.cer
Remove-Item $keyPath
Get-ChildItem cert:\currentuser\my -codesign |
Where-Object { $_.Subject -match "PowerShell User" }
makecert.exe is also included in the Microsoft .NET Framework SDK.
For more information about running scripts, see the section called “Run Programs, Scripts, and Existing Tools”.
You want to control PowerShell's security features in an enterprise setting.
To manage PowerShell's security features enterprise-wide:
enterprisewide --> enterprise-wide
Apply PowerShell's Group Policy templates to control PowerShell's execution policy through Group Policy.
Deploy Microsoft Certificate Services to automatically generate Authenticode code-signing certificates for domain accounts.
The administrative templates for Windows PowerShell let you override the machine's local execution policy preference at both the machine and per-user level. To obtain the PowerShell administrative templates, visit http://www.microsoft.com/downloads and search for "Administrative templates for Windows PowerShell."
Although Group Policy settings override local preferences, PowerShell's execution policy should not be considered a security measure that protects the system from the user. It is a security measure that helps prevent untrusted scripts from running on the system. As mentioned in the section called “Enable Scripting Through an Execution Policy”, PowerShell is only a vehicle that allows users to do what they already have the Windows permissions to do.
Once you install the administrative templates for Windows PowerShell, launch the Group Policy Object Editor MMC snapin. Right-click Administrative Templates and then select Add/Remove Administrative Templates. You will find the administrative template in the installation location you chose when you installed the administrative templates for Windows PowerShell. Once added, the Group Policy Editor MMC snapin provides PowerShell as option under its Administrative Templates node, as shown in Figure 18.2, “PowerShell Group Policy configuration”.
Figure 18.2. PowerShell Group Policy configuration

The default state is Not Configured. In this state, PowerShell takes its execution policy from the machine's local preference (as described in the section called “Enable Scripting Through an Execution Policy”. If you change the state to one of the Enabled options (or Disabled), PowerShell uses this configuration instead of the machine's local preference.
PowerShell respects these Group Policy settings no matter what. This includes settings that the machine's administrator may consider to reduce security—such as an Unrestricted group policy overriding an AllSigned local preference.
Per-user Group Policy settings override the machine's local preference, while per-machine Group Policy settings override per-user settings.
Although outside the scope of this book, Microsoft Certificate Services lets you automatically deploy code-signing certificates to any or all domain users. This provides a significant benefit, as it helps protect users from accidental or malicious script tampering.
For an introduction to this topic, visit http://technet.microsoft.com and search for "Enterprise Design for Certificate Services." For more information about script signing, see the section called “Sign a PowerShell Script, Module, or Formatting File”.
In addition to PowerShell's execution policy, you want to block scripts by their publisher, location, or similarity to a specific script.
Create new Software Restriction Policy rules to enforce these requirements.
While not common, you may sometimes want to
prevent PowerShell from running scripts signed by specific publishers,
from a certain path, or with specific content. For all execution
policies except for Bypass, PowerShell lets you
configure this through the computer's software restriction
policies.
To configure these software restriction policies, launch the Local Security Policy MMC snapin listed in the Administrative Tools group of the Start menu. Expand the Software Restriction Policies node, right-click Additional Rules, and then create the desired rules: certificate rules, path rule, or hash rules.
In Windows 7, the PowerShell module for
the AppLocker feature makes managing software restriction policies
immensely easier. For more information, search the Internet for
Applocker PowerShell.
Certificate rules let you configure certain certificates that PowerShell will never trust. Path rules let you define system paths that allow or disallow execution of PowerShell scripts from certain paths. Hash rules let you block specific scripts from execution if they are the same as the script you used to generate the rule.
Figure 18.3, “Adding a new certificate rule” demonstrates how to add a new Certificate Rule.
Figure 18.3. Adding a new certificate rule

Browse to the certificate that represents the publisher you want to block, and then click OK to block that publisher.
Rather than block specific certificates, you can also create certificate policy that allows only certificates from a centrally administered whitelist. To do this, select either Allow only all administrators to manage Trusted Publishers or Allow only enterprise administrators to manage Trusted Publishers from the Trusted Publishers Management dialog.
You want to verify the digital signature of a PowerShell script or formatting file.
To validate the signature of a script or
formatting file, use the Get-AuthenticodeSignature cmdlet:
PS > Get-AuthenticodeSignature .\test.ps1
Directory: C:\temp
SignerCertificate Status Path
----------------- ------ ----
FD48FAA9281A657DBD089B5A008FAFE61D3B32FD Valid test.ps1The Get-AuthenticodeSignature cmdlet gets the
Authenticode signature from a file. This can be a PowerShell script or
formatting file, but the cmdlet also supports DLLs and other Windows
standard executable file types.
By default, PowerShell displays the signature
in a format that summarizes the certificate and its status. For more
information about the signature, use the Format-List cmdlet, as shown in Example 18.3, “PowerShell displaying detailed information about an
Authenticode signature”.
Example 18.3. PowerShell displaying detailed information about an Authenticode signature
PS > Get-AuthenticodeSignature .\test.ps1 | Format-List
SignerCertificate : [Subject]
CN=PowerShell User
[Issuer]
CN=PowerShell Local Certificate Root
[Serial Number]
454D75B8A18FBDB445D8FCEC4942085C
[Not Before]
4/22/2007 12:32:37 AM
[Not After]
12/31/2039 3:59:59 PM
[Thumbprint]
FD48FAA9281A657DBD089B5A008FAFE61D3B32FD
TimeStamperCertificate :
Status : Valid
StatusMessage : Signature verified.
Path : C:\temp\test.ps1For more information about the Get-AuthenticodeSignature cmdlet, type
Get-Help
Get-AuthenticodeSignature.
You want to request sensitive information from the user, but want to do this as securely as possible.
To securely handle sensitive information,
store it in a SecureString whenever
possible. The Read-Host cmdlet (with
the –AsSecureString parameter) lets
you prompt the user for (and handle) sensitive information by returning
the user's response as a SecureString:
PS > $secureInput = Read-Host -AsSecureString "Enter your private key" Enter your private key: PS > $secureInput System.Security.SecureString
When you use any string in the .NET Framework
(and therefore PowerShell), it retains that string so that it can
efficiently reuse it later. Unlike most .NET data, unused strings
persist even after you finish using them. When this data is in memory,
there is always the chance that it could get captured in a crash dump,
or swapped to disk in a paging operation. Because some data (such as
passwords and other confidential information) may be sensitive, the .NET
Framework includes the SecureString
class: a container for text data that the framework encrypts when it
stores it in memory. Code that needs to interact with the plain-text
data inside a SecureString does so as
securely as possible.
PowerShell),it -- > PowerShell) ,it
Sorry.
PowerShell),it -- > PowerShell), it
When a cmdlet author asks you for sensitive
data (for example, an encryption key), the best practice is to designate
that parameter as a SecureString to
help keep your information confidential. You can provide the parameter
with a SecureString variable as
input, or the host prompts you for the SecureString if you do not provide one.
PowerShell also supports two cmdlets (ConvertTo-SecureString and ConvertFrom-SecureString) that let you
securely persist this data to disk. For more information about securely
storing information on disk, see the section called “Securely Store Credentials on Disk”.
Credentials are a common source of sensitive information. See the section called “Securely Request Usernames and Passwords” for information on how to securely manage credentials in PowerShell.
By default, the SecureString cmdlets use Windows'
Data Protection API (DPAPI) when they convert your
SecureString to and from its text
representation. The key it uses to encrypt your data is based on your
Windows logon credentials, so only you can decrypt the data that you've
encrypted. If you want the exported data to work on another system or
separate user account, you can use the cmdlet options that let you
provide an explicit key. PowerShell treats this sensitive data as an
opaque blob—and so should you.
However, there are many instances when you may
want to automatically provide the SecureString input to a cmdlet rather than
have the host prompt you for it. In these situations, the ideal solution
is to use the ConvertTo-SecureString
cmdlet to import a previously exported SecureString from disk. This retains the
confidentiality of your data and still lets you automate the
input.
If the data is highly dynamic (for example,
coming from a CSV), then the ConvertTo-SecureString cmdlet supports an
–AsPlainText parameter:
CSV),then --> CSV), then
$secureString = ConvertTo-SecureString "Kinda Secret" -AsPlainText-Force
Since you've already provided plain-text input
in this case, placing this data in a SecureString no longer provides a security
benefit. To prevent a false sense of security, the cmdlet requires the
-Force parameter to convert
plain-text data into a SecureString.
Once you have data in a SecureString, you may want to access its
plain-text representation. PowerShell doesn't provide a direct way to do
this, as that defeats the purpose of a SecureString. If you still want to convert a
SecureString to plain text, you have
two options:
doest't provide --> doesn't provide
Use the GetNetworkCredential() method of the
PsCredential class
$secureString = Read-Host -AsSecureString
$temporaryCredential = New-Object `
System.Management.Automation.PsCredential "TempUser",$secureString
$unsecureString = $temporaryCredential.GetNetworkCredential().PasswordUse the .NET Framework's Marshal class
$secureString = Read-Host -AsSecureString
$unsecureString = [Runtime.InteropServices.Marshal]::PtrToStringAuto(
[Runtime.InteropServices.Marshal]::SecureStringToBSTR($secureString))Your script requires that users provide it with a username and password, but you want to do this as securely as possible.
To request a credential from the user, use the
Get-Credential cmdlet:
$credential = Get-Credential
The Get-Credential cmdlet reads credentials from
the user as securely as possible and ensures that the user's password
remains highly protected the entire time. For an example of using the
Get-Credential cmdlet effectively in
a script, see the section called “Program: Start a Process As Another User”.
Once you have the username and password, you
can pass that information around to any other command that accepts a
PowerShell credential object without worrying about disclosing sensitive
information. If a command doesn't accept a PowerShell credential object
(but does support a SecureString for
its sensitive information), the resulting PsCredential object provides a Username property that returns the username in
the credential and a Password
property that returns a SecureString
containing the user's password.
Unfortunately, not everything that requires
credentials can accept either a PowerShell credential or SecureString. If you need to provide a
credential to one of these commands or API calls, the PsCredential object provides a GetNetworkCredential() method to convert the
PowerShell credential to a less secure NetworkCredential object. Once you've
converted the credential to a NetworkCredential, the UserName and Password properties provide unencrypted access
to the username and password from the original credential. Many
network-related classes in the .NET Framework support the NetworkCredential class directly.
The NetworkCredential class is less secure than
the PsCredential class because it
stores the user's password in plain text. For more information about
the security implications of storing sensitive information in plain
text, see the section called “Securely Handle Sensitive Information”.
If a frequently run script requires
credentials, you might consider caching those credentials in memory to
improve the usability of that script. For example, in the region of the
script that calls the Get-Credential
cmdlet, you can instead use the techniques shown by Example 18.4, “Caching credentials in memory to improve usability”.
Example 18.4. Caching credentials in memory to improve usability
$credential = $null
if(Test-Path Variable:\Lee.Holmes.CommonScript.CachedCredential)
{
$credential = ${GLOBAL:Lee.Holmes.CommonScript.CachedCredential}
}
${GLOBAL:Lee.Holmes.CommonScript.CachedCredential} =
Get-Credential $credential
$credential = ${GLOBAL:Lee.Holmes.CommonScript.CachedCredential}The script prompts the user for their credentials the first time they call it but uses the cached credentials for subsequent calls. If your command is part of a PowerShell module, you can avoid storing the information in a global variable. For more information about this technique, see the section called “Write Commands that Maintain State”.
To cache these credentials on disk (to support un-attended operation), see the section called “Securely Store Credentials on Disk”.
For more information about the Get-Credential cmdlet, type Get-Help Get-Credential.
If your script requires user credentials,
PowerShell offers the PsCredential
object. This lets you securely store those credentials, or pass them to
other commands that accept PowerShell credentials. When you write a
script that accepts credentials, consider letting the user to supply
either a username or a preexisting credential. This is the model
followed by the Get-Credential cmdlet, and provides
an intuitive user experience. Example 18.5, “Start-ProcessAsUser.ps1”
demonstrates a useful approach to support this model. As the framework
for this demonstration, the script lets you start a process as another
user. While this scenario addressed by this specific script is fully
handled by the Start-Process cmdlet, it provides a
useful framework for discussion.
Example 18.5. Start-ProcessAsUser.ps1
##############################################################################
param(
$credential = (Get-Credential),
[string] $process = $(throw "Please specify a process to start."),
[string] $arguments = ""
)
if($credential -is "String")
{
$credential = Get-Credential $credential
}
if(-not ($credential -is "System.Management.Automation.PsCredential"))
{
return
}
$startInfo = New-Object Diagnostics.ProcessStartInfo
$startInfo.Filename = $process
$startInfo.Arguments = $arguments
if(($credential.Username -eq "$ENV:Username") -or
($credential.Username -eq "\$ENV:Username"))
{
$startInfo.Verb = "runas"
}
else
{
$startInfo.UserName = $credential.Username
$startInfo.Password = $credential.Password
$startInfo.UseShellExecute = $false
}
[Diagnostics.Process]::Start($startInfo)
For a version of this script that lets you invoke PowerShell commands in an elevated session and easily interact with the results, see the section called “Program: Run a Temporarily Elevated Command”.
For more information about running scripts, see the section called “Run Programs, Scripts, and Existing Tools”.
One popular feature of many Unix-like
operating systems is the sudo command: a feature that
lets you invoke commands as another user without switching
context.
This is a common desire in Windows Vista and above, where User Access Control (UAC) means that most interactive sessions do not have their Administrator privileges enabled. Enabling these privileges is often a clumsy task, requiring that you launch a new instance of PowerShell with the "Run as Administrator" option enabled.
Example 18.6, “Invoke-ElevatedCommand.ps1” resolves many of these issues by launching an administrative shell for you, and letting it participate in a regular (non-elevated) PowerShell pipeline.
To do this, it first streams all of your input into a richly-structured CliXml file on disk. It invokes the elevated command, and stores its results into another richly-structure CliXml file on disk. Finally, it imports the structured data from disk, and removes the temporary files.
Example 18.6. Invoke-ElevatedCommand.ps1
##############################################################################
<#
.SYNOPSIS
Runs the provided script block under an elevated instance of PowerShell as
through it were a member of a regular pipeline.
.EXAMPLE
PS >Get-Process | Invoke-ElevatedCommand.ps1 {
$input | Where-Object { $_.Handles -gt 500 } } | Sort Handles
#>
param(
[Parameter(Mandatory = $true)]
[ScriptBlock] $Scriptblock,
[Parameter(ValueFromPipeline = $true)]
$InputObject,
[switch] $EnableProfile
)
begin
{
$inputItems = New-Object System.Collections.ArrayList
}
process
{
$null = $inputItems.Add($inputObject)
}
end
{
$outputFile = [IO.Path]::GetTempFileName()
$inputFile = [IO.Path]::GetTempFileName()
$inputItems.ToArray() | Export-CliXml -Depth 1 $inputFile
$commandLine = ""
if(-not $EnableProfile) { $commandLine += "-NoProfile " }
$commandString = "Set-Location '$($pwd.Path)'; " +
"`$output = Import-CliXml '$inputFile' | " +
"& {" + $scriptblock.ToString() + "} 2>&1; " +
"Export-CliXml -Depth 1 -In `$output '$outputFile'"
$commandBytes = [System.Text.Encoding]::Unicode.GetBytes($commandString)
$encodedCommand = [Convert]::ToBase64String($commandBytes)
$commandLine += "-EncodedCommand $encodedCommand"
$process = Start-Process -FilePath (Get-Command powershell).Definition `
-ArgumentList $commandLine -Verb RunAs `
-WindowStyle Hidden `
-Passthru
$process.WaitForExit()
if((Get-Item $outputFile).Length -gt 0)
{
Import-CliXml $outputFile
}
Remove-Item $outputFile
Remove-Item $inputFile
}
For more information about the CliXml commands, see the section called “Easily Import and Export Your Structured Data”. For more information about running scripts, see the section called “Run Programs, Scripts, and Existing Tools”.
Your script performs an operation that requires credentials, but you don't want it to require user interaction when it runs.
To securely store the credential's password to
disk so that your script can load it automatically, use the ConvertFrom-SecureString and ConvertTo-SecureString cmdlets.
The first step for storing a password on
disk is usually a manual one. Given a credential that you've stored in
the $credential variable, you can
safely export its password to
<currentScript>.ps1.credential using the
following command. Replace
<CurrentScript> with the name of the
script that will be loading it: a useful convention, but not
required.
PS > $credPath = Join-Path (Split-Path $profile) <CurrentScript>.ps1.credential
PS > $credential.Password | ConvertFrom-SecureString | Set-Content $credPathIn the script that you want to run automatically, add the following commands:
$credPath = Join-Path (Split-Path $profile)<CurrentScript>.ps1.credential $password = Get-Content $credPath | ConvertTo-SecureString $credential = New-Object System.Management.Automation.PsCredential `"CachedUser",$password
These commands create a new credential
object (for the CachedUser user) and store
that object in the $credential
variable.
When reading the solution, you might at first
be wary of storing a password on disk. While it is natural (and prudent)
to be cautious of littering your hard drive with sensitive information,
the ConvertFrom-SecureString cmdlet
encrypts this data using Windows' standard Data Protection
API. This ensures that only your user account can properly
decrypt its contents.
While keeping a password secure is an
important security feature, you may sometimes want to store a password
(or other sensitive information) on disk so that other accounts have
access to it anyway. This is often the case with scripts run by service
accounts or scripts designed to be transferred between computers. The
ConvertFrom-SecureString and ConvertTo-SecureString cmdlets support this by
letting you to specify an encryption key.
When used with a hard coded encryption key, this technique no longer acts as a security measure. If a user can access to the content of your automated script, they have access to the encryption key. If the user has access to the encryption key, they have access to the data you were trying to protect.
Although the solution stores the password in the directory that contains your profile, you could also load it from the same location as your script. To learn how to load it from the same location as your script, see the section called “Find Your Script's Location”.
For more information about the ConvertTo-SecureString and ConvertFrom-SecureString cmdlets, type
Get-Help ConvertTo-SecureString
or Get-Help
ConvertFrom-SecureString.
You want to retrieve information about certificates for the current user or local machine.
To browse and retrieve certificates on the local machine, use PowerShell's certificate drive. This drive is created by the certificate provider, as shown in Example 18.7, “Exploring certificates in the certificate provider”.
Example 18.7. Exploring certificates in the certificate provider
PS > Set-Location cert:\CurrentUserPS > $cert = Get-ChildItem -Rec -CodeSign
PS > $cert | Format-List
Subject : CN=PowerShell User
Issuer : CN=PowerShell Local Certificate Root
Thumbprint : FD48FAA9281A657DBD089B5A008FAFE61D3B32FD
FriendlyName :
NotBefore : 4/22/2007 12:32:37 AM
NotAfter : 12/31/2039 3:59:59 PM
Extensions : {System.Security.Cryptography.Oid, System.Security.Cryptogr
aphy.Oid}This is probably just an error on this page, and in a manuscript these commands are in two lines:
PS > Set-Location cert:CurrentUser
PS > $cert = Get-ChildItem -Rec -CodeSign
The full name of a parameter is preferable: -Recurse and -CodeSigningCert.
Yes, it's caused by the feedback system treating the back-slashes in the path as escape characters. It's correct in the manuscript.
The certificate drive provides a useful way to navigate and view certificates for the current user or local machine. For example, if your execution policy requires the use of digital signatures, the following command tells you which publishers are trusted to run scripts on your system:
Get-ChildItem cert:\CurrentUser\TrustedPublisher
The certificate provider is probably most
commonly used to select a code-signing certificate for the Set-AuthenticodeSignature cmdlet. The
following command selects the "best" code-signing certificate: the one
that expires last.
$certificates = Get-ChildItem Cert:\CurrentUser\My -CodeSign $signingCert = @($certificates | Sort -Desc NotAfter)[0]
-CodeSign --> -CodeSigningCert
-Desc --> -Descending
In this -CodeSign parameter lets you search for
certificates in the certificate store that support code signing. To
search for certificates used for other purposes, see the section called “Program: Search the Certificate Store”.
-CodeSign --> -CodeSigningCert
Although the certificate provider is useful
for browsing and retrieving information from the computer's certificate
stores, it does not let you add or remove items from these locations. If
you want to manage certificates in the certificate store, the System.Security.Cryptography.X509Certificates.X509Store
class (and other related classes from the System.Security.Cryptography.X509Certificates
namespace) from the .NET Framework support that functionality. For an
example of this approach, see the section called “Add and Remove Certificates”.
For more information about the certificate
provider, type Get-Help
Certificate.
One useful feature of the certificate provider
is that it supports a –CodeSign
parameter that lets you search for certificates in the certificate store
that support code signing.
-CodeSign --> -CodeSigningCert
If it's not already mentioned previously in the book then this may be a nice point to tell the reader about dynamic parameters, i. e. those that are added by a specific provider. In fact, it took me some time to even find the concept again in the included help. It's probably very confusing if you want to look up the parameter only to find that it doesn't exist in the cmdlet documentation.
This parameter is called a dynamic parameter:
one that has been added by a provider to a core PowerShell cmdlet. You
can discover the dynamic parameters for a provider by navigating to that
provider, and then reviewing the output of Get-Command
-Syntax. For example:
PS > Set-Location cert:PS > Get-Command Get-ChildItem -Syntax Get-ChildItem [[-Path] <String[]>] [[-Filter] <String>] (...) [-CodeSigningCert]
In
addition to reading the output of Get-Command, the
help topic for the provider often describes the dynamic parameters it
supports. For a list of the provider help topics, type
Get-Help -Category Provider.
Code-signing certificates are not the only kind of certificates,
however; other frequently used certificate types are Encrypting File System, Client Authentication, and more.
Example 18.8, “Search-CertificateStore.ps1” lets you search the certificate provider for certificates that support a given Enhanced Key Usage (EKU).
Example 18.8. Search-CertificateStore.ps1
##############################################################################
param(
$ekuName = $(throw "Please specify the friendly name of an " +
"Enhanced Key Usage (such as 'Code Signing'")
)
foreach($cert in Get-ChildItem cert:\CurrentUser\My)
{
foreach($extension in $cert.Extensions)
{
foreach($certEku in $extension.EnhancedKeyUsages)
{
if($certEku.FriendlyName -eq $ekuName)
{
$cert
}
}
}
}
For more information about running scripts, see the section called “Run Programs, Scripts, and Existing Tools”.
You want to add and remove certificates from the certificate store.
Use the certificate store APIs from the .NET Framework, as shown in Example 18.9, “Adding and removing certificates”.
Example 18.9. Adding and removing certificates
## Removing a certificate
$cert = Get-ChildItem cert:\currentuser\TrustedPublisher\<thumbprint>
$store = New-Object System.Security.Cryptography.X509Certificates.X509Store `
"TrustedPublisher","CurrentUser"
$store.Open("ReadWrite")
$store.Remove($cert)
$store.Close()
$cert = Get-PfxCertificate <path_to_certificate>
$store = New-Object System.Security.Cryptography.X509Certificates.X509Store `
"TrustedPublisher","CurrentUser"
$store.Open("ReadWrite")
$store.Add($cert)
$store.Close()The certificate drive provides a useful way to navigate and view certificates for the current user or local machine. For example, if your execution policy requires the use of digital signatures, the following command tells you which publishers are trusted to run scripts on your system:
Get-ChildItem cert:\CurrentUser\TrustedPublisher
The certificate provider is ultimately a read-only view of your certificates, however. After using the certificate provider to retrieve a certificate, you can then use the .NET APIs to remove it from the certificate store permanently.
Likewise, the
Get-PfxCertificate cmdlet lets you review a
certificate from a file that contains it, but does not let you install
it into the certificate store permanently. The .NET APIs are also the
way to import the certificate for good.
For more information about retrieving certificates from the certificate provider, see the section called “Access User and Machine Certificates”. For more information about working with classes from the .NET Framework, see the section called “Work with .NET Objects”.
You want to work with a security identifier in Security Descriptor Definition Language (SDDL) form.
An example of the actual SDDL form and a short explanation would be nice. ("The format is a null-terminated string with tokens to indicate each of the four main components of a security descriptor: owner (O:), primary group (G:), DACL (D:), and SACL (S:).")
I thought about that, but it's pretty chewy. I left it to the MSDN link in the rest of the recipe.
Use the
System.Security.AccessControl.CommonSecurityDescriptor
class from the .NET Framework, as shown by Example 18.10, “Automating security configuration of PowerShell
Remoting”.
Example 18.10. Automating security configuration of PowerShell Remoting
## Get the SID for the "PowerShell Remoting Users" group
$account = New-Object Security.Principal.NTAccount "PowerShell Remoting Users"
$sid = $account.Translate([Security.Principal.SecurityIdentifier]).Value
$config = Get-PsSessionConfiguration Microsoft.PowerShell
$existingSddl = $config.SecurityDescriptorSddl
$arguments = $false,$false,$existingSddl
$mapper = New-Object Security.AccessControl.CommonSecurityDescriptor $arguments
$mapper.DiscretionaryAcl.AddAccess("Allow",$sid,268435456,"None","None")
$newSddl = $mapper.GetSddlForm("All")
Set-PSSessionConfiguration Microsoft.PowerShell -SecurityDescriptorSddl $newSddlSecurity descriptors are often shown (or
requested) in SDDL form. The SDDL form of a security descriptor is
cryptic, highly-specific, and plain text. All of these aspects make it
difficult to work with reliably, so you can use the
System.Security.AccessControl.CommonSecurityDescriptor
class from the .NET Framework to do most of the gritty work for
you.
For more information about the SDDL format, see http://msdn.microsoft.com/en-us/library/aa379570%28VS.85%29.aspx. For an example of this in action, see the section called “Configure User Permissions for Remoting”.
No comments yet
Add a comment