Esiste un modo per aggirare l'XSS di Django che fugge con "unicode"?

12

Django (il framework web Python) scappa l'output per prevenire gli attacchi XSS (Cross Site Scripting). Sostituisce ' , " , < , > , & con le loro versioni HTML sicure.

Tuttavia questa presentazione sulla diapositiva , (in particolare scivolare № 13 ), dice:

Problems

  1. Any other Unicode will bypass this check

Non riesco a capire questa lamentela. Esiste un carattere unicode che non verrà sostituito dalla funzione escape di Django che consentirà un XSS? So un po 'di unicode e non riesco a pensare come.

    
posta Rory 10.04.2013 - 12:54
fonte

3 risposte

10

Non è chiaro a cosa si riferisca esattamente la diapositiva. L'auto-fuga di Django dovrebbe andare bene contro l'iniezione di HTML nel contenuto di testo e nei valori degli attributi citati correttamente.

Non ci sono altri caratteri Unicode che possono eludere l'escape dell'HTML, ma in linea di principio ci sono sequenze di byte che potrebbero essere interpretate erroneamente come nella codifica Unicode sbagliata:

  • Se il browser decide di interpretare un documento come UTF-7, +ADw- diventa sinonimo di < (e sequenze simili per &"'> ), consentendo ai metacaratteri HTML di evitare l'escape.

  • Alcune codifiche multibyte in Asia orientale consentono ai byte in coda in una sequenza multibyte di essere nell'intervallo 0x00-0x7F dove potrebbero essere interpretati come caratteri ASCII e sfuggiti in modo errato se gestiti in questo modo. Tuttavia, di solito questo porta solo a un testo spezzato piuttosto che a un problema di sicurezza.

  • Le sequenze di byte UTF-8 "overlong" non valide possono essere interpretate come ASCII da alcuni browser molto vecchi (l'originale IE6 pre-SP1 e Opera allo stesso tempo). Ciò potrebbe consentire ai metacaratteri HTML di evitare l'escape, come la sequenza di byte 0xC0 0xBC che rappresenta < .

Per evitare questi problemi, (a) assicurati di servire i tuoi documenti con un UTF-8 Content-Type charset , e (b) mantieni tutte le tue stringhe di testo come stringhe Unicode native internamente in modo che non possano mai codificare alle sequenze UTF-8 non valide.

Dal momento che le app di Django tendono a farlo di default già, non è probabile che l'auto-fuga dei modelli di Django venga sconfitta dai problemi Unicode.

Ciò non significa che XSS sia risolto in generale, naturalmente: devi ancora evitare l'uso improprio di |safe , attributi non quotati, problemi di immissione non HTML (come stringhe JavaScript, proprietà CSS, parametri URL), sniffing del contenuto HTML , schemi URL pericolosi ( javascript: et al) e così via. Ma come difesa contro l'iniezione di HTML nei modelli dovrebbe essere valido.

    
risposta data 10.04.2013 - 21:27
fonte
7

Sì, ci sono almeno tre casi in cui questo filtro XSS ha esito negativo. XSS è complesso e sostituire ciecamente i personaggi non risolve questo problema. Il più ovvio è se scrivi all'interno di un tag script:

<script>
var x = alert(1);
</script>

Se stai scrivendo un href o iframe src puoi usare l'url javascript: :

<a href=javascript:alert(1)>alert</a>

È anche vulnerabile a xss se scrivi all'interno di un evento DOM

<a href="doSomethingCool('userInput%27);sendHaxor(document.cookie);//');">Cool Link</a>

Il browser decodificherà automaticamente %27 (così come altri metodi di codifica) prima di eseguire l'evento JavaScript.

    
risposta data 10.04.2013 - 20:48
fonte
3

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 &lt; 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('&', '&amp;').replace('<', '&lt;').replace('>', '&gt;').replace('"', '&quot;').replace("'", '&#39;'))
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
    
risposta data 10.04.2013 - 23:04
fonte

Leggi altre domande sui tag