【问题标题】:Prevent escaping forward slashes in templates防止在模板中转义正斜杠
【发布时间】:2016-10-28 12:04:04
【问题描述】:

我正在将我的一个宠物项目从 Python 转换为 Go,只是为了帮助我稍微熟悉一下这门语言。我目前面临的一个问题是它正在逃避我的正斜杠。所以它会收到这样的字符串:

/location/to/something

然后就变成了

%2flocation%2fto%2fsomething

现在,它只有在链接中时才会这样做(从我一直在阅读的内容来看,此转义是上下文相关的)所以这就是 HTML 模板中的行的样子:

<tr><td><a href="/file?file={{.FullFilePath}}">{{.FileName}}</a></td></tr>

如果可能,如何在模板或代码本身中防止这种情况发生?

这就是我的模板函数的样子(是的,我知道它很老套)

func renderTemplate(w http.ResponseWriter, tmpl string) {
    t, err := template.ParseFiles(templates_dir+"base.html", templates_dir+tmpl)
    if err != nil {
        http.Error(w, err.Error(), http.StatusInternalServerError)
        return
    }
    if tmpl == "view.html" {
        err = t.Execute(w, FileList)
    } else {
        err = t.Execute(w, nil)
    }
    if err != nil {
        http.Error(w, err.Error(), http.StatusInternalServerError)
    }
}

【问题讨论】:

  • @icza,感谢您向我推荐该链接,但我看不到如何在此处实施该解决方案。诚然,我仍然是一个菜鸟,所以也许这就是原因。我最终确实找到了另一个问题,其解决方案最终帮助了我,所以我在这里发布了。
  • 在下面查看我的答案。

标签: templates go go-templates


【解决方案1】:

好的,所以我找到的解决方案(如果有更好的解决方案,请发布)基于here 的答案。

我改变了我正在使用的结构:

type File struct {
    FullFilePath string
    FileName     string
}

到这里:

type File struct {
    FullFilePath template.HTML
    FileName     string
}

并将 html 移动到 FullFilePath 名称中,然后将其放在 template.HTML 中,因此我生成的每个 FullFilePath 名称都是这样完成的:

file := File{template.HTML("<a href=\"/file?file=" + path + "\"</a>"), f.Name()}

而我的模板文件行改为:

<tr><td>{{.FullFilePath}}{{.FileName}}</td></tr>

【讨论】:

    【解决方案2】:

    作为.FullFilePath 的值,传递template.URL 类型的值而不是string,这将告诉html/template 包不要转义它。

    例如:

    func main() {
        t := template.Must(template.New("").Parse(templ))
    
        m := map[string]interface{}{
            "FileName":     "something.txt",
            "FileFullPath": template.URL("/location/to/something"),
        }
    
        if err := t.Execute(os.Stdout, m); err != nil {
            panic(err)
        }
    }
    
    const templ = `<tr><td><a href="/file?file={{.FileFullPath}}">{{.FileName}}</a></td></tr>`
    

    输出(在Go Playground上试试):

    <tr><td><a href="/file?file=/location/to/something">something.txt</a></td></tr>
    

    请注意,尽管在 URL 中允许使用正斜杠 /,但 template 包仍然对其进行编码的原因是因为它会分析 URL 并发现您要包含的值是 URL 参数的值(file=XXX),因此它还对斜杠进行编码(这样您传入的所有内容都将成为file URL 参数值的一部分)。

    如果你打算在服务器端通过URL参数获取这个文件路径,那么template包做的就是正确的方法。

    但要知道,这样做会失去防止代码注入 URL 的安全性。如果您是提供值的人并且您知道它们是安全的,那么就没有问题。但是,如果数据来自用户输入,例如,千万不要这样做。

    另请注意,如果您传递整个 URL(而不仅仅是其中的一部分),它可以在不使用 template.URL 的情况下工作(在 Go Playground 上尝试此变体):

    func main() {
        t := template.Must(template.New("").Parse(templ))
    
        m := map[string]interface{}{
            "FileName": "something.txt",
            "FileURL":  "/file?file=/location/to/something",
        }
    
        if err := t.Execute(os.Stdout, m); err != nil {
            panic(err)
        }
    }
    
    const templ = `<tr><td><a href="{{.FileURL}}">{{.FileName}}</a></td></tr>`
    

    另外请注意,我认为推荐的方法是将文件路径作为 URL 路径的一部分而不是作为参数的值,因此您应该像这样创建 url:

    /file/location/to/something
    

    将您的处理程序(提供文件内容,参见this answer 作为示例)映射到/file/ 模式,当它匹配并调用您的处理程序时,切断路径@ 中的/file/ 前缀987654346@,其余为完整文件路径。如果你选择这个,你也不需要template.URL 转换(因为你包含的值不再是 URL 参数的值):

    func main() {
        t := template.Must(template.New("").Parse(templ))
    
        m := map[string]interface{}{
            "FileName":     "something.txt",
            "FileFullPath": "/location/to/something",
        }
    
        if err := t.Execute(os.Stdout, m); err != nil {
            panic(err)
        }
    }
    
    const templ = `<tr><td><a href="/file{{.FileFullPath}}">{{.FileName}}</a></td></tr>`
    

    Go Playground 上试试这个。

    同样非常重要:永远不要在处理函数中解析模板!详情见:

    It takes too much time when using "template" package to generate a dynamic web page to client in golang

    【讨论】:

    • 谢谢,非常感谢。感谢您解释所涉及的安全风险,在我的情况下它不适用(这只是我在家里使用的东西,不是面向公众的)但它对其他人来说很有用。
    • @basica 另见最后添加的注释:永远不要在处理程序函数中解析模板(它很慢,应该在调用处理程序之前完成)。
    • 不幸的是,这种技术似乎不适用于 JavaScript on* 处理程序,例如 onclick:play.golang.org/p/E_TU3xPAq6s
    • 看起来有一个简单的解决方法哈哈:play.golang.org/p/d0l9Pb26oqT
    • 仅供参考:上述适用于 play.golang.org 的解决方法在我的服务器上不起作用:在“o”和“nclick”之间插入了一个空格。 Sill 正在寻找解决方案。
    猜你喜欢
    • 2010-10-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-04-29
    相关资源
    最近更新 更多