将用户提供的 URL 数据安全准确地插入 HTML5 文档的正确方法是啥?

Posted

技术标签:

【中文标题】将用户提供的 URL 数据安全准确地插入 HTML5 文档的正确方法是啥?【英文标题】:What is the right way to safely and accurately insert user-provided URL data into an HTML5 document?将用户提供的 URL 数据安全准确地插入 HTML5 文档的正确方法是什么? 【发布时间】:2017-02-09 06:54:49 【问题描述】:

假设客户在 Web 表单中输入了任意 URL,我想在 href 中生成包含该 URL 的新 html 文档。我的问题是我应该如何在我的 HTML 中保护该 URL。

对于未知最终用户输入的以下 URL,应在 HTML 中呈现什么:

    http://example.com/?file=some_19%affordable.txt http://example.com/url?source=web&last="f o o"&bar=< https://www.google.com/url?source=web&sqi=2&url=https%3A%2F%2Ftwitter.com%2F%3Flang%3Den&last=%22foo%22

如果我们假设 URL 已经被 uri 编码,我认为如果他们从 URL 栏中复制它是合理的,那么只需将其传递给 attr() 会生成一个有效的 URL 和通过 Nu HTML 检查器的文档在validator.w3.org/nu。

为了查看它的实际效果,我们在https://jsfiddle.net/kamelkev/w8ygpcsz/2/ 设置了一个 JS fiddle,用上面的示例替换其中的 URL 可以显示正在发生的事情。

为了将来参考,这包含一个 HTML sn-p

<a>My Link</a>

还有这个 JS:

$(document).ready(function() 
 $('a').attr('href', 'http://example.com/request.html?data=&gt;');
 $('a').attr('href2', 'http://example.com/request.html?data=<');
 alert($('a').get(0).outerHTML);
);

因此,对于 URL 1,无法通过机械地查看它来判断它是否是 URI 编码的。您可以根据您的人类知识推测它不是,并且指的是名为some_19%affordable.txt 的文件。当通过小提琴运行时,它会产生

<a href="http://example.com/?file=some_19%affordable.txt">My Link</a>

通过 HTML5 验证器没有问题。但这可能不是用户想要的。

第二个 URL 显然不是 URI 编码的。问题变成了在 HTML 中放入什么是正确的,以防止 HTML 解析问题。

通过小提琴运行它,Safari 10 会产生这个:

<a href="http://example.com/url?source=web&amp;last=&quot;f o o&quot;&amp;bar=&lt;">My Link</a>

几乎所有其他浏览器都会产生这个:

<a href="http://example.com/url?source=web&amp;last=&quot;f o o&quot;&amp;bar=<">My Link</a>

这些都没有通过验证器。可能有三种抱怨:文字双引号(来自非转义 HTML)、空格或尾随 &amp;lt; 字符(也来自非转义 HTML)。它只是向您展示它找到的第一个。这显然不是有效的 HTML。

尝试解决此问题的两种方法是:a) 在将 URL 提供给 attr() 之前对 URL 进行 html 转义。然而,这会导致每个&amp;amp; 变成&amp;amp;,并且&amp;amp;&amp;lt; 等实体被attr() 双重转义,并且文档中的URL 完全不准确。它看起来像这样:

<a href="http://example.com/url?source=web&amp;amp;last=&amp;quot;f+o+o&amp;quot;&amp;amp;bar=&amp;lt;">My Link</a>

另一种方法是在传递给attr() 之前对其进行URI 编码,这确实会产生一个正确的验证URL,它实际上会点击到预期的目的地。它看起来像这样:

<a href="http://example.com/url?source=web&amp;last=%22f%20o%20o%22&amp;bar=%3C">My Link</a>

最后,对于经过正确 URI 编码的第三个 URL,验证的正确 HTML 确实出来了。

<a href="https://www.google.com/url?source=web&amp;sqi=2&amp;url=https%3A%2F%2Ftwitter.com%2F%3Flang%3Den&amp;last=%22foo%22">My Link</a>

它会执行用户点击时期望发生的事情。

基于此,算法应该是:

if url is encoded then
 pass as-is to attr()
else
 pass encodeURI(url) to attr()

然而,基于这两个先前的讨论,“被编码”测试似乎不可能检测到肯定的(确实,参见示例 URL 1):

How to find out if string has already been URL encoded? How to know if a URL is decoded/encoded?

如果我们绕过attr()方法,强行将示例URL 2的HTML转义版本插入到文档结构中,它看起来像这样:

<a href="http://example.com/url?source=web&amp;last=&quot;f+o+o&quot;&amp;bar=&lt;">My Link</a>

这看起来像是有效的 HTML,但 HTML5 验证器却失败了,因为它没有转义以包含无效的 URL 字符。然而,浏览器似乎并不介意。不幸的是,如果您对该对象进行任何其他操作,浏览器无论如何都会重新转义所有&amp;amp;

如您所见,这一切都非常令人困惑。这是我们第一次使用浏览器本身来生成 HTML,我们不确定我们是否正确。以前,我们使用模板在服务器端进行,并且只使用 HTML-escape 过滤器。

什么是安全准确插入用户提供的正确方法 将 URL 数据转换为 HTML5 文档(使用 javascript)?

【问题讨论】:

【参考方案1】:

如果您可以假设 URL 已编码或未编码,那么您也许可以摆脱这种情况。尝试对 URL 进行解码,将错误视为 URL 未编码,您应该得到一个解码后的 URL。

<script>
var inputurl = 'http://example.com/?file=some_19%affordable.txt';
var myurl;

try 
    myurl = decodeURI(inputurl);

catch(error) 
    myurl = inputurl;


console.log(myurl);
</script>

【讨论】:

感谢您的回答。我不想有一个解码的 URI。请参见示例 #3。如果我对其进行解码,然后将其传递给attr() 方法,我会遇到双引号问题。 %22 解码后变为",然后attr() 将它们转换为&amp;quot;。至少浏览器可以很好地处理其余部分。但是,我认为,如果它确实可以安全解码,我知道将原件传递给attr() 是安全的,否则我必须进行补救以保护自己。

以上是关于将用户提供的 URL 数据安全准确地插入 HTML5 文档的正确方法是啥?的主要内容,如果未能解决你的问题,请参考以下文章

Bleach:Mozilla 出品,快速准确地清理 HTML

如何安全地为 bigquery 节点插入转义用户输入?可以在 bigquery.insert 节点库上使用参数化查询吗?

将 1 个密码安全地存储在数据库中

如何有效地为许多产品提供 1 个 html 布局页面,这些产品将调用 mysql 数据库获取产品信息并插入我指定的位置?

提交按钮的 HTML 不会将数据插入数据库并一起重定向到 URL

如何从 Azure 数据工厂安全地调用 Azure 逻辑应用