Correct escaping of delimited identifers in SQL Server without using QUOTENAME

后端 未结 2 1205
滥情空心
滥情空心 2021-01-02 05:34

Is there anything else that the code must do to sanitize identifiers (table, view, column) other than to wrap them in double quotation marks and \"double up\" double

相关标签:
2条回答
  • 2021-01-02 06:23

    Can you not just use [ and ] delimiters instead of quotes (single or double)?

    Identifiers should never really contain any quotes (unless you're more unlucky than now) so you remove the normal use factor of quotes in names etc

    Edit:

    But if the calls to the ORM are already parameterised, you don't need to worry about it, no? Using [ and ] removes the need for complex escaping in c# strings

    0 讨论(0)
  • 2021-01-02 06:26

    Your QuoteName function needs to check the length, because the T-SQL QUOTENAME function specifies the maximum length it returns. Using your example:

    String.Format(@"declare @delimitedIdentifier nvarchar(258);
    set @delimitedIdentifier = {0};", QuoteName(identifier));
    

    If QuoteName(identifier) is longer than 258 characters, it will be silently truncated when assigned to @delimitedIdentifier. When that happens, you open up the possibility for @delimitedIdentifier to be escaped improperly.

    There is an MSDN article by Bala Neerumalla, a "security software developer at Microsoft", that explains the topic in more depth. The article also contains the closest thing I have found to "definitive documentation about how to escape quoted identifiers in SQL Server":

    The escaping mechanism is simply doubling up the occurrences of right square brackets. You don't need to do anything with other characters, including left square brackets.

    This is the C# code I am currently using:

    /// <summary>
    /// Returns a string with the delimiters added to make the input string
    /// a valid SQL Server delimited identifier. Brackets are used as the
    /// delimiter. Unlike the T-SQL version, an ArgumentException is thrown
    /// instead of returning a null for invalid arguments.
    /// </summary>
    /// <param name="name">sysname, limited to 128 characters.</param>
    /// <returns>An escaped identifier, no longer than 258 characters.</returns>
    public static string QuoteName(string name) { return QuoteName(name, '['); }
    
    /// <summary>
    /// Returns a string with the delimiters added to make the input string
    /// a valid SQL Server delimited identifier. Unlike the T-SQL version,
    /// an ArgumentException is thrown instead of returning a null for
    /// invalid arguments.
    /// </summary>
    /// <param name="name">sysname, limited to 128 characters.</param>
    /// <param name="quoteCharacter">Can be a single quotation mark ( ' ), a
    /// left or right bracket ( [] ), or a double quotation mark ( " ).</param>
    /// <returns>An escaped identifier, no longer than 258 characters.</returns>
    public static string QuoteName(string name, char quoteCharacter) {
        name = name ?? String.Empty;
        const int sysnameLength = 128;
        if (name.Length > sysnameLength) {
            throw new ArgumentException(String.Format(
                "name is longer than {0} characters", sysnameLength));
        }
        switch (quoteCharacter) {
            case '\'':
                return String.Format("'{0}'", name.Replace("'", "''"));
            case '"':
                return String.Format("\"{0}\"", name.Replace("\"", "\"\""));
            case '[':
            case ']':
                return String.Format("[{0}]", name.Replace("]", "]]"));
            default:
                throw new ArgumentException(
                    "quoteCharacter must be one of: ', \", [, or ]");
        }
    }
    
    0 讨论(0)
提交回复
热议问题