2015-07-01 8 views

risposta

11

Il prefisso sql sblocca un StringContext in cui è possibile impostare i parametri SQL. Non esiste un parametro SQL per un elenco, quindi puoi facilmente finire per aprirti all'iniezione SQL qui se non stai attento. Ci sono alcuni buoni (e alcuni pericolosi) suggerimenti su come gestire questo problema con SQLServer su this question. Hai un paio di opzioni:

La cosa migliore è probabilmente quello di utilizzare l'operatore #$ insieme mkString interpolare SQL dinamico:

val sql = sql"""SELECT * FROM coffee WHERE id IN (#${ids.mkString(",")})""" 

Questo non usa correttamente i parametri e quindi potrebbe essere aperto a sql- iniezione e altri problemi.

Un'altra opzione è quella di utilizzare regolare l'interpolazione di stringa e mkString per creare l'istruzione:

val query = s"""SELECT * FROM coffee WHERE id IN (${ids.mkString(",")})""" 
StaticQuery.queryNA[Coffee](query) 

Questo è essenzialmente lo stesso approccio utilizzando #$, ma potrebbe essere più flessibili nel caso generale.

Se la vulnerabilità di SQL-injection è una delle principali preoccupazioni (ad esempio se gli elementi di ids sono forniti dall'utente), è possibile creare una query con un parametro per ciascun elemento di ids. Allora è necessario fornire un campione personalizzato SetParameter in modo che chiazza di petrolio può trasformare il List in parametri:

implicit val setStringListParameter = new SetParameter[List[String]]{ 
    def apply(v1: List[String], v2: PositionedParameters): Unit = { 
     v1.foreach(v2.setString) 
    } 
} 

val idsInClause = List.fill(ids.length)("?").mkString("(", ",", ")") 
val query = s"""SELECT * FROM coffee WHERE id IN ($idsInClause)""" 
Q.query[List[String], String](query).apply(ids).list(s) 

Dal momento che il ids sono Ints, questo è probabilmente meno di una preoccupazione, ma se si preferisce questo metodo, avrebbe solo bisogno di cambiare il setStringListParameter di utilizzare Int invece di String:

+3

Se 'ids' ha tipo' List [Int] 'Non riesco a vedere come sql injection è possibile anche se sono forniti dall'utente. – Daenyth

+1

@Daenyth E 'sicuramente meno preoccupante (anche se a volte l'integer SQL può essere un problema, causando una divisione per zero o altre eccezioni, e quindi sfruttando lo stato fallito - Google "sql injection interi"). Ma direi che è meglio usare i parametri per evitare problemi lungo la strada (ad esempio, se un altro sviluppatore modificasse il tipo in 'String' down per adattare i nuovi tipi di ID che includono alcuni caratteri). Stavo davvero coprendo il caso quando qui c'è un 'String'. –

+0

Grazie per la risposta Ben! Molto informativo delle soluzioni con possibili vulnerabilità. Sono comunque d'accordo con @Daenyth che non si può iniettare sql con un tipo intero esplicito. –

3
val ids = List(610113193610210035L, 220702198208189710L) 

    implicit object SetListLong extends SetParameter[List[Long]] { 
    def apply(vList: List[Long], pp: PositionedParameters) { 
     vList.foreach(pp.setLong) 
    } 
    } 

    val select = sql""" 
     select idnum from idnum_0 
     where idnum in ($ids#${",?" * (ids.size - 1)}) 
    """.as[Long] 

@ Ben Reich è giusto. questo è un altro codice di esempio, test su slick 3.1.0.

($ids#${",?" * (ids.size - 1)})

1

Anche se questo non è la risposta universale, e non può essere quello che l'autore ha voluto, ho ancora voglia di farlo notare a chi considera questa domanda.

Alcuni backend DB supportano tipi di array e sono presenti estensioni a Slick che consentono di impostare questi tipi di array nelle interpolazioni.

Per esempio, Postgres ha la sintassi where column = any(array), e con slick-pg è possibile utilizzare questa sintassi in questo modo:

def query(ids: Seq[Long]) = db.run(sql"select * from table where ids = any($ids)".as[Long]) 

Questo porta una sintassi molto più pulito, che è più amichevole per la cache dichiarazione compilatore e anche al sicuro da Iniezioni SQL e rischio generale di creare un SQL non valido con la sintassi di interpolazione #$var.

Problemi correlati