No, in un contesto HTML non puoi inserire nuovi tag senza lettere dopo la parentesi aperta. Tuttavia, questa tecnica di filtraggio è inutilmente rischiosa.
Il parser HTML del tuo browser web analizza il codice come macchina di stato . Per capire quali sono le tue opzioni, dai un'occhiata alla specifica della sintassi HTML e alle possibili transizioni di stato.
Il punto di iniezione si trova nello stato dei dati (che è il " predefinito "stato, al di fuori di qualsiasi tag):
8.2.4.1 Data state
Consume the next input character:
U+0026 AMPERSAND (&)
Switch to the character reference in data state.
"<" (U+003C)
Switch to the tag open state.
U+0000 NULL
Parse error. Emit the current input character as a character token.
EOF
Emit an end-of-file token.
Anything else
Emit the current input character as a character token.
Per XSS, l'unica continuazione interessante è aprire un tag con <
e passare a tag open state :
8.2.4.8 Tag open state
Consume the next input character:
"!" (U+0021)
Switch to the markup declaration open state.
"/" (U+002F)
Switch to the end tag open state.
Uppercase ASCII letter
Create a new start tag token, set its tag name to the lowercase version of the current input character (add 0x0020 to the character's code point), then switch to the tag name state. (Don't emit the token yet; further details will be filled in before it is emitted.)
Lowercase ASCII letter
Create a new start tag token, set its tag name to the current input character, then switch to the tag name state. (Don't emit the token yet; further details will be filled in before it is emitted.)
"?" (U+003F)
Parse error. Switch to the bogus comment state.
Anything else
Parse error. Switch to the data state. Emit a U+003C LESS-THAN SIGN character token. Reconsume the current input character.
Qui le tue opzioni sono a-z
, A-Z
, !
, /
e ?
.
Non sei stato chiaro se anche i caratteri speciali siano nella lista nera. Ma anche se non lo sono, sei sfortunato:
- Da
<!
si ottiene solo un commento ( <!--
), una dichiarazione doctype ( <!DOCTYPE
) o una sezione CDATA ( <![CDATA
). Questi non sono veri nodi DOM e quindi sono inutili per XSS. (Ad esempio, non si sarebbe in grado di allegare un gestore di eventi a un commento.)
-
<?
potrebbe essere interessante in XML, ma è trattato come un commento in HTML.
-
</
ti consente solo di chiudere i tag.
Potresti aver notato che le specifiche non tollerano alcun carattere di riempimento, come spazi, tabulazioni, nuove righe o caratteri di controllo.
Se vuoi approfondire un po 'e verificare l'implementazione, puoi sempre consultare il codice sorgente. Ad esempio, questo estratto fa parte del tokenizzatore HTML5 utilizzato in Mozilla Firefox . Come puoi vedere, lo stato tag open aderisce strettamente alle specifiche:
case NS_HTML5TOKENIZER_TAG_OPEN: {
for (; ; ) {
if (++pos == endPos) {
NS_HTML5_BREAK(stateloop);
}
c = checkChar(buf, pos);
if (c >= 'A' && c <= 'Z') {
endTag = false;
clearStrBufAndAppend((char16_t) (c + 0x20));
state = P::transition(mViewSource, NS_HTML5TOKENIZER_TAG_NAME, reconsume, pos);
NS_HTML5_BREAK(tagopenloop);
} else if (c >= 'a' && c <= 'z') {
endTag = false;
clearStrBufAndAppend(c);
state = P::transition(mViewSource, NS_HTML5TOKENIZER_TAG_NAME, reconsume, pos);
NS_HTML5_BREAK(tagopenloop);
}
switch(c) {
case '!': {
state = P::transition(mViewSource, NS_HTML5TOKENIZER_MARKUP_DECLARATION_OPEN, reconsume, pos);
NS_HTML5_CONTINUE(stateloop);
}
case '/': {
state = P::transition(mViewSource, NS_HTML5TOKENIZER_CLOSE_TAG_OPEN, reconsume, pos);
NS_HTML5_CONTINUE(stateloop);
}
case '\?': {
if (viewingXmlSource) {
state = P::transition(mViewSource, NS_HTML5TOKENIZER_PROCESSING_INSTRUCTION, reconsume, pos);
NS_HTML5_CONTINUE(stateloop);
}
if (P::reportErrors) {
errProcessingInstruction();
}
clearStrBufAndAppend(c);
state = P::transition(mViewSource, NS_HTML5TOKENIZER_BOGUS_COMMENT, reconsume, pos);
NS_HTML5_CONTINUE(stateloop);
}
case '>': {
if (P::reportErrors) {
errLtGt();
}
tokenHandler->characters(nsHtml5Tokenizer::LT_GT, 0, 2);
cstart = pos + 1;
state = P::transition(mViewSource, NS_HTML5TOKENIZER_DATA, reconsume, pos);
NS_HTML5_CONTINUE(stateloop);
}
default: {
if (P::reportErrors) {
errBadCharAfterLt(c);
}
tokenHandler->characters(nsHtml5Tokenizer::LT_GT, 0, 1);
cstart = pos;
reconsume = true;
state = P::transition(mViewSource, NS_HTML5TOKENIZER_DATA, reconsume, pos);
NS_HTML5_CONTINUE(stateloop);
}
}
}
tagopenloop_end: ;
}
Quindi, il filtro XSS che descrivi sembra essere sicuro secondo le specifiche HTML. È un ghiaccio molto sottile, difficile. Non si sa mai, se un venditore presenta un'implementazione stravagante che potrebbe comunque consentire un exploit. (Microsoft, ti sto guardando!)
La corretta protezione XSS è quindi semplicemente sfuggire al staffe .