"Untrusted initialization" flaw - while creating SQL Connection - c#

I have done the following...
private static IDbConnectionProvider CreateSqlConnectionProvider(DbConfig dbConfig)
{
return new QcDbConnectionProvider(() =>
{
SqlConnectionStringBuilder csBuilder = new SqlConnectionStringBuilder();
if (!string.IsNullOrEmpty(dbConfig.DataSource))
csBuilder.DataSource = dbConfig.DataSource;
if (!string.IsNullOrEmpty(dbConfig.Database))
csBuilder.InitialCatalog = dbConfig.Database;
.
.
.
.
return new SqlConnection(csBuilder.ConnectionString);
});
}
The client is using VERACODE tool for doing code analysis and the VERACODE has detected a flaw "Untrusted initialization" at
return new SqlConnection(csBuilder.ConnectionString);
Also, the dbConfig is being initialized as shown below...
DbConfig configDbConfig = new DbConfig
{
Database = codeFile.ConfigurationDb,
DataSource = codeFile.DataSource,
IntegratedSecurity = sqlCredentials.UseWindowsAuthentication ? 1 : 0,
UserId = sqlCredentials.UseWindowsAuthentication ? null : sqlCredentials.SqlUserName,
ClearTextPassword = sqlCredentials.UseWindowsAuthentication ? null : sqlCredentials.SqlUserPassword
};
What else I need to do in order to fix this flaw? Also as per this link, I am creating the connection string using the SqlConnectionStringBuilder which is safe of creating the connection string.
Thanks in advance...

Description for Untrusted initialization issue is:
Applications should be reluctant to trust variables that have been initialized outside of its trust boundary. Untrusted initialization refers to instances in which an application allows external control of system settings or variables, which can disrupt service or cause an application to behave in unexpected ways. For example, if an application uses values from the environment, assuming the data cannot be tampered with, it may use that data in a dangerous way.
In your case you're reading data for dbConfig from file:
if (TryReadCodeFile(configurationProfileFile...)) {
DbConfig configDbConfig = new DbConfig...
}
Note that warning you get should also come with a line number (to circumscribe erroneous code). Almost everything in code you posted can generate this issue (I don't see where sqlCredentials comes from but it may even be another source of security problems if they're in clear text - or code to decrypt is accessible in your application).
From cited paragraph: "...application allows external control of system settings or variables, which can disrupt service...". This is the core of this issue: if your application uses external data without a direct control over them then its behavior can be changed modifying that data. What these external data are? List is all but not exhaustive:
Environment variables (for example to resolve a path to another file or program) because user may change them. Original files aren't touched but you read something else.
Paths (to load code or data) because user may redirect to something else (again original files aren't touched but you read something else).
Support files because user can change them (in your case, for example, to point to another server and/or catalog).
Configuration files because user can change them (same as above).
Databases because they may be accessible to other users too and they may be changed (but they may be protected).
How a malicious user may use this? Imagine each user is connected to a different catalog (according to their rule in organization). This cannot be changed and it's configured during installation. If they can have access to your configuration files they may change catalog to something else. They may also change DB host name to a tunnel where they may sniff data (if they have physical access to someone else's machine).
Also note that they also say "...assuming the data cannot be tampered with, it may use that data in a dangerous way". It means if, for example, your application runs on a web server and physical access is secured then you may consider that data safe.
Be aware your application will be secure as less secure item in your whole system. Note that to make an application safe (I know, this term is pretty vague) to encrypt password is not enough.
If support files may be manipulated then best thing you can do is to encrypt them with a public/private key encryption. A less optimal solution is to calculate a CRC or hash (for example) you'll apply to configuration files before you use them (they are able to change them but your application will detect this issue).
To summarize: you can ignore this issue but you have to prove your customer that data you rely on cannot be tampered. You can reasonably prove if at least one of these conditions is satisfied:
1) System where support files reside is not accessible by anyone else than your application. Your application security cannot be higher than system security.
2) Your support files are valid per-machine (to avoid copies between different machines) and they're encrypted in a way they cannot be changed (intentionally or not) by anyone.
3) Your support files are valid per-machine and they're hashed in a way your application can detect external changes.
4) It doesn't matter what users do with your configuration files, application itself cannot change its behavior because of that (for example it's a single installation where only one DB and one catalog exist).

The most important for connection strings is how they are stored. If they are stored in plaintext, this poses a security risk. So, it is advisable to store them in encrypted format and in application decrypt and use it.

