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.
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.