I can't figure out what I'm doing wrong here.

I want to allow the user to name the database on their own. Can you use parameters in a situation where the command is simple like:

CREATE DATABASE @DatabaseName

I've been trying forever and it just keeps creating a database called @DatabaseName, it just ignores the parameter.

Dani AI

Generated

You are running into a SQL Server rule: parameters can only stand in for data values, not identifiers (like database, table, or column names). So @DatabaseName inside CREATE DATABASE is treated as literal text, which is why you see a database named @DatabaseName. was on the right track about composing the statement, but you need to compose it safely. The fix is to build dynamic SQL and wrap the user input with QUOTENAME, which turns a validated name into a properly delimited identifier and prevents breaking out of the command. See QUOTENAME.

Example pattern you can drop into your command (note: pass the user input as a parameter, then construct the dynamic SQL on the server):

using (var cmd = new SqlCommand(@"
IF DB_ID(@n) IS NOT NULL
    RAISERROR('Database already exists.', 16, 1);
DECLARE @sql nvarchar(max) = N'CREATE DATABASE ' + QUOTENAME(@n);
EXEC(@sql);", connection))
{
    cmd.Parameters.Add("@n", SqlDbType.NVarChar, 128).Value = dbName; // sysname length
    connection.Open();
    cmd.ExecuteNonQuery();
}

Hardening tips that complement ’s points:

  • Whitelist the name before you send it (e.g., letters, digits, underscore) and cap it at 128 chars (SQL Server identifier limit; sysname is nvarchar(128)). See .
  • Connect to master when creating a new database to avoid issues if the target DB does not exist yet.
  • Verify permissions first; the login needs CREATE ANY DATABASE or membership in dbcreator or sysadmin. You can check with SELECT HAS_PERMS_BY_NAME(NULL, NULL, 'CREATE ANY DATABASE'); See CREATE DATABASE permissions.

If you prefer to avoid hand-rolled T-SQL, .NET’s SMO API can create databases and handles quoting for you (SMO Database.Create).

Recommended Answers

All 9 Replies

The problem here is that the @DatabaseName in the command is not an SQL datatype field. You instead need to build your query like so:

public void CreateDatabase(string databaseName)
{
   string query = String.Format("CREATE DATABASE {0}", databaseName);
   // now execute the query using DbCommand or similar class...

}

That would work, but would that prevent injection attacks?

That's indeed a good question, but I would like to ask you the same when you use @DatabaseName? Does this prevent injection attacks?
If it does, how; and can you use the same method to prevent attacks on code suggested by darkagn?

I guess the rest of the code should be present.

Yes, using SQL parameters does prevent injection attacks...at least thats what it is advertised as doing.
(MSDN: Sql Injection)
(MSDN: SqlParameter Class)

This code works:

//create the string
string sqlstring = "UPDATE accounts SET @company WHERE id = @id";

//create the command object
SqlCommand cmd = new SqlCommand(sqlstring, connection);

//set the SQL Parameters
cmd.Parameters.Add(new SqlParameter("@id", (object)id.Text));
cmd.Parameters.Add(new SqlParameter("@company", (object)company.Text));

//execute the command
connection.Open();
cmd.ExecuteNonQuery();
connection.Close();

But this does not work:

//create the string
string sqlstring = "CREATE DATABASE @DatabaseName";

//create the command object
SqlCommand cmd = new SqlCommand(sqlstring, connection);

//set the SQL Parameters
cmd.Parameters.Add(new SqlParameter("@DatabaseName", (object)dbName.Text));

//execute the command
connection.Open();
cmd.ExecuteNonQuery();
connection.Close();

First off, SQL injection is prevented in parameters because the code is formatted to match the parameter's datatype. This is designed to be passed as a parameter in a stored procedure.

So, you pass it @DBName SYSNAME in the parameter. The value can be:
"YouLose;drop database msdb;drop database master"

That can't be a sql injection because a line in the sproc that says
CREATE DATABASE @DBName
WOULD blow up.

IF you are using dynamic SQL, it is YOUR RESPONSIBILITY to prevent a SQL injection attack. The same as if you are building a dynamic SQL command like the other suggestion made.

So, keep your procedure, but in your C# code make sure the data entered is alphanumeric, spaces or known characters like "_" or "-" and the string is no longer than 254 characters. (It should work up to 256, but anyone who wants a DB that long is up to something. Maybe limit it to 60?)

In your sproc code, execute dynamic SQL:
[SQL]
DECLARE @dynCmd NVARCHAR(300) = 'CREATE DATABASE '
IF EXISTS( SELECT * FROM sys.databases WHERE NAME = @DBName)
RAISERROR('The DB name you picked already exists', 16, 1)
ELSE
EXEC (@dynCmd)
[/SQL]

Don't check in C# and my first example wouldn't cause SQL injection, but there are still ways you could end up with missing DBs if your account is allowed to do so.

Oh yea. If your connection account doesn't have authority to create databases, that would explain why the straight dynamic create command doesn't work. Horray, you at least have something set up properly. (Properly set up, your account shouldn't be allowed to do anything BUT call stored procedures and that is because the sproc specifically granted EXEC permission. Hardly anyone properly sets up the security model.)
If you create the stored procedure in an account that does have authority to create DBs, your program would be allowed to create them in the sproc. (With minor exceptions.)

I am really surprised that you said "UPDATE accounts SET @company WHERE id = @id" works. I'd believe "UPDATE accounts SET company=@company WHERE id = @id"

Now for your @company string -> "junk';truncate table accounts;--"
This used to work as an attack in the old style because the formatting was
CMD = "UPDATE accounts SET company='" + Str + "' WHERE id = " + id.ToString()

The command SQL would see in the new format is
UPDATE accounts SET company='junk'';truncate table accounts;--' WHERE id = 1234

Even though this really is a dynamic query, you are maintaining the relationship of working with a string in SQL being set to a string in your parameter list so a SQL attack can't happen. I wonder what happens if you violate that principle?

create a table accountx with a company field, add a few records where company is not "junk"
Try "UPDATE @accounts SET company=@company WHERE id = @id"

In your @accounts string "accountx' SET company='junk';--"

I'm betting every row in the table is "junk". If it is, then you proved SQL insertion is possible.
If it isn't, I'm going by memory so interactively try:
UPDATE 'accountx' SET company='junk'
If it blows up, then my memory is screwed up. If not, wow, I'm impressed and really surprised with C#.

kplcjl,

Your right. I was a bit tired and frustrated when I wrote that.

The problem was not creating a database, it was that the name of the database was incorrect. It's okay though, I figured it out and everything is working now.

Thanks for the help!

This is marked as unsolved. It is your responsibility to post it as solved.

Just wanted to make the point that C# has done a lot of good work to remove SQL insertion but it is possible when dynamic SQL is involved. (After reading what I wrote earlier, I realize that it was wrong. Because of the unbalanced quotes, it wouldn't work as an attack. There still are places where a dynamic SQL command could be attacked.

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.