Raz, dwa, trzy (to teraz). To make long story short:
<a href="#" onclick="javascript:doStuff('!\"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~')">demo 3</a><br />
Co jest tu nie tak? Cóż, przykładowy payload, czyli:
'+alert(/xss/)+'
w tym przypadku nie zadziała, będzie rezultat w kodzie strony będzie wyglądał tak:
<a href="#" onclick="javascript:doStuff('\'+alert(/xss/)+\'')">demo 3</a><br />
Jak widać znak ' jest wypisywany jako \', co jest właściwym sposobem escapingu tego znaku. Podobnie zresztą jak \\\\" jest właściwym sposobem escapingu znaku ", uwaga, w JavaScript. Problem tylko w tym, że nie jesteśmy (wyłącznie) w kontekście JavaScript, ale również w kontekście atrybutu HTML. A skoro tak, to:
"><script>alert(/xss/)</script><foo bar="
da w rezultacie:
<a href="#" onclick="javascript:doStuff('\"><script>alert(/xss/)</script><foo bar=\"')">demo 3</a><br />
Zagmatwane? Przeglądarka zobaczy to tak (IE i Firefox):
Poprzednio okazało się, że encoding HTML w kontekście atrybutu HTML, który zawiera JavaScript, nie jest zbyt skuteczny. Tak samo encoding właściwy dla JavaScript stosowany w przypadku JavaScript, który zawarty jest w atrybucie HTML, nie jest wystarczający. Zupełnie inaczej sytuacja wyglądałaby, gdyby złożyć ze sobą te dwa sposoby kodowania znaków. Najpierw dane wstawiane do kontekstu JavaScript zabezpieczyć w sposób właściwy dla JavaScript właśnie, a potem, skoro trafiają do atrybutu HTML, całość zakodować w sposób właściwy dla tego kontekstu. Wyglądałoby to mniej więcej tak:
<a href="#" onclick="javascript:doStuff('\"><script>alert(/xss/)</script><foo bar=\"')">demo 3</a><br />
Jak zobaczy to przeglądarka - do sprawdzenia we własnym zakresie :)