2012-02-23 14 views
14

Estoy desarrollando una aplicación web usando F #. Pensando en proteger las cadenas de entrada del usuario de SQL, XSS y otras vulnerabilidades.Restricciones de tiempo de compilación para cadenas en F #, similares a las Unidades de medida: ¿es posible?

En dos palabras, necesito algunas limitaciones de tiempo de compilación que me permitiera discriminar cadenas de civil de aquellos que representan SQL, URL, XSS, XHTML, etc.

Muchas lenguas tienen, por ejemplo, La función de interpolación de cuerdas nativa de Ruby #{...}.
Con F #, parece que las Unidades de medida funcionan muy bien, pero solo están disponibles para los tipos numéricos.
Existen varias soluciones que emplean runtime UoM (link), sin embargo, creo que es una sobrecarga para mi objetivo.

He mirado en FSharpPowerPack, y parece muy posible llegar a algo similar para cadenas:

[<MeasureAnnotatedAbbreviation>] type string<[<Measure>] 'u> = string 
// Similarly to Core.LanguagePrimitives.IntrinsicFunctions.retype 
[<NoDynamicInvocation>] 
let inline retype (x:'T) : 'U = (# "" x : 'U #) 
let StringWithMeasure (s: string) : string<'u> = retype s 

[<Measure>] type plain 
let fromPlain (s: string<plain>) : string = 
    // of course, this one should be implemented properly 
    // by invalidating special characters and then assigning a proper UoM 
    retype s 

// Supposedly populated from user input 
let userName:string<plain> = StringWithMeasure "John'); DROP TABLE Users; --" 
// the following line does not compile 
let sql1 = sprintf "SELECT * FROM Users WHERE name='%s';" userName 
// the following line compiles fine 
let sql2 = sprintf "SELECT * FROM Users WHERE name='%s';" (fromPlain userName) 

Nota: Es sólo una muestra; no sugiera usar SqlParameter. :-)

Mis preguntas son: ¿Hay una biblioteca decente que lo haga? ¿Hay alguna posibilidad de agregar azúcar sintaxis?
Gracias.

Actualización 1: Necesito restricciones de tiempo de compilación, gracias Daniel.

Actualización 2: Estoy tratando de evitar cualquier sobrecarga del tiempo de ejecución (tuplas, estructuras, uniones discriminadas, etc.).

+2

Ver http://blog.moertel.com/articles/2006/10/18/a-type-based-solution-to-the-strings-problem para un interesante Haskell asumir el problema. – kvb

+1

Bueno, si intentas implementar esto, ¡me interesaría verlo! – Benjol

+2

@kvb, tu enlace parece estar muerto ... déjame poner un enlace de trabajo solo para mí :) http://blog.moertel.com/posts/2006-10-18-a-type-based- solution-to-the-strings-problem.html – moudrick

Respuesta

2

Es difícil decir lo que estás tratando de hacer. Dijiste que "necesitas algunas restricciones de tiempo de ejecución", pero esperas resolver esto con unidades de medida, que son estrictamente de tiempo de compilación. Creo que la solución fácil es crear clases SafeXXXString (donde XXX es Sql, Xml, etc.) que validan su entrada.

type SafeSqlString(sql) = 
    do 
    //check `sql` for injection, etc. 
    //raise exception if validation fails 
    member __.Sql = sql 

Proporciona seguridad en tiempo de ejecución, no de tiempo de compilación. Pero es simple, autodocumentado, y no requiere leer la fuente del compilador F # para que funcione.

Pero, para responder a su pregunta, no veo ninguna manera de hacer esto con las unidades de medida. En lo que respecta al azúcar sintáctico, es posible que puedas encapsularlo en una mónada, pero creo que lo hará más torpe, no menos.

+0

Pero su pregunta menciona que necesita restricciones de tiempo de ejecución, y por lo tanto la confusión. Sin embargo, intenté abordar ambas posibilidades. – Daniel

+0

¡Oh, lo siento, gracias por señalar esto! – bytebuster

3

En teoría es posible utilizar 'unidades' para proporcionar diversos tipos de comprobaciones en tiempo de compilación en cadenas (es esta cadena 'contaminada' la entrada del usuario, o desinfectados? Es este nombre de archivo relativo o absoluto? ...)

En la práctica, personalmente no he encontrado que sea demasiado práctico, ya que hay tantas API existentes que usan 'cadena' que tiene que ejercitar una tonelada de cuidado y conversiones manuales de datos de plomería de aquí para allá.

Creo que las "cadenas" son una gran fuente de errores, y ese tipo de sistemas que se ocupan de la corrupción/canonicalización/etc. en cadenas será uno de los próximos saltos en tipeo estático para reducir errores, pero creo que es como un horizonte de 15 años. Sin embargo, me interesaría que las personas que intentan un enfoque con F # UoM vean si obtienen algún beneficio.

+0

Tal vez no lo entiendo completamente, pero ¿qué proporcionaría un enfoque de la UM a esto más allá de las clases contenedoras con validación? El nivel de seguridad de tipo parece ser el mismo, pero el último es (actualmente) mucho más fácil de implementar. – Daniel

+0

Daniel: las razones son muy similares a las de los tipos numéricos. UoM previene las variables de mezcla accidental que tienen diferentes _purposes_ aún almacenados como el mismo _runtime type_. Hay una explicación perfecta del problema del "olor del código", escrito por Joel http://www.joelonsoftware.com/articles/Wrong.html – bytebuster

+0

Entiendo que no quiero mezclar diferentes tipos de cadenas, pero podrías lograr eso de varias maneras (clases de contenedor , etc.) Simplemente no tengo clara la ventaja del enfoque de UM hacia esto. – Daniel

3

La solución más sencilla al no ser capaz de hacer

"hello"<unsafe_user_input> 

sería escribir un tipo que tenía algún tipo numérico para envolver la cadena como

type mystring<'t>(s:string) = 
    let dummyint = 1<'t> 

Entonces usted tiene un cheque en tiempo de compilación en sus cadenas

+1

Sugiero usar una estructura en lugar de una clase, para evitar la (pequeña) sobrecarga de la envoltura. – Joh

2

Puede usar uniones discriminadas:

type ValidatedString = ValidatedString of string 
type SmellyString = SmellyString of string 

let validate (SmellyString s) = 
    if (* ... *) then Some(ValidatedString s) else None 

Obtiene una verificación en tiempo de compilación, y la adición de dos cadenas validadas no generará una cadena validada (qué unidades de medida permitirían).

Si la sobrecarga adicional de los tipos de referencia es demasiado grande, puede usar structs en su lugar.

+2

Gracias por la sugerencia. El problema es que DU es una clase, no puedes evitar construirla. 'int ' es en realidad un 'int' en tiempo de ejecución, lo que no genera ninguna sobrecarga. – bytebuster

7

Un poco tarde (estoy seguro de que hay un formato de hora, donde sólo hay un poco diferente entre febrero 23 y 30 de noviembre), creo que estos chistes son compatibles para su objetivo:

type string<[<Measure>] 'm> = string * int<'m> 

type string<[<Measure>] 'm> = { Value : string } 

type string<[<Measure>] 'm>(Value : string) = struct end 
+0

Gracias por la respuesta, pero el primero obliga a construir un 'Tuple <_,_>', y los últimos son en realidad 'struct''s. Estoy tratando de evitar cualquier sobrecarga en tiempo de ejecución. – bytebuster

Cuestiones relacionadas