是否可以通过批处理脚本在文件中存储和检索密码?

时间:2019-12-21 01:07:29

标签: windows powershell batch-file vbscript

@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

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

现在,DFC.exe密码行是存储密码的位置。

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

1 个答案:

答案 0 :(得分:4)

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

如果要使用一台计算机和一个用户的凭据

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

powershell.exe -c "Get-Credential | Export-CliXml cred.xml"

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

我们需要获取网络证书,以便在该命令中获得可用的密码。如果您确实在其他命令中也需要用户名,则也可以通过类似的方式获取该用户名:

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

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

如果要使用多台计算机和用户的凭据

上面,我们利用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

现在我们有了密钥文件,我们可以使用它将您的密码存储到磁盘上(请记住,这次我们只是将密码存储在文件中,而不是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

现在,您将把加密的密码文件保存到$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

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

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

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

  

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


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