有没有办法从批处理脚本中存储和检索文件中的密码?

Ril*_*ley 6 windows vbscript powershell batch-file

@ECHO OFF
DFC.EXE get /isfrozen

IF Errorlevel 1 GOTO Frozen
IF Errorlevel 0 GOTO Thawed

:Frozen
Echo Errorlevel 1 Computer is Frozen, Thawing Now
DFC.EXE fakepassword123 /BOOTTHAWED
Echo
Goto END

:Thawed
Echo Errorlevel 0 Computer is Not Frozen
Echo
Goto END

:END
Exit
Run Code Online (Sandbox Code Playgroud)

我正在尝试运行一个非常简单的批处理文件,但如果有人要编辑密码,我不希望以纯文本形式存储密码。

现在 DFC.exe 密码行是存储密码的地方。

有没有办法将此密码隐藏在另一个文件中并从那里调用它?

Ben*_*est 5

您可以使用 PowerShell 将密码存储在磁盘上,这种方式(默认情况下)只能由创建密码的计算机上的当前执行用户检索。如果您无法在 PowerShell 中编写整个脚本,您至少可以通过powershell.exe在脚本期间调用来完成此操作。

如果您想使用来自单个机器和用户的凭据

要创建凭证文件(这将提示您输入凭证,在这种情况下用户名无关紧要,但必须提供):

powershell.exe -c "Get-Credential | Export-CliXml cred.xml"
Run Code Online (Sandbox Code Playgroud)

Export-CliXml是一个特殊的 cmdlet,它将 PowerShell 对象序列化到磁盘,并带有一些边缘警告(例如不要尝试使用 COM 对象执行此操作并期望它可以工作),通常可用于将对象读回另一个会话,因为我们下面做。并将其从您的脚本读入一个变量并在您的命令中使用它:

for /f %i in ('powershell.exe -c "( Import-CliXml cred.xml ).GetNetworkCredential().Password"') do set PASSWORD=%i
DFC.EXE %PASSWORD% /BOOTTHAWED
Run Code Online (Sandbox Code Playgroud)

我们需要获取网络凭据,以便我们可以获得可用密码作为该命令的一部分。如果您发现自己在其他命令中也需要用户名,您也可以以类似的方式获取:

for /f %i in ('powershell.exe -c "( Import-CliXml cred.xml ).UserName"') do set USERNAME=%i
Run Code Online (Sandbox Code Playgroud)

对于用户名,它没有加密,因此您无需返回网络凭据即可获得可用的用户名。

如果要使用来自多台机器和用户的凭据

上面,Get-Credential为了简单起见,我们利用创建初始凭证,但使用它构建凭证对象并不是绝对必要的。

如果您需要解密来自多台机器或用户的凭据,则稍微复杂一些。您将无法利用Get-Credential并且必须自己构建凭证,并且仅将密码存储在文件中(您也可以存储用户名,但对于这种情况)。

准备秘密

要准备凭证,首先我们需要创建一个密钥文件。这比能够使用更复杂,Get-Credential您只需在第一次生成密钥文件或密码文件时执行这些步骤。最好从powershell.exe以下位置执行此步骤:

$keyFile = "C:\path\to\keyfile.key"

# you can adjust this number for different levels of AES encryption
# 32 = 256 bits
# 24 = 192 bits
# 16 = 128 bits
$key = New-Object Byte[] 32
[Security.Cryptography.RNGCryptoServiceProvider]::Create().GetBytes($key)

# Save the key file to disk
$key | Out-File $keyFile
Run Code Online (Sandbox Code Playgroud)

现在我们有了密钥文件,我们可以使用它将您的密码存储到磁盘(请记住,这次我们只是将密码存储在文件中,而不是PSCredential对象中)。再一次,这最好powershell.exe与上一步一样:

# Save your password in a file so you don't have it plaintext in your history
$insecurePassFile = "C:\path\to\insecurePassFile.txt"

# This will be the encrypted password outfile
$securePassFile = "C:\path\to\securePassFile.txt"

# This can also be a UNC path if on a share
$keyFile = "C:\path\to\keyfile.key"

# Read the key in
$key = Get-Content $keyFile

# Read the plaintext password in and convert it to a secure string using our key
$password = Get-Content $insecurePassFile | Select-Object -First 1 | ConvertTo-SecureString -AsPlainText -Force | ConvertFrom-SecureString -key $key

# Write the encrypted password out to a file to be read in later using our key
$password | Out-File $securePassFile
Run Code Online (Sandbox Code Playgroud)

现在,您将加密的密码文件保存到$securePassFile. 此时,您可以将密码文件和密钥复制到网络上的某个位置。

在批处理脚本中使用凭据

现在我们要回到批处理世界。要读入密码,您需要知道密码文件和密钥文件的位置,并有权访问这两个文件。我很抱歉,但这将是一个很长的 PowerShell 命令,可以放在一行中:

for /f %i in ('powershell.exe -c "$key = Get-Content \\server.domain.tld\share\path\to\keyfile.key; [System.Net.NetworkCredential]::new("", ( Get-Content \\server.domain.tld\share\path\to\password.txt | ConvertTo-SecureString -Key $key ) ).Password"') do set PASSWORD=%i
Run Code Online (Sandbox Code Playgroud)

那是一口,所以让我们分解一下:

  1. 批处理for循环是将变量设置为命令输出所需的“魔法”。最终将PASSWORD变量设置为PowerShell 命令的输出。IMO 编写命令提示符的人是受虐狂 XD。
  2. 这开始了实际的解密过程。首先,我们需要$key从密钥文件中读取。没有这个,我们就无法从密码文件中解密我们的密码。
  3. 我们创建了一个新的网络凭据,我们可以通过创建一个新System.Net.NetworkCredential对象来从中提取密码。第一个参数是用户名(这里我们不需要,空字符串有效),第二个参数是SecureString密码:
  4. 对于密码参数,我们从文件中读取密码并SecureString使用我们的密钥文件中的密钥将其解密为正确的。
  5. 从生成的NetworkCredential对象中,我们可以读取Password属性,这是我们可以返回的可用密码。
  6. 作为原始批处理for循环的一部分,PASSWORD设置为前一个 PowerShell 命令的输出,在本例中是我们构建的Password属性NetworkCredential

像对待任何其他敏感密码一样对待您的密钥

如果您提供了自己的密钥,请确保将您的密钥存放在安全的地方。只有应该有权访问它的用户和机器才能读取它。理想情况下,凭证和机密应该从机密保险库(例如 Hashicorp 保险库、Keepass 等)存储和检索,但文件 ACL 也可用于控制谁可以访问这些信息。


请注意,当帐户密码更改时,cred.xml如果您依赖默认加密行为,则必须重新生成。

  • 我建议你学习一下,嗯。它的语法更简洁,而且比 cmd.exe 更强大。 (2认同)