Я пытаюсь создать новый логин SQL на Via C #. Я пробовал следующее, чтобы установить параметры запроса:

using (SqlConnection connection = new SqlConnection(sqlServerConnString))
{
    connection.Open();

    string addLogin = "CREATE LOGIN [@databaseUserId] WITH PASSWORD = '@databasePassword';";
    using (SqlCommand command = new SqlCommand(addLogin, connection))
    {
        // Attempt 1.
        command.Parameters.AddWithValue("@databaseUserId", databaseUserId);
        command.Parameters.AddWithValue("@databasePassword", databasePassword);

        // Attempt 2.
        command.Parameters.Add(new SqlParameter("@databaseUserId", databaseUserId));
        command.Parameters.Add(new SqlParameter("@databasePassword", databasePassword));

        // Attempt 3.
        command.Parameters.Add("databaseUserId", System.Data.SqlDbType.NVarChar).Value = databaseUserId;
        command.Parameters.Add("databasePassword", System.Data.SqlDbType.NVarChar).Value = databasePassword;

        command.ExecuteNonQuery();
    }
}

Каждая «попытка» проводилась индивидуально.

Независимо от того, какую версию я пытаюсь, команда завершается без ошибок. Однако, когда я проверяю логины на SQL Server, я вижу, что он создал пользователь с именем "@databaseuserid".

Я могу подтвердить, что переменные базы данных и базы данных DatabasePasswass являются обеим ненульными.

Что мне не хватает?

1
James 2 Окт 2019 в 11:51

1 ответ

Лучший ответ

Я не разработчик C #, поэтому я доверять , что код, который вы имеете правильно.

Как я указываю в моем комментарии ", вы не можете использовать переменную для замены строкового литерала. Вышеуказанное будет пытаться создать вход с именем @databaseUserId не значение @databaseUserId. " Он также установил бы пароль этого входа в систему, чтобы быть строкой '@databasePassword' (опять же, не значение @databasePassword).

Вам нужно будет использовать динамический SQL для достижения этого в вашем утверждении. Я верю это будет работать, но у меня нет способа тестирования.

using (SqlConnection connection = new SqlConnection(sqlServerConnString))
{
    connection.Open();

    //string addLogin = "CREATE LOGIN [@databaseUserId] WITH PASSWORD = '@databasePassword';";
    string addLogin = "DECLARE @SQL nvarchar(MAX) = N'CREATE LOGIN ' + QUOTENAME(@databaseUserId) + N' WITH PASSWORD = N' + QUOTENAME(@databasePassword,'''') + N';'; EXEC sp_executesql @SQL;";
    using (SqlCommand command = new SqlCommand(addLogin, connection))
    {

        command.Parameters.Add("databaseUserId", System.Data.SqlDbType.NVarChar,128).Value = databaseUserId;
        command.Parameters.Add("databasePassword", System.Data.SqlDbType.NVarChar,128).Value = databasePassword;

        command.ExecuteNonQuery();
    }
}

Обратите внимание на ВАЖНОЕ Использование QUOTENAME Здесь и что вызов SQL, который вы по-прежнему параметризированы на стороне приложений. Как вам нужно использовать буквальные строки для значений, вы должны ввести их (что является обычно нахмущенным). QUOTENAME Правильно цитирует ваши строки, означающие, что воздействие на инъекцию значительно сокращено (некоторые, как простое из этого сейчас будет инъекция «иммунитет»).

Итак, если кто-то попытался ввести с именем логина, то символы будут сбежать. Например, значение N'L] WITH PASSWORD = 'abc123!"£'; ALTER SERVER ROLE sysadmin ADD MEMBER L;--' значение будет указано значение значение [L]] WITH PASSWORD = 'abc123!"£'; ALTER SERVER ROLE sysadmin ADD MEMBER L;--], и на самом деле будет создано вход с этим (глупым) именем (при условии, что прилагается пароль значение). Для пароля, поскольку он должен быть строковым литералом, а не буквалом, я использую 2-й параметр, чтобы сообщить SQL Server, какой персонаж цитировать строку с.

2
Larnu 2 Окт 2019 в 09:23