I am writing an SQL Server application in C# built in Visual Studio. It is a Windows Forms Application. The program will be installed on the network where users will run it.
The problem I am struggling with is how to manage the configuration file. It has the server username and password there for all to see. I tried Click Once and an encryption scheme but they both required the programs to run on the computer the program was running from. It failed when I tried to run it from a workstation. This is different from How do I avoid having the database password stored in plaintext in sourcecode? because all of those solutions either suggested using integrated security or machine based encryption. Neither of those options would work for me.
Any help would be deeply appreciated.
Don't store passwords in plain text. Period. Full stop.
You should take a cue from SQL Server. Yes, you can store usernames in passwords in plain text in a web/app.config. But for Production servers you never should. Instead for Production deployments you should have a config that uses Integrated Security. That allows for elevated access by accessing credentials which are handled securely by Windows rather than insecurely in a config file.
Similarly, you should use something like WindowsIdentity, or OpenId. Then you can pass around auth tokens in your code rather than storing credentials in plain text.
This is why software developers created multi-tier designs that include middleware services like web services. Web services can be hosted in IIS and the windows account and password can be configured into the Application Identity section of the application connection pool. Then the web.config connection string can be configured with trusted_connection=true. Configuring it this way uses the Windows Data Protection API to protect the identities.
If you mean data at app.config it is simple! You have to use these two classes:
EntityConnectionStringBuilder
https://msdn.microsoft.com/en-us/library/system.data.entityclient.entityconnectionstringbuilder(v=vs.110).aspx
And
SqlConnectionStringBuilder
https://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqlconnectionstringbuilder(v=vs.110).aspx
I learn it from this page: Programmatic Connection Strings in Entity Framework 6 It is very good guide. In any cases, That link didn't help you!? Just Google something like this:
C# define connection string at runtime
After you put all connection string inside your code, you can go and delete any sensitive data from connectionStrings tag of app.config file because your app will not use it anymore! Then compile your code again.
If you are using DB First in EF, then you can check this Guide too: How to set Connection String with Entity Framework
UPDATED:
I added two of my Classes that I manage and create connection string with them programmatic (Dynamic), One is belong to my Entity Framework project that I used SQL Server Compact Edition (SQL Server CE) and the second one belong to another Entity Framework Project That I used SQL Server Express 2014 with SQL Server authentication (used sa username). I will leave both method here in case anyone need them:
This belong to my SQL Server CE project:
public static string GetDBConnectionString(string dataParentPath = "")
{
EntityConnectionStringBuilder entityBuilder = new EntityConnectionStringBuilder();
SqlCeConnectionStringBuilder sqlCEBuilder = new SqlCeConnectionStringBuilder();
if (string.IsNullOrEmpty(dataParentPath) == true)
dataParentPath = #"C:\MyDBFolder\CMS.sdf";
sqlCEBuilder.DataSource = dataParentPath;
sqlCEBuilder.Password = "12345687";
sqlCEBuilder.MaxDatabaseSize = 4090;
entityBuilder.Metadata = "res://*/CMS.csdl|res://*/CMS.ssdl|res://*/CMS.msl";
entityBuilder.ProviderConnectionString = sqlCEBuilder.ToString();
entityBuilder.Provider = "System.Data.SqlServerCe.4.0";
return entityBuilder.ToString();
}
This belongs to my SQL Server Express project with SQL Server authentication:
using System;
using System.Collections.Generic;
using System.Data.Entity.Core.EntityClient;
using System.Data.SqlClient;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace CMS
{
class mySettings
{
public static string GetDBConnectionString()
{
// **************************************************
// This is my "ConnectionString" from App.config file.
// <connectionStrings>
// <add name="CMSEntities"
// connectionString=
// "metadata=res://*/CMS.csdl|res://*/CMS.ssdl|res://*/CMS.msl
// ;provider=System.Data.SqlClient
// ;provider connection string="
// ;data source=MY-PC\SQLEXPRESS
// ;initial catalog=CMS
// ;user id=sa
// ;password=12345687
// ;MultipleActiveResultSets=True
// ;App=EntityFramework
// ""
// providerName="System.Data.EntityClient" />
//</connectionStrings>
// **************************************************
string metaData = "res://*/CMS.csdl|res://*/CMS.ssdl|res://*/CMS.msl";
string providerName = "System.Data.SqlClient";
string dataSource = #"MY-PC\SQLEXPRESS";
string databaseName = "CMS"; // = InitialCatalog
string userID = "sa";
string password = "12345687";
string appName = "EntityFramework";
EntityConnectionStringBuilder entityBuilder = new EntityConnectionStringBuilder();
SqlConnectionStringBuilder sqlBuilder = new SqlConnectionStringBuilder();
// = = = = = = = = = = = = = = = =
sqlBuilder.DataSource = dataSource;
sqlBuilder.InitialCatalog = databaseName;
sqlBuilder.MultipleActiveResultSets = true;
sqlBuilder.UserID = userID;
sqlBuilder.Password = password;
sqlBuilder.ApplicationName = appName;
// = = = = = = = = = = = = = = = =
entityBuilder.Provider = providerName;
entityBuilder.Metadata = metaData;
entityBuilder.ProviderConnectionString = sqlBuilder.ConnectionString;
return entityBuilder.ToString();
}
}
}
As you can see, My database in both project have same name "CMS" so its Entities will be named "CMSEntities". Now! you have to override its DbContext constructor. It is Important but easiest part! Better description than mine is from this page "http://www.cosairus.com/Blog/2015/3/10/programmatic-connection-strings-in-entity-framework-6":
Now your Entity Model extends from DbContext and DbContext provides a
constructor to pass in a Connection String, but your Entity Model does
not overload those constructors for you. In order to access the
constructor overload, you will need to create a new class partial for
your Entity Model database context in the same namespace as your
Entity Model with the required constructor signature. Pro Tip: be sure
to name the filename of the cs file a different name than the Entity
Model database context in the event that future generated code does
not overwrite your changes.
So I build a class at root of my Project, The class must be partial:
using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace CMS // Your Project Namespace
{
public partial class CMSEntities : DbContext
{
public CMSEntities(string connectionString)
: base(connectionString)
{
}
}
}
and Anytime I wanna access to my Database I will use this code:
using (CMSEntities db = new CMSEntities(CMSSettings.GetDBConnectionString()))
{
// Do your DB stuff here...
}
I hope It help you or others which I learn all of that from this site "stackoverflow" and users.
Good Luck
Related
I had successfully encrypt my SQLite file using zetetic method. Using DB Browser for SQLite to open encrypted file, I was able to browse the data after entering the encryption key. Currently, I can't seem to decrypt the encrypted file using the below code:
string password = "secretKey";
using (SQLiteConnection sqlite_conn = new SQLiteConnection("Data Source=c:\\test.db"))
{
sqlite_conn.ConnectionString = new SqliteConnectionStringBuilder(sqlite_conn.ConnectionString){
Password = password
}.ToString();
sqlite_conn.Open();
using (SQLiteCommand cmdCount = new SQLiteCommand("SELECT * from fruit", sqlite_conn))
{
using (SQLiteDataReader reader = cmdCount.ExecuteReader())
{
while (reader.Read())
{
MessageBox.Show(reader["Type"].ToString());
}
}
}
}
Encountered error: System.Data.SQLite.SQLiteException: 'file is not a database
file is not a database'
Update:
public class Model
{
[PrimaryKey,AutoIncrement]
public int Id { get; set; }
public string Content { get; set; }
}
var conn = new SQLiteConnection(new SQLiteConnectionString(filename, true, password));
conn.ExecuteScalar<int>(String.Format("PRAGMA cipher_license = '{0}';", licenseKey));
conn.CreateTable<Model>();
I was able to create table using above code but how do I select, update, insert new data in with parameterized?
First, immediately after opening your connection I would verify that you are properly including SQLCipher. This can be verified by executing a SQL statement which will return the current SQLCipher library version number:
PRAGMA cipher_version;
If you do not receive a value back from the above query, SQLCipher is not properly integrated with your application.
If you do get a version string value, but remain unable to query the database content itself, either your password is incorrect, or possibly the encrypted database file may have been created with a different major version of SQLCipher than the library version. If this is the case, you can choose to either perform a one-time migration by executing the following command:
PRAGMA cipher_migrate;
Alternatively, you can choose to set a compatibility mode based on the SQLCipher version that was used to create the database file. More information about the compatibility mode can be found here.
I am implementing SQLCipher into a Xamarin.Forms application. I thought everything was working until I noticed that the DB that was being created by the X.F. application was actually a SQLite3 DB w/o encryption or a password. After looking into it for a while, I haven't been able to find a solution. I am encountering an exception that says
System.InvalidOperationException: 'You specified a password in the connection string, but the native SQLite library you're using doesn't support encryption.'
I currently have 4 projects in this solution. The standard 3 in XamarinForms (Default PCL for cross platform stuff, Project.Android, and Project.iOS). In addition to those 3, I have a custom PCL that is labeled Project.Core. This PCL is responsible for all DataAccess since it implements the Repository Pattern, Unit Of Work, DbContext, etc.
In this 4th project, and within my DbContext.cs class, I have this:
// Added for more context
using System;
using System.IO;
using Microsoft.Data.Sqlite;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Xamarin.Forms;
private SqliteConnection connection;
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
string connStr = Path.Combine(
path1: Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments),
path2: "App.db");
string passStr = deviceIdentifier;
string path = Path.GetDirectoryName(connStr);
if (!Directory.Exists(path))
{
Directory.CreateDirectory(path);
}
// Check if db file exists
if (!File.Exists(connStr))
{
FileStream stream = File.Create(connStr);
stream.Close();
}
// DOCS => https://learn.microsoft.com/en-us/dotnet/standard/data/sqlite/encryption?tabs=netcore-cli
// => https://www.bricelam.net/2016/06/13/sqlite-encryption.html
var connectionString = new SqliteConnectionStringBuilder()
{
DataSource = connStr,
Mode = SqliteOpenMode.ReadWriteCreate,
Password = passStr
}.ToString();
// NOTE: THIS IS WHERE THE EXCEPTION IS THROWN!!!
// THE CODE BELOW THIS IS AN ALTERNATE ROUTE THAT DOENS'T WORK EITHER
**connection.Open();**
// This code doesn't throw anything, but it doesn't key the DB either
using (SqliteCommand command = connection.CreateCommand())
{
command.CommandText = "SELECT quote($password);";
command.Parameters.AddWithValue("$password", passStr);
string escapedPassword = (string)command.ExecuteScalar(); // Protects against SQL injection
command.CommandText = "PRAGMA key = " + escapedPassword /*+ ";"*/;
command.Parameters.Clear();
command.ExecuteNonQuery();
}
#if DEBUG
optionsBuilder.EnableSensitiveDataLogging();
#endif
optionsBuilder.UseSqlite(connection);
SQLitePCL.Batteries_V2.Init();
}
Through my research it appears there might be an issue with one of the SQLite/SQLCipher packages in this PCL (the PCL is targeting .NET Standard 2.0 for reference).
I currently have:
Microsoft.Data.Sqlite.Core 3.1.1 (w/ dependencies on Microsoft.Data.Sqlite.dll & SQLitePCLRaw.core 2.0.2)
SQLitePCLRaw.bundle_sqlcipher 1.1.14 (w dependencies on SQLitePCLRaw.core 2.0.2, SQLitePCLRaw.batteries_sqlcipher.dll, SQLitePCLRaw.batteries_v2.dll)
A couple of other things to note:
When viewing SQLitePCL namespace, it shows the package as being sqlitepclraw.bundle_e_sqlite3 instead of having a reference to sqlcipher.
\.nuget\packages\sqlitepclraw.bundle_e_sqlite3\2.0.2\lib\netstandard2.0\SQLitePCLRaw.batteries_v2.dll
I believe there may be an issue with that dependency, but I'm not sure and would appreciate any assistance!
Thanks in advance.
PS - Can provide more information as requested
Found a working solution.
After looking into the packages, I found that replacing the existing SQLitePCLRaw bundle package with SQLitePCLRaw.bundle_zetetic found here, resolved the issues connecting and maintaining an encrypted database.
Working code snippet is:
// StringBuilder here, and the SqliteConnection below are
// from the Microsoft.Data.Sqlite namespace v3.1.1
var connectionString = new SqliteConnectionStringBuilder()
{
DataSource = connStr,
Mode = SqliteOpenMode.ReadWriteCreate,
Password = passStr
}.ToString();
connection = new SqliteConnection(connectionString);
connection.Open();
I have created a database programmatically.
Is there any way to create its connection string dynamically using C#, so that after the database creation all data is stored in the new database using C#.
You could also used nuget which will create ConnectionString for you.
After installing package
Install-Package ConnectionStringPT
You will just need to invoke:
var connectionString = ConnectionString.GetSqlServerConnectionString("localhost", "dbName");
I have use this in my C# widows form app and it works for me. Change it according to your needs.
You need to add namespace:
//using Microsoft.SqlServer.Management.Smo;
using System.Data.SqlClient;
and add below code:
try
{
SqlConnectionStringBuilder _connectionString = new SqlConnectionStringBuilder();
_connectionString.DataSource = #".\SQLEXPRESS";
_connectionString.InitialCatalog = "databaseName"; //add database name which created dynamically
_connectionString.IntegratedSecurity = true;
}
catch(Exception ex)
{
MessageBox.Show("Not able to create connection string. Error : " +ex);
}
I'd like to supply the connection string for my database at runtime. I am using the Entity Framework. This is what I have so far
class MyClassDBContext:DbContext
{
public MyClassDBContext(string str) : base(str)
{
this.Database.Connection.ConnectionString = str;
}
}
To use the above code, I tried
//create connection string
EntityConnectionStringBuilder myConn = new EntityConnectionStringBuilder();
myConn.Provider = "System.Data.SqlClient";
myConn.ProviderConnectionString = "user id=xxxx;password=xxxx;server=localhost;database=xxxx;connection timeout=30";
//inject the connection string at runtime
MyClassDBContext a = new MyClassDBContext(myConn.ToString())
The above code gave me an error saying "Provider keyword not supported".
To attempt to debug this error, I tried the following
MyClassDBContext a = new MyClassDBContext("metadata=res://*/Model.csdl|res://*/Model.ssdl|res://*/Model.msl;provider=System.Data.SqlClient;provider connection string=user id=xxxx;password=xxxx;server=localhost;database=xxxx;connection timeout=30")
Now, I got an error saying "metadata keyword not supported". So I changed my code to
MyClassDBContext a = new MyClassDBContext("provider=System.Data.SqlClient;provider connection string=user id=xxxx;password=xxxx;server=localhost;database=xxxx;connection timeout=30")
Now I got an error saying "provider keyword not supported". So I again changed my code to
MyClassDBContext a = new MyClassDBContext("user id=xxxx;password=xxxx;server=localhost;database=xxxx;connection timeout=30")
and now it works!. My question is : how do I specify the provider and metadata at runtime? It looks like only the connection string is being accepted. I am using Entity 4.3.1 from Nuget.
Thanks
edmx file based EF require the "Provider" and "Metadata" content. Code-first based EF doesn't require this, requiring only the regular connection string. You could use a SqlConnectionStringBuilder (instead of EntityConnectionStringBuilder) to build this normal connection string if you'd like. But as you've seen, you need only specify the actual connection details. The Provider and Metadata aren't needed in EF 4.3.1's DbContext Code-first paradigm.
Building on HatSoft's answer:
var entityConnectionStringBuilder= new EntityConnectionStringBuilder();
entityConnectionStringBuilder.Provider = "System.Data.SqlClient";
entityConnectionStringBuilder.ProviderConnectionString = <your SQL Server connection string>;
entityConnectionStringBuilder.Metadata = "res://*";
MyClassDBContext a = new MyClassDBContext(entityConnectionStringBuilder.ToString());
The EntityConnectionStringBuilder class can be used to to specify provider and metadata at runtime
e.g.
var entityConnectionStringBuilder= new
EntityConnectionStringBuilder();
entityConnectionStringBuilder.Provider = "System.Data.SqlClient";
entityConnectionStringBuilder.Metadata =
"res:///Example.csdl|res:///Example.ssdl|res://*/Example.msl";
Please see for more on CSDL, SSDL & MSDL in Metadata
I followed this link
and also this one
How to use EF Code-First without an app.conf file?
Basically what I do is almost like you, create a constructor with a string and calling the base.
But I also set the provider in this constructor.
here's a example
public Context(string ConnectionString) : base(ConnectionString) {
Database.DefaultConnectionFactory = new SqlCeConnectionFactory("Oracle.DataAccess.Client");
}
That way you can specify the provider. And you won't get the provider keyword error since you don't need to specify in the connection string
Here's how I call it.
var dbCont = new ClassLibrary1.Models.Context("DATA SOURCE=xxx;PASSWORD=xxx;USER ID=xxx");
Hope that helps took me long time to find it
It's old question but maybe it will be useful for someone
var provider = (DbProviderFactory)System.Data.Entity.DbConfiguration
.DependencyResolver
.GetService(typeof(DbProviderFactory), "invariant provider name");
var conn = provider.CreateConnection();
//conn.ConnectionString = "sample connection string";
DbInterception.Dispatch.Connection.SetConnectionString(conn, new DbConnectionPropertyInterceptionContext<string>()
.WithValue("sample connection string"));
return new SampleDbContext(conn,true);
I’m currently working in c# with a SQLite database file (data.db3) which is located in the application directory. During development, an absolute path has been used and it worked fine so far. Now I’m trying to access the database by using a relative path, but that fails because of a possibly wrong connection string. The following connection string works fine and was automatically created by the ADO.Net framework.
<connectionStrings>
<add name="dataEntities"
connectionString="metadata=res://*/DataEntities.csdl|res://*/DataEntities.ssdl|res://*/DataEntities.msl;provider=System.Data.SQLite;provider connection string='data source="C:\Projekte\DataProvider\data.db3";datetimeformat=Ticks'"
providerName="System.Data.EntityClient" />
</connectionStrings>
Now I tried the following to access the database using a relative path (all fails):
dataContext = new dataEntities("Data Source=data.db3");
dataContext = new dataEntities("Data Source=.\\data.db3");
dataContext = new dataEntities("Data Source=data.db3;Version=3;DateTimeFormat=Ticks;");
dataContext = new dataEntities("metadata=res://*/DataEntities.csdl|res://*/DataEntities.ssdl|res://*/DataEntities.msl;provider=System.Data.SQLite;provider connection string='data source="data.db3";datetimeformat=Ticks'" providerName="System.Data.EntityClient");
Created by the ADO.Net framework:
public partial class dataEntities : ObjectContext
{
public dataEntities() : base("name=dataEntities", "dataEntities")
{
this.ContextOptions.LazyLoadingEnabled = true;
OnContextCreated();
}
public dataEntities(string connectionString) : base(connectionString, "dataEntities")
{
this.ContextOptions.LazyLoadingEnabled = true;
OnContextCreated();
}
/// ……
}
You might want to consider using the EntityConnectionStringBuilder class
Which will simplify at least isolating the connection string down to just the SQL part.
string baseFolder = AppDomain.CurrentDomain.BaseDirectory;
string sqlLiteConnectionString = string.Format(
"data source=\"{0}\";datetimeformat=Ticks",
Path.Combine(baseFolder, "data.db3"));
var entityConnectionString = new EntityConnectionStringBuilder
{
Metadata = "res://*",
Provider = "System.Data.EntityClient",
ProviderConnectionString = sqlLiteConnectionString,
}.ConnectionString;
var entities = new dataEntities(entityConnectionString);
The problem is NOT relative versus absolute path but the fact that you are trying to write in the application directory which is prohibited by newer Windows version for security reasons...
So this more an issue of permission/rights - depending on your OS (i.e. Windows 7...) and the user your running the app with (i.a. Administrator?) for security reasons you are not allowed to write in the application directory... if you need someplace with read+write you should use http://msdn.microsoft.com/de-de/library/system.windows.forms.application.userappdatapath.aspx
Just check whether the db is in that path -if not copy it there- and use it there...
Other locations could be ApplicationData/CommonApplicationData/LocalApplicationData from http://msdn.microsoft.com/de-de/library/14tx8hby.aspx and http://msdn.microsoft.com/de-de/library/system.environment.specialfolder.aspx