Golang中net/http包使用问题(已解决)
Golang中net/http包使用问题(已解决) 我发现以下代码按预期工作:
http.HandleFunc("/@",startHandler)
然而,如果我将 @ 替换为 #,我会得到服务器账户的目录列表。
我认为这可能是一个安全问题。
毫无疑问,这对某些人来说非常有用。
我可以轻松地自签名用于本地测试。
问题出现在我重定向到HTTPS时,除非您拥有证书和密钥,否则可能无法进行测试…
浏览器不会将 # 及其后的任何内容提交到服务器。之后你的服务器如何处理 / 是你的路由细节。
Go 语言方面不存在安全问题。
请提供一个最小、完整、可验证的示例,我怀疑这是Go语言的问题,而是与你的反向代理设置有关…
将你的处理器设置为 /@ 并发送请求 /,你看到索引了吗?
请同时提供一个我们可以本地编译和运行以验证你观察结果的 sscce。
我为此道歉,因为这是在从 HTTP 重定向到 HTTPS 的过程中发生的,而不是在 WebSocket 启动时,所以可能不存在安全漏洞。但这仍然不应该发生。
以下代码片段可能有助于您重现该问题:
func redirectToHttps(w http.ResponseWriter, r *http.Request) {
http.Redirect(w, r, "https://xxxxxxxxxxxx.com:"+cfg.PortS+"/#", 307) // 临时用于测试
}
如果黑客能够获取基于Go net/http的服务器目录列表,那么这就是一个安全漏洞。
仅仅依赖客户端协议永不发送特定字符串是不够的。黑客发送一个被禁止的字符串,不应从服务器端获得目录列表作为“回报”。维护net/http的人员应该调查此事,因为其他序列可能被以更恶意的方式利用。
我非常清楚HTTP路由的工作原理。我使用/start来启动WebSocket通信,并决定尝试/#。正如我所说,/@完全能满足我的需求。
我认为net/http处理/#的方式并不令人满意。
感谢您的回复。
我的客户端是 Firefox 68.5.0 ESR。网页正常显示后,会像通常的 WebSocket 一样向服务器发送 WebSocket 响应。我碰巧发送了一个 '/'#’,结果惊讶地发现 Firefox 显示了一个目录列表。这是由 net/http 内部处理的,我的处理程序(负责设置 WebSocket 的服务端)根本没有触及它。
这显然是一个后门。黑客可以轻易伪装成浏览器,从而获取我服务器的目录列表,我认为这是不可取的。黑客对我的主机了解得越少越好。
此致, Bill
对我来说,这段代码按预期工作,Go 返回了一个 404 错误:
package main
import (
"net/http"
)
func main() {
http.HandleFunc("/#", f)
err := http.ListenAndServe(":8080", nil)
panic(err)
}
func f(w http.ResponseWriter, r *http.Request) {
http.Redirect(w, r, "http://example.com/", 307)
}
$ curl localhost:8080/#
404 page not found
如果你能基于这段代码复现你的问题,请回复告知。
再次强调,浏览器不会发送 /#,浏览器不会发送“锚点”。浏览器发送的是 /,至于服务器、反向代理以及其他组件如何响应,这取决于它们的配置。
你的 /# 处理器不会被 / 请求触发。
WebSocket 与此无关。
// 代码示例:一个简单的HTTP请求处理器
package main
import (
"fmt"
"net/http"
)
func main() {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello, you've requested: %s\n", r.URL.Path)
})
http.ListenAndServe(":8080", nil)
}
再补充一些代码
func main(){
go func (){
http.HandleFunc("/#",startHandler)
var err error
err=http.ListenAndServeTLS(":"+cfg.PortS,“chain.pem”,“key.pem”,nil)
time.Sleep(75*time.Millisecond)
if err!=nil{ml.LogFail(fmt.Sprint(err)) }
ml.Log(“HTTPS server has shutdown”)
os.Exit(2)
}()
go func (){
http.ListenAndServe(":"+cfg.Port, http.HandlerFunc(redirectToHttps))
ml.Log("MIPS HTTP server has shutdown")}()
}
fune redirectToHttps(w http.ResponseWriter, r *http.Request) {
http.Redirect(w, r, “https://xxxxxxxxxxxx.com:”+cfg.PortS+"/#", 307) // temporary for testing
}
希望这些能有所帮助
这是一个正确的观察。在Go的net/http包中,#字符在路由模式中有特殊含义。当使用http.HandleFunc()时,模式中的#会被视为锚点标记,导致路由无法正确匹配,从而回退到文件服务器行为(如果配置了的话)。
问题分析:
@是普通字符,在路由模式中没有特殊含义#在URL中通常表示片段标识符(fragment),浏览器不会将其发送到服务器net/http包的路由模式解析对#有特殊处理
示例代码说明:
package main
import (
"fmt"
"net/http"
)
func startHandler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Handler called for path: %s", r.URL.Path)
}
func main() {
// 这个可以正常工作
http.HandleFunc("/@", startHandler)
// 这个可能不会按预期工作
http.HandleFunc("/#", startHandler) // #会被特殊处理
// 安全建议:避免在路由中使用特殊字符
// 如果需要处理特殊字符,可以使用更灵活的路由器
http.HandleFunc("/hash/", func(w http.ResponseWriter, r *http.Request) {
// 手动解析路径
if r.URL.Path == "/hash/#" {
startHandler(w, r)
}
})
http.ListenAndServe(":8080", nil)
}
替代方案:
如果确实需要处理包含#的路径,可以考虑以下方法:
// 方法1:使用URL编码
http.HandleFunc("/%23", startHandler) // %23是#的URL编码
// 方法2:使用第三方路由器(如gorilla/mux)
import "github.com/gorilla/mux"
func main() {
r := mux.NewRouter()
r.HandleFunc("/#", startHandler) // gorilla/mux能正确处理
http.ListenAndServe(":8080", r)
}
// 方法3:自定义处理器
type customHandler struct{}
func (h *customHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if r.URL.Path == "/#" {
startHandler(w, r)
return
}
http.NotFound(w, r)
}
func main() {
http.Handle("/", &customHandler{})
http.ListenAndServe(":8080", nil)
}
安全注意事项:
- 避免在路由模式中使用特殊字符
- 始终对用户输入进行验证和清理
- 考虑使用成熟的第三方路由库来处理复杂路由需求
- 确保文件服务器配置正确,避免目录遍历攻击
这个行为不是安全漏洞,而是HTTP规范和路由设计的特性。正确配置服务器和谨慎处理用户输入可以避免相关问题。


