【问题标题】:Is there a way to store and retrieve a password in a file from a batch script?有没有办法从批处理脚本中存储和检索文件中的密码?
【发布时间】:2019-12-21 01:07:29
【问题描述】:
@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 密码行是存储密码的地方。

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

【问题讨论】:

    标签: windows powershell batch-file vbscript


    【解决方案1】:

    您可以使用 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. 对于密码参数,我们从文件中读取密码并使用我们的密钥文件中的密钥将其解密为正确的SecureString
    5. 从生成的NetworkCredential 对象中,我们可以读取Password 属性,这是我们可以返回的可用密码。
    6. 作为原始批处理 for 循环的一部分,PASSWORD 设置为上一个 PowerShell 命令的输出,在本例中是我们构建的 NetworkCredentialPassword 属性。

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

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


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

    【讨论】:

    • Bender 真是太棒了,非常感谢!我肯定会使用这个。一个问题是它不会让我在用户名字段留空的情况下存储它。
    • 我已经在上面提到过,但是由于用户名在这里并不重要,您可以输入任何内容。如果您需要同时存储用户名和密码,请确保使用真实的用户名!
    • 非常感谢本德!你是最伟大的!
    • 我建议学习它,是的。它的语法更简洁,比 cmd.exe 更强大。
    • 我认为有一种方法可以使用证书作为密钥,但我不知道细节。
    猜你喜欢
    • 2021-01-13
    • 1970-01-01
    • 2014-03-18
    • 2019-08-16
    • 2019-10-10
    • 1970-01-01
    • 1970-01-01
    • 2013-08-20
    • 1970-01-01
    相关资源
    最近更新 更多