Related

Testing same test cases across multiple environments

Currently we have set up our UI test automation in one environment(Dev). We are using C#, .Net, Visual Studio, specflow and MSTest
CONFIG
We read an app.config file for environment specific variables. We use Azure Pipeline for CI and run these tests on a nightly build.
<configuration>
<appSettings>
<add key="DevApp" value="https://dev.websitename.com />
</appSettings>
<connectionStrings>
<add name="DevDatabase" connectionString="http://dev.url/" />
</connectionStrings>
</configuration>
Now we want to run these tests on our UAT environment as well. The setup will be the same, and we want to run the same tests with the same data. The only difference is that for UAT we will point to different URL's and a different database.
for example
Dev env = https://dev.websitename.com
UAT env = https://uat.websitename.com
server name="DevDatabase" connectionString="http://dev.url/"
server name="UATDatabase" connectionString="http://uat.url/"
PASSWORDS
In terms of password, out application is an internal application and we use windows auth. So in our dev and uat environment we have the same password set up for all users. So in Dev = devpassword and UAT = uatpassword
For both dev and test we are using the same users with the password being the only difference.When testing we launch the browser using impersonation and launch the browser as 'run as' for that user
var service = ChromeDriverService.CreateDefaultService(driverpath)
if user is not null then we do this
var pwd = new SecureString()
service.StartDomain = Configurationhelper.Domain
service.StartupUserName = username
service.StartupPassword= = pwd
service.StartupLoadUserProfile = true
we store domain and password and other environmental variables in a separate config file as constants.
**Main issue: **
This wont work now so I think it could be best to store passwords as secrets in AZURE pipeline variables? if so, how would i change this code? for example
The server team, db team and devops team have taken care of server,db setup and urls etc
So for me its just configuring the test automation repo with my changes to configuration
What could be an elegant approach to do this?
AZURE PIPELINE
How could we run tests for both these environments in parallel? by parallel i mean having both run on a nightly run. Our Azure pipeline has 2 separate clients UAT and DEV pointing to the same artifact. The tasks and Variable are the same for both environments but with different values obviously
Currently they both would run in isolation
Solutions to this problem come down to how does the context (in your case the environment and all its associated connection strings and URLs) get to the tests where they will be consumed. In your question, you stated several orthogonal concerns:
using the same data
running in a different environment
running in parallel
Not mentioned is another concern
how to handle secrets (e.g. passwords in connection strings)
I'll explain one solution (a strategy really) that addresses these concerns, and why it appears to be a maintainable and extensible solution.
Using the same data
This can be very simple or very complex. The simple solution is to create a database of canonical and representative test data, and to then swap in that database to your environment. That can be done via your database's backup/restore or by creating the data programmatically. You would need to have a mechanism to restore or wipe the data whether the test(s) succeeds or fails.
Very rarely will using the environment's database "as is" lead to reliable tests; tests often modify state, and a database is the ultimate form of state; mutations of state will affect future tests.
It is with this last sentence that a full swap that occurs before/after each test is probably a) faster (occurring at a bulk/macro level with a quicker swap function), b) more maintainable (data can be reviewed/created ahead of time) and c) less brittle.
Running in a different environment
This, like the heart of your question discusses, is where you come down to whether to use multiple files or a single file. Using multiple files means you can take advantage of some of the built-in .NET configuration mechanisms that allow you to specify the environment. That means duplicating the files and changing the values to reflect the environment.
The other way, you mentioned, is storing all of this information into a single configuration file. If you do it this way, you need some sort of discriminator to disambiguate the entries, and your test needs to be able to pass in the environment name to some API to pull the value. I prefer this mechanism personally, because when you add a new feature/test you can add all the configuration in one place.
So the two mechanisms are roughly the same in terms of work, except the latter leads to a more compact editing session when adding new config, and from a readability/maintainability scenario, you have fewer places to look.
Secrets
If you follow the single-source of configuration approach, you simply extend that to your secrets, but you select the appropriate secret store (e.g. a secrets file or Azure Key Vault, or some such... again with an environment-based discriminator). Here's an example:
{
"DEV.Some.Key" : "http://devhost/some/path",
"UAT.Some.Key" : "https://uathost/some/other/path"
...
}
Using a discriminator means far less changes to your DevOps pipeline, which is, from a developer/editing experience, most likely slower and more cumbersome than editing a file or key vault.
Running in parallel
While you could rotate out the context and design your solution to run in parallel using the MSTest mechanisms, it would be more elegant to allocate this to your pipeline itself, and have enough resources to be able to run these pipelines in parallel by having enough build agents and so on.
Conclusion
It comes down to which parts of the solution should be addressed by which resources. The solution above allocates environmental selection and test execution into the pipeline itself. Granular values such as connection strings and secrets are allocated to a single source to reduce the friction that occurs when having to edit these values.
Following this strategy might better leverage your team's skills as well. Those with a DevOps mindset can most likely spin up new environments and parallelize more readily than a Developer mindset, who would be more aware of what data needs to be setup and how to craft the tests.

