Давайте проанализируем несколько сценариев:
Не кодировать вообще
<!--- our "tricky" ID --->
<cfset ID = '"><script>alert("my evil script");</script><div foo="'>
<!--- we are closing the data-href attribute, injecting our JS and start a new tag to complete the remaining tag --->
<cfoutput>
<div data-href="page.cfm?Id=#ID#"></div>
<!--- [data-href] is printed as: page.cfm?Id="><script>alert("my evil script");</script><div foo=" --->
</cfoutput>
Результат
Появляется диалоговое окно с предупреждением «Мой злой скрипт».
Заключение
Никогда не оставляйте пользовательский ввод без кодировки! (Вы это уже знали).
Кодирование строки запроса для вывода в виде HTML
Примечание: Вы всегда должны кодировать полное значение атрибута HTML, а не только его части.
<!--- our "tricky" ID --->
<cfset ID = "&a=b?c">
<!--- we are having some reserved characters here that will confuse the browser's query string parser --->
<cfoutput>
<div data-href="#encodeForHtmlAttribute("page.cfm?Id=#ID#")#"></div>
<!--- [data-href] is printed as: page.cfm?Id=&a=b?c --->
<script>
var attr = document.getElementsByTagName('div')[0].getAttribute('data-href');
console.log(attr); <!--- page.cfm?Id=&a=b?c --->
<cfif structIsEmpty(URL)> <!--- test related: to prevent infinite redirection --->
location.href = attr;
</cfif>
</script>
</cfoutput>
<cfdump var="#URL#">
Результат
При запросе page.cfm
мы будем перенаправлены на page.cfm?Id=&a=b?c
, обычное значение атрибута data-href
.Однако дамп области URL
предоставит нам пары ключ-значение:
Id: [empty string]
a: b?c
Что и следовало ожидать, потому что синтаксический анализатор строки запроса браузера не мог различить буквальное значение и техническое назначениеперсонажи. Я недавно ответил на это здесь.
Заключение
Кодирование вывода недостаточно при наличии нескольких контекстов (здесь: HTML & URL / QueryString).
Кодирование строки запроса в виде URL
<!--- our "tricky" ID --->
<cfset ID = 'a&b="><script>alert("my evil script");</script><div foo="'>
<!--- we are mixing in both contexts now --->
<cfoutput>
<div data-href="page.cfm?Id=#encodeForUrl(ID)#"></div>
<!--- [data-href] is printed as: page.cfm?Id=a%26b%3D%22%3E%3Cscript%3Ealert%28%22my+evil+script%22%29%3B%3C%2Fscript%3E%3Cdiv+foo%3D%22 --->
<script>
var attr = document.getElementsByTagName('div')[0].getAttribute('data-href');
console.log(attr); <!--- page.cfm?Id=a%26b%3D%22%3E%3Cscript%3Ealert%28%22my+evil+script%22%29%3B%3C%2Fscript%3E%3Cdiv+foo%3D%22 --->
<cfif structIsEmpty(URL)> <!--- test related: to prevent infinite redirection --->
location.href = attr;
</cfif>
</script>
</cfoutput>
<cfdump var="#URL#">
Результат
При запросе page.cfm
мы будем перенаправлены на page.cfm?Id=a%26b%3D%22%3E%3Cscript%3Ealert%28%22my+evil+script%22%29%3B%3C%2Fscript%3E%3Cdiv+foo%3D%22
, обычное значение data-href
приписывать.Дамп области URL
предоставит нам пару ключ-значение:
Id: a&b="><script>alert("my evil script");</script><div foo="
На этот раз парсер строки запроса браузера может различать буквальное значение и техническое назначение символов.Но как насчет HTML-контекста здесь?Ну, процентное кодирование, выполняемое encodeForUrl()
, не конфликтует с зарезервированными символами HTML, потому что %
не имеет технической цели в HTML и ничего не нарушает.
Заключение
Теоретически мы сделали здесь.Нет необходимости в HTML-кодировании значения в кодировке URL, поскольку нет перекрытия двух кодировок.
Кодирование строки запроса в виде URL - И - для вывода в виде HTML
<!--- our "tricky" ID --->
<cfset ID = 'a&b="><script>alert("my evil script");</script><div foo="'>
<!--- we are mixing in both contexts again --->
<cfoutput>
<div data-href="#encodeForHtmlAttribute("page.cfm?Id=#encodeForUrl(ID)#")#"></div>
<!--- [data-href] is printed as: page.cfm?Id=a%26b%3D%22%3E%3Cscript%3Ealert%28%22my+evil+script%22%29%3B%3C%2Fscript%3E%3Cdiv+foo%3D%22 --->
<script>
var attr = document.getElementsByTagName('div')[0].getAttribute('data-href');
console.log(attr); <!--- page.cfm?Id=a%26b%3D%22%3E%3Cscript%3Ealert%28%22my+evil+script%22%29%3B%3C%2Fscript%3E%3Cdiv+foo%3D%22 --->
<cfif structIsEmpty(URL)> <!--- test related: to prevent infinite redirection --->
location.href = attr;
</cfif>
</script>
</cfoutput>
<cfdump var="#URL#">
Результат
При запросе page.cfm
мы будем перенаправлены на page.cfm?Id=a%26b%3D%22%3E%3Cscript%3Ealert%28%22my+evil+script%22%29%3B%3C%2Fscript%3E%3Cdiv+foo%3D%22
, обычное значение атрибута data-href
.Дамп области URL
предоставит нам пару ключ-значение:
Id: a&b="><script>alert("my evil script");</script><div foo="
Кажется, ничего не изменилось, верно?Не совсем.Вот как наша data-href
теперь выглядит в итоговом HTML-выводе: page.cfm?Id=a%26b%3D%22%3E%3Cscript%3Ealert%28%22my+evil+script%22%29%3B%3C%2Fscript%3E%3Cdiv+foo%3D%22
Как видите, кодирование процентов теперь дополнительно кодируется для HTML (%
было закодировано в его шестнадцатеричное представление * 1077).*).
Заключение
Значение теперь безопасно для обоих контекстов.
Существует больше кодировок, которые могут смешиваться (например, encodeForJavaScript()
),Но ты получил идею.Это всегда о том, какие символы в значении требуют, чтобы кодировка была неверно истолкована для их технического назначения.Это может закончиться так, как если бы у вас было от 3 до 4 вложенных кодировок.Но опять же: обычно эти кодировки не конфликтуют друг с другом, поэтому необязательно кодировать их для всех контекстов.