Django fa le cose sensate per ridurre l'esposizione all'XSS.
Django utilizza la codifica unicode e UTF-8 ovunque per impostazione predefinita e impone sensibilmente la codifica unicode prima di eseguire la sostituzione su tutte le variabili del modello (eseguita per impostazione predefinita) per impedire agli utenti di inserire elementi HTML arbitrari. Django consente agli sviluppatori di cambiare la codifica con l'impostazione DEFAULT_CHARSET
, ma imporrà tale codifica in tutta l'applicazione e inserirà le intestazioni di risposta HTTP Content-Type: text/html; charset=utf-8
per impostazione predefinita (con 'text / html' e 'utf-8' cambiando se sei restituire un content_type diverso o modificare il set di caratteri). Inoltre, le pagine di django imposteranno anche <meta http-equiv="content-type" content="text/html; charset=utf-8">
nei loro modelli di base e nelle loro pagine di amministrazione, ma di nuovo offrono agli sviluppatori la possibilità di non usare i loro modelli di base (e i modelli personalizzati personalizzati di dev non possono definire un set di caratteri nel meta tag o peggio potrebbero usa il set di caratteri sbagliato). Pertanto, mentre la grande risposta di Bobince ha elencato alcune carenze del sostituto <
per <
nell'input dell'utente tramite problemi di codifica; django per impostazione predefinita gestirà questi correttamente.
È al 100% infallibile? No, offrono allo sviluppatore una configurabilità sufficiente per eseguire operazioni non sicure come inserire l'input dell'utente in un'azione onclick, ignorare l'escape automatico (tramite la funzione mark_safe()
o {{ user_input|safe }}
nel modello) o consentire l'input dell'utente in una posizione non sicura Ad esempio, un collegamento o all'interno di JavaScript javascript. Certo, sarebbe quasi impossibile fare molto di più senza un'accurata compilazione / analisi semantica di ogni modello.
Per le persone interessate, il codice di escape è abbastanza leggibile in django / utils / html. py . (Il mio link va alla versione corrente dev, ma la mia copia è da django 1.2 La differenza principale tra la versione dev e la versione 1.2 è che sono state rinominate force_unicode
in force_text
(in py3 tutto il testo è unicode) e reso compatibile con python 3 (tutti i riferimenti a sei).)
Fondamentalmente, la funzione di escape viene eseguita su ogni variabile da rendere nel modello e prima controlla che possa essere codificata correttamente e quindi sostituisce i caratteri: &<>'"
con i loro equivalenti con escape HTML. Esistono anche funzioni per l'escape di JS, anche se credo che debba essere chiamato manualmente nel modello come {{ variable|escapejs }}
.
def escape(html):
"""
Returns the given HTML with ampersands, quotes and angle brackets encoded.
"""
return mark_safe(force_unicode(html).replace('&', '&').replace('<', '<').replace('>', '>').replace('"', '"').replace("'", '''))
escape = allow_lazy(escape, unicode)
_base_js_escapes = (
('\', r'\u005C'),
('\'', r'\u0027'),
('"', r'\u0022'),
('>', r'\u003E'),
('<', r'\u003C'),
('&', r'\u0026'),
('=', r'\u003D'),
('-', r'\u002D'),
(';', r'\u003B'),
(u'\u2028', r'\u2028'),
(u'\u2029', r'\u2029')
)
# Escape every ASCII character with a value less than 32.
_js_escapes = (_base_js_escapes +
tuple([('%c' % z, '\u%04X' % z) for z in range(32)]))
def escapejs(value):
"""Hex encodes characters for use in JavaScript strings."""
for bad, good in _js_escapes:
value = mark_safe(force_unicode(value).replace(bad, good))
return value
escapejs = allow_lazy(escapejs, unicode)
def conditional_escape(html):
"""
Similar to escape(), except that it doesn't operate on pre-escaped strings.
"""
if isinstance(html, SafeData):
return html
else:
return escape(html)
e da django / utils / encoding.py :
def force_unicode(s, encoding='utf-8', strings_only=False, errors='strict'):
"""
Similar to smart_unicode, except that lazy instances are resolved to
strings, rather than kept as lazy objects.
If strings_only is True, don't convert (some) non-string-like objects.
"""
if strings_only and is_protected_type(s):
return s
try:
if not isinstance(s, basestring,):
if hasattr(s, '__unicode__'):
s = unicode(s)
else:
try:
s = unicode(str(s), encoding, errors)
except UnicodeEncodeError:
if not isinstance(s, Exception):
raise
# If we get to here, the caller has passed in an Exception
# subclass populated with non-ASCII data without special
# handling to display as a string. We need to handle this
# without raising a further exception. We do an
# approximation to what the Exception's standard str()
# output should be.
s = ' '.join([force_unicode(arg, encoding, strings_only,
errors) for arg in s])
elif not isinstance(s, unicode):
# Note: We use .decode() here, instead of unicode(s, encoding,
# errors), so that if s is a SafeString, it ends up being a
# SafeUnicode at the end.
s = s.decode(encoding, errors)
except UnicodeDecodeError, e:
if not isinstance(s, Exception):
raise DjangoUnicodeDecodeError(s, *e.args)
else:
# If we get to here, the caller has passed in an Exception
# subclass populated with non-ASCII bytestring data without a
# working unicode method. Try to handle this without raising a
# further exception by individually forcing the exception args
# to unicode.
s = ' '.join([force_unicode(arg, encoding, strings_only,
errors) for arg in s])
return s