2015-06-24 11 views
5

Sto scrivendo una libreria e ho un metodo che contiene un dizionario. Il valore del dizionario è non attendibile/non sicuro, ma la chiave è affidabile e se all'utente finale è stata data la possibilità di immettere un nome di chiave arbitraria, potrebbero verificarsi "cose ​​brutte".Controlla se una stringa è una stringa letterale nota al momento della compilazione?

Così quando altri sviluppatori usano questa funzione di libreria, voglio costringerli a conoscere il nome della chiave in fase di compilazione. Quindi, qualcosa di simile sarebbe consentito:

string userInput = Console.ReadLine(); 
Dictionary<string, string> something = new Dictionary<string, string>(); 
something.Add("MyKey", userInput);  

Perché "MyKey" è una stringa letterale, noto al momento della compilazione. Ma qualcosa di simile sarebbe neanche sollevare un'eccezione di compilazione o di esecuzione:

string userInput = Console.ReadLine(); 
string userKey = Console.ReadLine(); 
Dictionary<string, string> something = new Dictionary<string, string>(); 
something.Add(userKey, userInput);  

Perché l'input dell'utente è stata utilizzata per la chiave (UserKey) e quindi era sconosciuto al momento della compilazione.

Ho cercato in GetType() e non c'è nulla che distingua realmente tra una stringa letterale e una stringa creata in fase di esecuzione.

+1

Non è possibile creare (o riutilizzare) un metodo che può visualizzare un valore di stringa e disinfettarlo o determinare se è sicuro? Quindi ti proteggi dai letterali di stringa problematici e consenti l'uso di valori di sicurezza non letterali. – Servy

+3

Le espressioni negli attributi sono garantite come costanti, se ricordo correttamente. Quindi, potresti anche provare a creare un attributo personalizzato e avere codice client che ti passa un membro o un tipo decorato con quell'attributo. Quindi, puoi leggere i metadati da quell'attributo. (Anche se potrebbero esserci trucchi di riflessione che possono simulare ciò, usando assiemi dinamici.) –

+0

Il problema è che la chiave è intrinsecamente insicura. Ecco perché non dovrebbe mai venire dall'input dell'utente in nessuna circostanza. In questo momento le mie opzioni sono per verificare se è noto in fase di compilazione O per hardcode nella mia libreria in modo che non possa essere modificato da sviluppatori esterni. –

risposta

2

Mi vengono in mente due modi possibili, sia utilizzando la riflessione:

  • Ricevi un tipo che contiene una chiave come un campo costante di stringa. There is another answer on how to get constant fields from a type.

  • Definire un attributo che memorizza una stringa. Gli argomenti degli attributi devono essere espressioni costanti. È possibile ricevere un tipo o un membro decorato con tale attributo ed estrarre la chiave da detto attributo.


pena ricordare che entrambi possono probabilmente essere falsificato dal codice client. Ad esempio, è possibile utilizzare dynamic assemblies o la classe System.ComponentModel.TypeDescriptor.

2

È possibile utilizzare string.IsInterned per determinare se la stringa è internata. Tutti i valori letterali di tempo di compilazione verranno internati per impostazione predefinita, ma puoi disattivarlo utilizzando un flag del compilatore.

Questo è un controllo di runtime, non un controllo del tempo di compilazione.

Si noti inoltre che letterali non in tempo di compilazione possono essere internati utilizzando la funzione di string.Intern, così tecnicamente si può avere le stringhe letterali non passano il test. Ma se il tuo programma non sta eseguendo o meno stringhe interne in qualsiasi momento, o solo le stringhe interne che conosci sono sicure, allora potrebbe funzionare.


Un'altra opzione, se tutte le chiavi dovrebbero essere noti al momento della compilazione, è quello di non avere la chiave sia una stringa a tutti. Ad esempio, fai in modo che la chiave sia un'enumerazione in modo che tu sappia che gli unici valori che possono mai essere usati come chiavi sono in un elenco che hai risolto in fase di compilazione.

+0

In alternativa, si potrebbe ricevere un valore di tipo 'enum' e utilizzare il nome del valore passato come stringa. Ciò limiterebbe le stringhe ai caratteri che sarebbero validi all'interno del nome di un valore enum, ma consentirebbero al codice client di utilizzare enumerazioni arbitrarie che erano definite all'interno del codice. – supercat

1

Non testato in modo approfondito, ma utilizzando le espressioni anziché i valori diretti, è possibile verificare il tipo di valore passato. per esempio.

void Add(Expression<Func<string>> fn) 
{ 
     if (fn.Body.NodeType != ExpressionType.Constant) 
      throw new ArgumentException("Only literal strings are allowed"); 

     //and extra check if the value itself is interned 
     var val = fn.Compile()(); 
     if (string.IsInterned(val) == null) 
      throw new ArgumentException("Only literal strings are allowed"); 

} 

Poi lo sviluppatore deve passare l'argomento come un lambda:

Add(() => "Test"); //Valid 
    string somestring = "Test"; 
    Add(() => somestring); //Error 
+0

È piuttosto facile costruire un'espressione in fase di esecuzione di una lambada che restituisce un 'Expression.Constant' che restituisce un valore di stringa calcolato in fase di esecuzione. – Servy

+0

Considerati i requisiti della domanda (e non assumendo intenzioni malevole dal codice client), penso che questa sia la soluzione migliore, essendo sia facile da implementare che non troppo facile da aggirare "accidentalmente". –

+0

@Servy Vero, ma come hai detto tu stesso, se l'intenzione degli sviluppatori è lì per aggirare la restrizione, può farlo. Ancora un buon punto. Aggiunto un controllo extra su internato per renderlo più difficile. Mi chiedo se puoi controllare se il lambda è stato creato dinamicamente –

Problemi correlati