Storing data on client side - How to protect against manipulation?

General/Introduction:
I work on a project where we have two parts of software. There is a client application that runs on the user's computer and a web application that manages a lot of things related to this project.
The client application reads a lot of different values via IPC from another program, that I don't have the source code of and I don't have the option to change anything in that other program I am reading from.
So, my application collects those values and stores them locally, since it is not always connected to the web application.
Since the web application builds statistics and more out of those values and since the values that are collected are quite important for the whole project, the user should not be able to change them (or at least it should be really hard - I am pretty sure you can't provide 100 % security for data on the client).
Just for the sake of full information: The client application is written in C#, while the web application is based on the Laravel Framework (PHP). But this question is more about theory than exactly how to code this.
My thoughts:
I was thinking about having asymmetric encryption. The client encrypts the data with the public key of the web application server. The data is now stored encrypted. But of course, the client has this public key. Hence, an attacker could just go ahead and encrypt his own manipulated values and store them in the file.
Another thought, that builds upon the encryption, was that I could not only encrypt the data, but the whole file and use a format that is not too obvious. But that is more like security through obscurity and should be avoided as far as I know. Plus one could just decompile the client application and instantly have the format that I am using.
My question:
Is there any way I can provide a decent level of integrity when sending that data to the server? If so, how can it be done?
There are two things you can do:
Give up, because client software authenticity is not the server's problem, and it's theoretically impossible to know for sure that the other end is running the software you intend in a way that isn't spoofable.
If you're using the client software as a data mule, use hash_hmac() and hash_equals() to authenticate the data so it's tamper-evident.
For example, you could store the MAC by prefixing it to the data:
$key = random_bytes(32); // Store me for long-term. Maybe per-client?
$data = "foo";
$mac = hash_hmac('sha256', $data, $key);
echo $data . $mac;
And then to validate it upon being returned by the client software:
if (mb_strlen($message, '8bit') < 64) {
throw new Exception("Invalid message length.");
}
$mac = mb_substr($message, 0, 64, '8bit');
$data = mb_substr($message, 64, null, '8bit');
$recalc = hash_hmac('sha256', $data, $key);
if (!hash_equals($recalc, $mac)) {
throw new Exception("Invalid MAC.");
}
// Now we know $data is legitimate.
It is important to use hash_equals() not == or === to prevent timing attacks.
Note that this renders any such data unavoidably read-only. If you want them to be able to edit data, you're stuck with option 1.

C# ask user for servername and save for everytime application is ran

I've tried searching but had no luck as I'm not sure I'm using the correct terminology.
I'm trying to figure out how to ask a user to input their server name on the first application run, store that and insert it into filepath/connection string.
Pseudocode for initial run:
I see this is the first time you ran this application. Please input your fileserver name.
user inputs: fileserver123x
Write Fileserver123x to text file.
Initialize database connection
string fileServername = read text file;
connection = new OleDbConnection(#"Provider=Microsoft.ACE.OLEDB.12.0;Data Source=\'"+fileServerName+#"'\share\folder\Database.accdb;Persist Security Info=False;");
rest of code
I'm looking to see if there is a better/more professional way to store the user file server information other than via text file and read it each time the DB connection is initialized.
Any thoughts?
Your concept looks fine, I'd suggest a few improvements:
Store the file server name in the Windows registry instead of a text file. This is where well-behaved Windows programs store their configuration data.
Store the complete path to the database instead of the file server name. That way, your customers don't have to use a fixed share/folder name.
Make a configuration window where this configuration data can be modified. This can also serve as the window you show on your first run.
Don't read the configuration data each time a connection is opened. Read it once when your application starts and store it in a global variable.
(Note: In general, global variables are a code smell, but storing global configuration data is usually considered a legitimate use case. If you want a more advanced solution that simplifies unit testing, look into dependency injection for your configuration data.)

ClickOnce connection string encryption

I'm working on my first real WinForms application, and my boss has asked that I figure out a way to encrypt the connection string inside the app.config. I've read some of the suggestions in other questions about connection string encryption, and I recognize that it isn't a silver bullet answer to the security/privacy problem. We've considered writing web services to retrieve data from the database, but this is a very small project and unfortunately isn't a priority at this time.
Edit: I left out the detail that I'm working for a state institution (community college) where, because we're identifying students using a state-mandated private system ID, we need to secure the application in some form or fashion. Students may enter their network IDs to identify themselves (which we need to protect anyway as some students have restraining orders and need much of their records kept private), but many students only know their system IDs (which are always kept private).
Regardless, we'd like to get this process working in conjunction with ClickOnce deployment, but my encryption process crashes the application when I run the ClickOnce executable. Here's my encryption code (which is lifted from another question here on SO):
public static void EncryptConfigSection(string sectionName)
{
Configuration config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
ConfigurationSection section = config.GetSection(sectionName);
if (section != null)
{
if (section.IsReadOnly() == false &&
section.SectionInformation.IsProtected == false)
{
section.SectionInformation.ProtectSection("RsaProtectedConfigurationProvider");
section.SectionInformation.ForceSave = true;
config.Save(ConfigurationSaveMode.Full);
}
}
ConfigurationManager.RefreshSection(sectionName);
}
I'm calling this function from the Main() function in Program.cs, but I'm not sure if this is the appropriate place for it. Additionally, while this function encrypts the app.config correctly, as soon as I exit the application, the app.config decrypts. I feel like I'm missing a piece to the puzzle (or perhaps large swaths of the puzzle).
Can anyone offer me some insight into these problems? I'd like to reiterate that I recognize that web services are the end goal here, so if this is just not a solvable problem using CLickOnce, then I'm willing to suggest that we prioritize writing web services now.
Have you looked at this topic? It talks about setting up the client using DPAPI so the string is encrypted. I would still look at the web services route rather than embed a connection string, encrypted or not, into a client application. This is ESPECIALLY true if you are talking about client apps outside of your domain (ie, non-employee use).

How can my C# app test whether the user has "Read" access to a network share?

I work on a thick-client app that often runs into "issues" accessing network shares. Before doing any IO with the server, my app tests whether the share (usually of the form \\server\share$) exists. This works fine for detecting those scenarios in which the client has lost its connection to the server, but there are still those odd scenarios where the hidden share exists but the user does not have the rights to read from the within the share. Can someone share (no pun intended) the C# code required to test whether the current user can read files on a share? Should I be querying the share's ACL or the files within the share? What if the share is empty? What if the user is a local non-admin in a mixed environment (XP Pro workstation, Windows 2003 server without a domain on a Novell network)?
The easiest way is to just do it (i.e. try to read a file, for example). As Jared mentioned, there is no way to make sure that you will be able to read in the future (network failure, change of permissions, etc).
As far as code goes, you could use the DirectoryInfo class for some attempts at an answer:
string remotePath = #"\\server\share$";
bool haveAccess = false;
DirectoryInfo di = new DirectoryInfo(remotePath);
if (di.Exists)
{
try
{
// you could also call GetDirectories or GetFiles
// to test them individually
// this will throw an exception if you don't have
// rights to the directory, though
var acl = di.GetAccessControl();
haveAccess = true;
}
catch (UnauthorizedAccessException uae)
{
if (uae.Message.Contains("read-only"))
{
// seems like it is just read-only
haveAccess = true;
}
// no access, sorry
// do something else...
}
}
There are many shortcomings in the above code (such as the hard-coded "read-only" test), but it is just an example used to illustrate what you could do. DirectoryInfo has a few other helper methods that you can use to list the files in the folder. If you don't have access, the methods will throw an UnauthorizedAccessException exception which you can use to test why the access failed. Check out the info on GetAccessControl for further details on the exceptions it throws.
The #1 most reliable way to determine if you used to have permission to read from the share is to
Try and read from the share
Handle errors that could occur while reading and consider that a failed attempt
Unfortunately though based on your description you are trying to determine if you will have read permission to the share. There is no way to reliably determine this.
No matter how many ACLs, directories, etc ... you look at the moment you're done looking at them you could lose access to the share via any number of mechanisms. The most obvious one is the network share going down. All you can determine is that you used to have permission to the share.

Categories

Resources