2017-08-22 3 views
2

Je dois créer un script SQL Server 2016 en utilisant la concaténation de chaînes à partir de C# (le script sera exécuté via ADO.NET). Je ne peux pas utiliser les paramètres de requête, ce que je ferais normalement, car le script est plus un script d'installation et contient des instructions non paramétrables.Noms d'objets de base de données d'échappement

Quelle est la chemin à parcourir pour échapper à des noms tels que:

"ALTER DATABASE " + Escape(databaseName) + " ADD ..." 

n'est pas vulnérable à l'injection SQL? Comment mettre en œuvre Escape? Actuellement j'utilise des crochets autour de tous les noms, cependant, cela ne suffit pas bien sûr ...

+0

vous pouvez valider les variables vérifier pour certains donner des allures comme []; et "etc – BugFinder

+1

@BugFinder, tout moche et faux, et horrible, et [insérer plus d'adjectifs ici], il est parfaitement * légal * d'avoir une table nommée' ['dans SQL Server = ( – Rob

+0

vrai mais vous pourriez en faire une exigence pour votre application :) que vous n'avez pas: D – BugFinder

Répondre

2

Vous pouvez utiliser la fonction intégrée QUOTENAME SQL Server pour cela. Comme il semble que vous construisiez le script en C#, avant de l'exécuter en SQL, vous voudrez probablement avoir une fonction C# qui fait un voyage dans la base de données pour faire cela, peut-être stockant également les valeurs résultantes dans un Dictionary<string, string> pour éliminer les allers-retours pour les chaînes qui ont déjà été citées.

Par exemple:

private Dictionary<string, string> _quotedNames = new Dictionary<string, string>(); 

private string GetSqlQuotedName(string name) 
{ 
    if (!_quotedNames.ContainsKey(name)) 
    { 
     _quotedNames[name] = GetSqlQuotedNameFromSqlServer(name); 
    } 

    return _quotedNames[name]; 
} 

private string GetSqlQuotedNameFromSqlServer(string name) 
{ 
    /// Code here to use your Data access method of choice to basically execute 
    /// SELECT QUOTENAME(name) and return it 
} 

En fait, juste pour montrer l'utilisation des classes dans l'espace de noms System.Data.SqlClient, voici une classe qui exécute ce comportement, lorsqu'il est administré une chaîne de connexion à utiliser pour parler à SQL Server:

public class SqlNameEscaper 
{ 
    private Dictionary<string, string> _quotedNames = new Dictionary<string, string>(); 
    private string _connectionString = string.Empty; 

    public SqlNameEscaper(string connectionString) 
    { 
     _connectionString = connectionString; 
    } 

    public string GetSqlQuotedName(string name) 
    { 
     if (!_quotedNames.ContainsKey(name)) 
     { 
      _quotedNames[name] = GetSqlQuotedNameFromSqlServer(name); 
     } 

     return _quotedNames[name]; 
    } 

    private string GetSqlQuotedNameFromSqlServer(string name) 
    { 
     using (var connection = new SqlConnection(_connectionString)) 
     { 
      connection.Open(); 
      using (var command = new SqlCommand("SELECT QUOTENAME(@name)", connection)) 
      { 
       command.Parameters.AddWithValue("@name", name); 
       var result = command.ExecuteScalar(); 

       return result.ToString(); 
      } 
     } 
    } 
} 

cela peut ensuite être appelé en faisant ceci:

var sne = new SqlNameEscaper(@"CONNECTION_STRING_HERE"); 
var bracket = sne.GetSqlQuotedName("["); 

Ou, dans le contexte de votre exemple:

var sqlNameEscaper = new SqlNameEscaper(@"CONNECTION_STRING_HERE"); 
var text = "ALTER DATABASE " + sqlNameEscaper.GetSqlQuotedName(databaseName) + " ADD ..."; 

Il y a aussi une question sur dba.stackexchange.com qui vaut la peine d'avoir une lecture de ce sujet même: Should we still be using QUOTENAME to protect from injection attacks?

+0

Est-ce que QUOTENAME est associé à une liste noire? aussi utilisable pour les "paramètres", comme dans ADD FILE (name = '', ...) '? –

+0

@DR - peut-être, bien que vous ayez plus de chances de commencer à atteindre la limite de 128 caractères que le d mentions d'information. Si vous utilisez la (les) fonction (s) wrapper (s) que j'ai proposée ci-dessus, alors la chaîne résultante peut être utilisée n'importe où! =) – Rob