La debilidad de seguridad de aplicaciones web más comunes es la falla en validar adecuadamente la entrada del cliente o entorno.
OWASP, Validación de datos (2 de julio de 2017)
No manejar adecuadamente la entrada no confiable deja una aplicación vulnerable a los ataques de inyección de código, como los secuencias de comandos del sitio cruzado (XSS). "Alguna forma de contabilidad visible en la entrada del cliente o fuentes no confiables" es una contramedida recomendada por OWASP, como parte de una estrategia de defensa en profundidad.
Este módulo Python "contamina" la entrada no confiable al envolverlos en tipos especiales. Estos tipos se comportan en la mayoría de los aspectos, al igual que los tipos nativos de Python, pero le impiden usar sus valores accidentalmente. Esto proporciona no solo la "punzada visible", sino las garantías de tiempo de ejecución y (opcionalmente) seguridad de tipo verificable con mypy.
Las estrategias para desinfectar, escapar, normalizar o validar la entrada están fuera de alcance para este módulo.
Este módulo debe ser adecuado para el uso de la producción, con las siguientes advertencias:
untrusted.sequence El tipo de secuencia faltan pruebas y puede estar incompletountrusted.mapping El tipo de mapas no tiene pruebas y puede estar incompletounstrusted.<*>Of no está completamente probadoCualquier código con consideraciones de seguridad merece los más altos estándares de escrutinio. Por favor, ejercita su juicio antes de usar este módulo.
El software se proporciona "tal cual", sin garantía de ningún tipo.
Probado para Python> = 3.4, pero las versiones anteriores pueden funcionar.
sudo pip3 install untrusted --upgrade
(Si solo tiene instalado Python3, es posible que solo necesite pip , no pip3 )
import html # for html.escape
import untrusted
line = untrusted . string ( input ( "Enter some text, HTML will be escaped: " ))
try :
# You're not allowed to print an untrusted.string!
# This raises a TypeError!
print ( "<b>You typed:</b> " + line )
except TypeError :
pass # Expected
# Safely escape the HTML!
print ( "<b>You typed:</b> " + line / html . escape )
# / is overloaded as shorthand for:
# print("<b>You typed:</b> " + line.escape(html.escape)) untrusted.stringstr , pero no se puede emitir sin escaparstr nativosstr nativo y untrusted.string Valores de manchas de str /Promueve los tipos de untrusted.string .Ejemplo de manejo HTML no confiable:
import html # for html.escape()
import untrusted
# example of a string that could be provided by an untrusted user
firstName = untrusted . string ( "Grace" )
lastName = untrusted . string ( "<script>alert('hack attempt!');</script>" )
# works seamlessly with native python strings
fullName = firstName + " " + lastName
# fullName was tainted and keeps the untrusted.string type
print ( repr ( fullName )) # <untrusted.string of length 46>
# the normal string methods still work as you would expect
fullName = fullName . title ()
# but you can't accidentally use it where a `str` is expected
try :
print ( fullName ) # raises TypeError - untrusted.string used as a `str`!
fullName . encode ( 'utf8' ) # also raises TypeError
except TypeError :
print ( "We caught an error, as expected!" )
# instead, you are forced to explicitly escape your string somehow
print ( "<b>Escaped output:</b> " + fullName . escape ( html . escape )) # prints safe HTML!
print ( "<b>Escaped output:</b> " + fullName / html . escape ) # use this easy shorthand! Consulte untrustedStringExample.py para componer múltiples funciones de escape, pasando argumentos para escapar de las funciones, etc.
Este módulo proporciona tipos para envolver perezosamente las colecciones de valores no confiables. Los valores están envueltos con un tipo apropiado untrusted.* Tipo cuando se accede.
untrusted.iteratoruntrusted.*untrusted.string , pero también puede ser un iterador sobre colecciones no confiablesEjemplo:
import html # for html.escape
import untrusted
it = untrusted . iterator ( open ( "example.txt" ))
for i , s in enumerate ( it ):
print ( "Line %d: %s" % ( i , s . escape ( html . escape ). strip ())) Consulte examples/untrustedIteratorExample.py para una lista anidada no confiable (por ejemplo, un untrusted.iterator de untrusted.iterator de untrusted.string ).
untrusted.sequencelist que contenga valores no confiables.untrusted.*untrusted.string , pero también puede ser un iterador sobre colecciones no confiablesEjemplo:
import html # for html.escape
import untrusted
# list of strings from an untrusted source
animals = [ "cat" , "dog" , "monkey" , "elephant" , "<big>mouse</big>" ]
untrustedAnimalList = untrusted . sequence ( animals )
assert "cat" in untrustedAnimalList untrusted.mappingdict , claves de confianza o no confiables para valores no confiables.untrusted.* Tipo.*.str -> untrusted.string , pero también podría ser un mapeo unstrusted.string String -> untrusted.string , o un mapeo a una colección no confiable.Ejemplo:
import html # for html.escape
import untrusted
user = untrusted . mapping ({
'username' : 'example-username<b>hack attempt</b>' ,
'password' : 'example-password' ,
})
try :
print ( user . get ( "username" ))
except TypeError :
print ( "Caught the error we expected!" )
print ( user . get ( "username" , "default-username" ) / html . escape )Ejemplo:
import html # for html.escape
import untrusted
untrustedType = untrusted . mappingOf ( untrusted . string , untrusted . string )
args = untrustedType ({ 'animal' : 'cat' , '<b>hack</b>' : 'attempt' })
for k , v in args . items ():
print ( "%s: %s" % ( k / html . escape , v / html . escape ))Los contenedores anidados perezosamente también son totalmente compatibles.
Use untrusted.iteratorOf(valueType) , untrusted.sequenceOf(valueType) untrusted.mappingOf(keyType, valueType) .
Ejemplo:
import html # for html.escape
import untrusted
people = [
{
'id' : 'A101' ,
'name.first' : 'Grace' ,
'name.last' : 'Hopper' ,
'name.other' : 'Brewster Murray' ,
'dob' : '1906-12-09' ,
},
{
'id' : 'A102' ,
'name.first' : 'Alan' ,
'name.last' : 'Turing' ,
'name.other' : 'Mathison' ,
'dob' : '1912-06-23' ,
},
{
'id' : 'HACKER' ,
'name.first' : 'Robert ' ); DROP TABLE Students;--' ,
'name.last' : '£Hacker' ,
'dob' : '<b>Potato</b>'
},
]
# a list of dicts with trusted keys, but untrusted values
mappingType = untrusted . sequenceOf ( untrusted . mapping )
# aka (setting defaults explicitly)
mappingType = untrusted . sequenceOf ( untrusted . mappingOf ( str , untrusted . string ))
for person in mappingType ( people ):
for key , value in person . items ():
print ( " %s: %s" % ( key , value . escape ( html . escape ))) Admite la mayoría de los métodos str nativos, pero posiblemente puede tener diferentes tipos de retorno.
El valor subyacente: siempre una instancia no nonosa de str .
untrusted.string(s)
Construye un objeto untrusted.string , donde s es una instancia no non no de str o untrusted.string .
En el caso de que una untrusted.string sea construida con un argumento untrusted.string , el nuevo valor solo se envuelve una vez. Escapar de él le dará a un str , no un untrusted.string .
untrusted.string.escape(escape_function, [*args, **kwargs]) -> str
Aplica el escape_function , una función str -> str que escapa de una cadena y la devuelve, con argumentos opcionales y argumentos de palabras clave.
untrusted.string.valid(valid_function, [*args, **kwargs]) -> bool
Aplica el valid_function , una función str -> bool , que verifica una cadena y devuelve verdadero o falso, con argumentos opcionales y argumentos de palabras clave.
untrusted.string.valid(validate_function, [*args, **kwargs]) -> any
Aplica el valid_function , una función str -> any , que verifica una cadena y devuelve cualquier valor (por ejemplo, una lista de razones por las cuales una cadena no validó), con argumentos opcionales y argumentos de palabras clave.
untrusted.string / escape_expr Para alguna expresión de escape, untrusted.string("value") / escape_expr -> str
Un escape_expr aquí es una función str -> str que escapa de una cadena, o un 3 -tuple (escape_function, args, kwargs_dict) , por ejemplo:
import html # for html.escape
import untrusted
myString = untrusted . string ( "<b>Exam " ple</b>" )
myEscape = ( html . escape , [], { 'quote' : False })
print ( myString / html . escape )
print ( myString / myEscape ) Donde collection es una de iterator , sequence (lista), mapping (tipo dict) o un tipo personalizado construido por el usuario.
Admite la mayoría de los métodos de recolección nativos, pero posiblemente puede tener diferentes tipos de retorno.
Cada colección es consciente de su tipo de valor (predeterminado untrusted.string ).
Las asignaciones también son conscientes de su tipo de clave ( str predeterminado)
El valor subyacente: siempre un objeto no none que es una instancia del tipo de valor de la colección.
Cree un tipo de iterador con untrusted.iteratorOf(type) .
Cree un tipo de secuencia usando untrusted.sequenceOf(type) .
Cree un tipo de mapeo con untrusted.sequenceOf(keyType, valueType) .
Estas definiciones son recursivas.
Por ejemplo:
import untrusted
itType = untrusted . iteratorOf ( untrusted . iteratorOf ( untrusted . string ))
seqType = untrusted . sequenceOf ( itType )
mappingType = untrusted . mappingOf ( untrusted . string , seqType )
someDict = {}
myMapping = mappingType ( someDict )
# or, as a less readable one-liner
myMapping = untrusted . mappingOf ( untrusted . string , seqType )( someDict ) Recuerde, solo porque haya usado un método para escapar de una untrusted.string en un str , puede no ser seguro en otros contextos. Por ejemplo, lo que es seguro para HTML podría ser un SQL peligroso. En el momento en que se captura la entrada del usuario, es posible que no se conozca de antemano el contexto en el que se debe usar, y por lo tanto no se sabe cuál es la validación y la estrategia de escape correctas. Es mejor retrasar el escape hasta el punto de uso final: mantenga un valor tan untrusted.* Durante el mayor tiempo posible.
Este módulo no es una solución mágica. Es una herramienta para usarse sabiamente. No caigas en la trampa de pensar en un valor "Es un str ahora, por lo que es completamente seguro".
El hecho de que una cadena se pueda escape de manera segura, no significa que haya sido validada como entrada permitida. Por ejemplo, puede poner un límite mínimo en una contraseña. O puede requerir que una entrada no sea un nombre de archivo reservado.
Las buenas formas de hacer esto son usar el método unstruted.string.valid , que devuelve un método booleano, untrusted.string.validate , que devuelve cualquier valor (por ejemplo, esta podría ser una lista de razones por las cuales la entrada no valdia), o para arrojar un ValueError de una función que se escape y validan.
A veces, se genera HTML y escapar es algo que debe hacer.
A veces, una interfaz siempre separa los parámetros del código, como la mayoría de las bibliotecas SQL modernas. En este caso, el controlador de la base de datos manejará la entrada potencialmente no confiable de lo que podría esperar, y escapar de él es lo incorrecto.
Puede marcar la entrada como no confiable por otras razones, por ejemplo, para hacer cumplir una longitud máxima en una consulta de búsqueda, o porque necesita validarla contra la lógica de negocios.
Los tipos de recolección no confiables, como untrusted.sequence , son "vistas" sobre el objeto subyacente. Si el objeto subyacente cambia, también lo hace el objeto que ve a través del tipo de colección no confiable. En otras palabras: es una referencia al mismo objeto, no una copia. Si eso no es lo que desea, use el módulo de copia.
Con suerte, esto debería ser un comportamiento obvio e insuficiente, no en absoluto exclusivo de este módulo, pero puede hacer tropezar a las personas.
Este es el software libre (licencia del MIT).
untrusted - write safer Python with special untrusted types
Copyright © 2017 - 2018 Ben Golightly <[email protected]>
Copyright © 2017 - 2018 Tawesoft Ltd <[email protected]>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.