带有 URL 的 data- 属性上的哪个 EncodeFor
Which EncodeFor on data- attributes with URLs
我正在尝试将 URL 放入 data-
属性中。特别是
<tr data-href="/page.cfm?Id=#EncodeForHTMLAttribute(ID)#">
...
或者也许应该是
<tr data-href="/page.cfm?Id=#EncodeForURL(ID)#">
...
注意ID可以有特殊字符
编辑:
很久以后我要去
$("tr").click(function() { window.location = $(this).data("href"); });
让我们分析一些场景:
根本不编码
<!--- 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>
结果
出现警告对话框 "my evil script"。
结论
切勿让用户输入未编码!(您已经知道了。)
将查询字符串编码为 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 - AND - 输出为 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(%
被编码为其十六进制表示形式 %
)。
结论
该值现在对于两种上下文都是安全的。
可以混合使用更多编码(想想 encodeForJavaScript()
),但您明白了。它总是关于值中的哪些字符需要编码,以免因其技术目的而被误解。这最终可能会像拥有 3 到 4 个嵌套编码一样疯狂。但话又说回来:通常这些编码不会相互冲突,因此不一定需要针对所有上下文对它们进行编码。
我正在尝试将 URL 放入 data-
属性中。特别是
<tr data-href="/page.cfm?Id=#EncodeForHTMLAttribute(ID)#">
...
或者也许应该是
<tr data-href="/page.cfm?Id=#EncodeForURL(ID)#">
...
注意ID可以有特殊字符
编辑:
很久以后我要去
$("tr").click(function() { window.location = $(this).data("href"); });
让我们分析一些场景:
根本不编码
<!--- 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>
结果
出现警告对话框 "my evil script"。
结论
切勿让用户输入未编码!(您已经知道了。)
将查询字符串编码为 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 - AND - 输出为 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(%
被编码为其十六进制表示形式 %
)。
结论
该值现在对于两种上下文都是安全的。
可以混合使用更多编码(想想 encodeForJavaScript()
),但您明白了。它总是关于值中的哪些字符需要编码,以免因其技术目的而被误解。这最终可能会像拥有 3 到 4 个嵌套编码一样疯狂。但话又说回来:通常这些编码不会相互冲突,因此不一定需要针对所有上下文对它们进行编码。