使用用户名和密码启动进程

8

我知道您可以按照以下方式使用给定的用户名/密码运行进程:

var processInfo = new ProcessStartInfo
{
    WorkingDirectory = workingDirectory,
    FileName = "a name",
    UserName = loggedUserName, 
    Password = "password",
    Domain = userNameDomain,
    UseShellExecute = false,
};
Process.Start(processInfo);

我面临的问题是,我不想将实际密码作为代码的一部分写入,如果我将密码属性留空,该过程将无法启动...如何在不将密码作为硬编码字符串暴露在代码中的情况下安全地启动该进程?


1
ProcessStartInfo 的 Password 属性是 SecureString 类型的。你不能简单地以那种方式写入它。 - Steve
您可以将其存储在文件/数据库中,在程序启动时读取。您还可以通过依赖于文件的简单方法(https://dev59.com/bUfRa4cB1Zd3GeqP6zD-)或依赖于DB的内置函数(http://dev.mysql.com/doc/refman/5.0/es/set-password.html)来加密它。 - varocarbas
2个回答

9
ProcessStartInfo.Password不是一个简单的字符串,你不能直接写下并分配给该属性。你需要一个SecureString实例,但是不能通过将普通字符串传递给它的构造函数来创建SecureString。显然,操作系统没有API或方法允许非受信任程序检索当前用户的密码(这将是有史以来最大的安全漏洞)。

因此,在我的看法中,你只剩下一个选择。要求用户重新输入密码,并将结果输入转换为SecureString。

以下示例是字符串类的扩展方法,我在这里看到过

using System.Security;

// ...

public static SecureString ConvertToSecureString(this string password)
{
    if (password == null)
        throw new ArgumentNullException("password");

    unsafe
    {
        fixed (char* passwordChars = password)
        {
            var securePassword = new SecureString(passwordChars, password.Length);
            securePassword.MakeReadOnly();
            return securePassword;
        }
    }
}

您可以使用它来转换用户输入的密码并开始处理过程。

using(frmGetPassword fgp = new frmGetPassword())
{
     if(DialogResult.OK == fgp.ShowDialog())
     {
         SecureString ss = fgp.Password.ConvertToSecureString();
         var processInfo = new ProcessStartInfo
         {
             WorkingDirectory = workingDirectory,
             FileName = "a name",
             UserName = loggedUserName, 
             Password = ss,
             Domain = userNameDomain,
             UseShellExecute = false,
         };
         Process.Start(processInfo);
     }
}

1
请注意,任何类型的ConvertToSecureString方法都存在一个问题,即值一开始就在常规的string中 - 因此已经在内存中,并且破坏了使用SecureString的目的。此外,从.NET 4.6.1开始,还有一个ProcessStartInfo.PasswordInClearText属性。如果您已经有明文密码,最好使用它。 - Matt Johnson-Pint
@MattJohnson-Pint 谢谢,事实上明文传递密码帮助我跳过了所有这些答案(顺便说一下,没有必要使用 unsafe,因为可以逐个添加字符)。 - user11323942

2
我使用Windows密码存储来管理这些密码。请查看http://credentialmanagement.codeplex.com/库,它封装了Windows API。您的安装程序或管理员可以将密码添加到存储中,然后应用程序可以在运行时检索该密码。唯一的缺点是该存储是特定于用户的。您无法创建可跨多个用户使用的密码。
就是这么简单:
        _credentials = new CredentialSet("myApp:*");
        if (_credentials.Count == 0)
        {
            //TODO: ask user for password, supply it here, or use windows UI to set password (rundll32.exe keymgr.dll, KRShowKeyMgr)
            var c = new Credential()
            {
                Target = "myApp:Production",
                Username = "SomeUser",
                Description = "Credentials for doing something...",
                PersistanceType = PersistanceType.LocalComputer,
                Type = CredentialType.DomainPassword
            };
            c.Save();
            _credentials.Add(c);
        }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接