Search code examples
c#vsixvspackagedpapi

Manage sensitive Options data in VSPackage


I'm working on a Visual Studio extension package (VSIX) which needs to connect to a database.

I'd also like to take reasonable precautions security-wise when storing sensitive configuration.

At the moment, I'm using a standard property-grid Options page for my plugin, as described in the introductory documentation for Visual Studio extensions.

public class MyPackageOptions : DialogPage
{
    [Category("Repository")]
    [DisplayName("Server")]
    [Description("Database host name or IP address")]
    public String Server { get; set; }

    [Category("Repository")]
    [DisplayName("Port")]
    [Description("Database listen port")]
    public UInt16 Port { get; set; } = 3306;

    [Category("Repository")]
    [Description("Database name")]
    public String Database { get; set; } = "default_database_name";

    [Category("Repository")]
    [DisplayName("User ID")]
    [Description("Database login user name")]
    public String UserId { get; set; }

    // BUG PasswordPropertyTextAttribute doesn't seem to be having the desired effect
    [Category("Repository")]
    [Description("Database login password")]
    [PasswordPropertyText]
    public String Password { get; set; }
}

The Category, DisplayName and Description attributes have the effect I expect on the property grid display. But the PasswordPropertyTextAttribute doesn't seem to have any effect. Is there some other way to get text masking to work with basic property-grid-based options pages? Or do I need to make a custom options UI form to get text masking?

Secondly, how can I ensure that the password field value is persisted on disk in a reasonably secure fashion, similar to other software which needs to store user credentials? For my needs, I think Windows DPAPI protection using the current user account would be sufficient protection, but I'm not sure how to use it to protect the MyPackageOptions.Password property.


Solution

  • For PasswordPropertyTextAttribute to be effective, it must be "turned on" by setting its Password property to true. If the default constructor is used, the Password property is false and the attribute has no effect.

    The easiest way to make it work is to add the attribute to the property with the line [PasswordPropertyText(true)], instead of [PasswordPropertyText], to invoke the appropriate non-default constructor.

    Persisted options values can be encrypted by overriding the DialogPage LoadSettingsFromStorage, SaveSettingsToStorage, LoadSettingsFromXml, and SaveSettingsToXml methods as described in this answer.

    Implementing a custom TypeConverter to handle the encryption/decryption (similar to this approach) isn't useful because the same TypeConverter is used for converting to/from string values in the PropertyGrid and also when persisting the value in the Visual Studio registry or XML exports. So if the converter can handle plain text inputs in the PropertyGrid, it will also generate plain text strings when the object is saved to storage or exported to XML.