PowerShell 技能连载 - 使用代理服务器的缺省凭据
如果您的公司使用一个需要身份认证的代理服务器,PowerShell 可能有时候无法访问 Internet。您可能需要通知代理服务器使用凭据缓存中的缺省凭据:
1 | [System.Net.WebRequest]::DefaultWebProxy.Credentials=[System.Net.CredentialCache]::DefaultCredentials |
如果您的公司使用一个需要身份认证的代理服务器,PowerShell 可能有时候无法访问 Internet。您可能需要通知代理服务器使用凭据缓存中的缺省凭据:
1 | [System.Net.WebRequest]::DefaultWebProxy.Credentials=[System.Net.CredentialCache]::DefaultCredentials |
今天我们不讨论代码,而是讨论 PowerShell 的总体情况。Microsoft 宣布了下一代 PowerShell 叫做 “PowerShell 7”,并且是基于 .NET Core 3.0 的。这是非常重大的变化,因为 .NET Core 3.0 重新引入了 WPF (Windows Presentation Foundation, GUI),至少在 Windows 平台上。基于这个变化,PowerShell 可以重新引入 GUI 相关的 cmdlet 例如 Out-GridView
,并且可以期待 PowerShell 7 能够弥补大多数使用 PowerShell 6 的 Windows 管理员所缺少的功能。
虽然在 PowerShell 7 之前,使用目前的版本也是可行的,但我们建议您关注新闻并阅读以下链接:
https://devblogs.microsoft.com/powershell/the-next-release-of-powershell-powershell-7/
远程服务器管理工具 (RSAT) 过去是一个外部下载,添加了两个重要的 PowerShell 模块:ActiveDirectory
和 GroupPolicy
。不幸的是,主要的 Windows 更新移除了已安装的 RSAT 工具,所以如果您的脚本需要客户端的 Active Dicrectory 命令,那么需要人工确定并且下载合适新版 Windows 10 的 RSAT 包并且手工安装它。
在 Windows 10 Build 1809 和以后的版本中,这要更容易一些。您可以通过 PowerShell 以类似这样的方式控制 RSAT 状态(假设您有管理员特权):
1 | PS> Get-WindowsCapability -Online -Name *RSAT.ActiveDirectory* |
要安装 RSAT,请运行以下代码:
1 | PS> Get-WindowsCapability -Online -Name *RSAT.ActiveDirectory* | |
虽然在某些情况下仍然需要下载 RSAT 包,但您不再需要搜索正确的版本并手动构建。
Windows 索引服务能够对您的用户数据文件进行索引,并且在文件资源管理器中快速搜索。以下是一个基于内容返回文件的函数:
1 | function Search-FileContent ([String][Parameter(Mandatory)]$FilterText, $Path = $home ) |
要使用它,请指定一个关键字。以下代码返回所有包含该关键字的文件:
1 | PS> Search-FileContent -FilterText testcase -Path C:\Users\tobwe\Documents\ |
如你快速发现的,Index Search 并不会返回 PowerShell 脚本(*.ps1 文件)。缺省情况下,PowerShell 脚本并没有被索引。如果您希望通过内容搜索到这些文件,请到 Index Service 设置并且包含 PowerShell 脚本。点击这里了解更多:https://devblogs.microsoft.com/scripting/use-windows-search-to-find-your-powershell-scripts/
PowerShell 包含许多循环构造。这些循环构造不能变成流,所以您无法将结果通过管道传递给其它 cmdlet 并且享受管道的实时性优势。相反,您必须先将所有数据存储在变量中,并且只有当循环结束之后才可以将变量通过管道传递给其它命令。
虽然您可以用 ForEach-Object
来代替传统的 foreach
和 for
循环,但它会减慢代码的执行速度,并且不是代替 while
和 do
循环的方案。
以下是一个对所有循环启用快速流的简单技巧。让我们从这个非常简单的循环开始:
1 | # stupid sample using a do loop |
它会一直循环直到达到随机数 6。您会发现无法实时将结果传输到管道,所以你必须使用类似这样的方法来对结果进行排序或者用它们来做其他事情:
1 | $all = do |
通过将循环封装到一个脚本块中,您可以获得实时流:更少的内存消耗,立即得到结果:
1 | & { do |
您可能知道,有两种类型的 PowerShell:随着 Windows 操作系统分发的 Windows PowerShell 是基于完整的 .NET Framework而 PowerShell 6 以及更高版本是开源、跨平台,并且基于(有限的).NET Core 和 Standard。
如果您开发能够在两类系统上执行的脚本,那非常棒!不过如果如果知道您的代码是基于其中的一个系统,请确保在脚本的顶部添加合适的 #requires
语句。
这段代码只能在 PowerShell 6 及更高版本运行(假设您已事先将它保存为文件):
1 | #requires -PSEdition Core |
类似地,以下代码只能在 Windows PowerShell 中运行:
1 | #requires -PSEdition Desktop |
要调节音量以及静音、取消扬声器的静音,PowerShell 可以像这样用 C# 代码来操作 API:
1 | Add-Type -TypeDefinition @' |
在前一个技能中我们解释了如何用 Web Service 来安全地检测密码并且查明它们是否已被泄漏。
信息安全有关的代码有时经过压缩后看起来是否“有趣”,所以在第一步分钟我们分享了优美的而且可读的代码。而以下是考虑“信息安全”的变体,它展示 PowerShell 代码可以被压缩到什么程度并且可以自动混淆。这段代码返回一个指定的密码被暴露了多少次(如果未曾发现被暴露过,返回 null
)。
1 | $p = 'P@ssw0rd'[Net.ServicePointManager]::SecurityProtocol = 'Tls12'$a,$b = (Get-FileHash -A 'SHA1' -I ([IO.MemoryStream]::new([Text.Encoding]::UTF8.GetBytes($p)))).Hash -split '(?<=^.{5})'(((irm "https://api.pwnedpasswords.com/range/$a" -UseB) -split '\r\n' -like "$b*") -split ':')[-1 |
复杂的密码不一定安全。例如,”P@ssw0rd” 是一个非常复杂的密码,但是非常不安全。这是为什么安全社区开始建议用更相关的测试取代复杂性标准,并防止使用以前黑客入侵中使用过的密码。这些密码——虽然它们可能很复杂——是字典攻击的一个常规部分并且非常不安全。
如何知道某个密码是否已被泄露?您可以使用类似 haveibeenpwnd.com 的网站或者它们的 API。这是它的工作原理:
以下是用 PowerShell 检查密码的方法:
1 | # enable all SSL protocols |
试着改变 $Password
中的密码来测试这段代码。您会很惊讶地发现许多密码已经泄漏:
Sunshine has been seen 13.524 times.
在前一个技能中我们学习了如何在 PowerShell 中启用所有的 SSL 安全协议来连接到 Web Service 和 网站:
1 | [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Ssl3 -bor [Net.SecurityProtocolType]::Tls -bor [Net.SecurityProtocolType]::Tls11 -bor [Net.SecurityProtocolType]::Tls12 |
有趣的是,可以用一行更短的代码来代替:
1 | [Net.ServicePointManager]::SecurityProtocol = 'Ssl3, Tls, Tls11, Tls12' |
以下是它的原因:
由于 SecurityProtocol
是 Net.SecurityProtocolType
类型的,所以当您传入字符串数据,它可以自动转换:
1 | PS> [Net.ServicePointManager]::SecurityProtocol.GetType().FullName |
与其用 SecurityProtocolType
枚举并且用 -bor 操作符来连接,您还可以用比特标志位的名称组成的逗号分隔的字符串。两者是相同的:
1 | $a = [Net.SecurityProtocolType]::Ssl3 -bor [Net.SecurityProtocolType]::Tls -bor [Net.SecurityProtocolType]::Tls11 -bor [Net.SecurityProtocolType]::Tls12 |