使用C#在SQL Server中清理用户提供的列名。

huangapple go评论59阅读模式
英文:

Sanitize user given column name in SQL Server via C#

问题

以下是您要翻译的代码部分:

public bool Set(int id, string columnName, object value)
{
    /// <summary>
    /// 此函数将给定的列名设置为给定的值,用于给定的 id。
    /// 如果操作成功,则返回 true,否则返回 false。
    /// </summary>

    string query = "UPDATE " + TableName + " SET " + columnName + " = @value WHERE id = @id";

    using (SqlConnection connection = new SqlConnection(ConnectionString))
    {
        using (SqlCommand command = new SqlCommand(query, connection))
        {
            connection.Open();

            command.Parameters.AddWithValue("@id", id);

    ....

请注意,这段代码已经被翻译为中文。

英文:

In C# I have the following function:

public bool Set(int id, string columnName, object value)
{
    /// <summary>
    /// This function sets the given column name to the given value for the given id.
    /// Returns true if the operation was successful, false otherwise.
    /// </summary>

    string query = "UPDATE " + TableName + " SET " + columnName + " = @value WHERE id = @id";

    using (SqlConnection connection = new SqlConnection(ConnectionString))
    {
        using (SqlCommand command = new SqlCommand(query, connection))
        {
            connection.Open();

            command.Parameters.AddWithValue("@id", id);

    ....

I cannot set columnName as an parameter, that does not work. However the column name should be a user input. How can I make this sanitize?

I tried making columnName an variable like this @columnName and setting it later, however, this does not work because I think SQL Server interprets this as a string later, not a column object.

答案1

得分: 1

我感觉无论如何要写点东西,因为我不怎么写C#。但我确实想重申评论中的一切仍然是真实的;只是不要这样做。

话虽如此,如果您可以传递对象名称、ID和值作为参数,然后创建一个动态SQL语句,检查系统对象,可以做以下操作。这会导致类似于以下的一些(可怕的怪物):

String sql = @"DECLARE @SQL nvarchar(MAX);
               SELECT @SQL = N'UPDATE ' + QUOTENAME(s.name) + N'.' + QUOTENAME(t.name) + N' SET ' + QUOTENAME(c.name) + N' = @Value WHERE id = @Id;';
               FROM sys.schemas s
                    JOIN sys.tables t ON s.schema_id = t.schema_id
                    JOIN sys.columns c ON t.object_id = c.object_id
               WHERE s.name = @Schema
                 AND t.name = @Table
                 AND c.name = @Column;
               IF @SQL IS NOT NULL
                   EXEC sys.sp_executesql @SQL, N'@Id int, @Value nvarchar(4000)', @Id, @Value;
               ELSE
                   THROW 56725, N'找不到要更新的对象。',16;";
try
{   
    using (SqlCommand command = new SqlCommand(sql, connection))
    {
        
        command.Parameters.Add("@Schema",SqlDbType.NVarChar,128).Value = schemaName;
        command.Parameters.Add("@Table",SqlDbType.NVarChar,128).Value = tableName;
        command.Parameters.Add("@Column",SqlDbType.NVarChar,128).Value = columnName;
        command.Parameters.Add("@Id",SqlDbType.Int).Value = idValue;
        command.Parameters.Add("@Value",SqlDbType.NVarChar,128).Value = updateValue;
        command.ExecuteNonQuery();
        Console.WriteLine("更新完成。");

    }   
}
catch (SqlException e)
{
    //引发适当的异常,我只是将错误输出到控制台
    Console.WriteLine(e.ToString());
    Console.WriteLine("更新失败。");
}
英文:

I felt like writing something for this anyway, because I don't write a lot of C#. I do, however, want to repeat that everything in the comments is still true; just don't do this.

That being said, what you can do if pass parameters for the object names, the id and the value, and then create a dynamic SQL statement, checking against the system objects. This results in some (awful abomination) like this:

String sql = @"DECLARE @SQL nvarchar(MAX);
               SELECT @SQL = N'UPDATE ' + QUOTENAME(s.name) + N'.' + QUOTENAME(t.name) + N' SET ' + QUOTENAME(c.name) + N' = @Value WHERE id = @Id;'
               FROM sys.schemas s
                    JOIN sys.tables t ON s.schema_id = t.schema_id
                    JOIN sys.columns c ON t.object_id = c.object_id
               WHERE s.name = @Schema
                 AND t.name = @Table
                 AND c.name = @Column;
               IF @SQL IS NOT NULL
                   EXEC sys.sp_executesql @SQL, N'@Id int, @Value nvarchar(4000)', @Id, @Value;
               ELSE
                   THROW 56725, N'Object not found for update.',16;";
try
{   
    using (SqlCommand command = new SqlCommand(sql, connection))
    {
        
        command.Parameters.Add("@Schema",SqlDbType.NVarChar,128).Value = schemaName;
        command.Parameters.Add("@Table",SqlDbType.NVarChar,128).Value = tableName;
        command.Parameters.Add("@Column",SqlDbType.NVarChar,128).Value = columnName;
        command.Parameters.Add("@Id",SqlDbType.Int).Value = idValue;
        command.Parameters.Add("@Value",SqlDbType.NVarChar,128).Value = updateValue;
        command.ExecuteNonQuery();
        Console.WriteLine("Update Complete.");

    }   
}
catch (SqlException e)
{
    //Raise appropriate exception, I just dump errors to the console
    Console.WriteLine(e.ToString());
    Console.WriteLine("Update Failed.");
}

答案2

得分: 0

以下是代码部分的翻译:

一个简单的“消毒”输入的方法是尝试在系统表中查找表名和列名,然后使用参数化查询进行操作。

SELECT *
FROM SYS.SYSOBJECTS SO
JOIN SYS.SYSCOLUMNS SC ON SC.id = SO.id
WHERE SO.XTYPE = N'U'
AND SO.NAME = N'TABLE NAME'
AND SC.NAME = N'COLUMN NAME';

免责声明:我不太喜欢按照你目前的方式做事,也不太喜欢处理需要消毒的用户输入。你需要首先质疑这些事情。
英文:

A simple way to "sanitize" your input is to try to find the table name and column names in the system tables and do it using a parametrized query.

SELECT *
FROM SYS.SYSOBJECTS SO
JOIN SYS.SYSCOLUMNS SC ON SC.id = SO.id
WHERE SO.XTYPE = N'U'
AND SO.NAME = N'TABLE NAME'
AND SC.NAME = N'COLUMN NAME'

disclaimer: I'm not a big fan of doing things the way you are doing nor doing stuff that needs sanitized user inputs. You need to question those things first.

huangapple
  • 本文由 发表于 2023年3月3日 18:52:15
  • 转载请务必保留本文链接:https://go.coder-hub.com/75626122.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定