网络请求中使用的不受控制的数据¶
ID: go/request-forgery
Kind: path-problem
Security severity: 9.1
Severity: error
Precision: high
Tags:
- security
- external/cwe/cwe-918
Query suites:
- go-code-scanning.qls
- go-security-extended.qls
- go-security-and-quality.qls
将用户输入直接合并到 HTTP 请求中而不验证输入会导致不同类型的请求伪造攻击,攻击者实际上可以控制请求。如果易受攻击的请求位于服务器端代码中,则可以绕过安全机制,例如外部防火墙。如果易受攻击的请求位于客户端代码中,则不知情的用户可能会向其他服务器发送恶意请求,从而可能导致 DDOS 攻击。
建议¶
为了防止请求伪造,建议避免将用户输入直接放入网络请求中。如果需要灵活的网络请求机制,建议维护一个授权请求目标列表,并根据提供的用户输入从该列表中进行选择。
示例¶
以下示例显示了直接在 URL 请求中使用的 HTTP 请求参数,而没有验证输入,这会导致 SSRF 攻击。由于攻击者可以选择 target
的值,因此请求 http.Get(...)
很容易受到攻击。例如,攻击者可以选择 "internal.example.com/#"
作为目标,从而导致请求中使用的 URL 为 "https://internal.example.com/#.example.com/data"
。
如果服务器 https://internal.example.com
不应该从攻击者的计算机直接访问,则对该服务器的请求可能会出现问题。
package main
import (
"net/http"
)
func handler(w http.ResponseWriter, req *http.Request) {
target := req.FormValue("target")
// BAD: `target` is controlled by the attacker
resp, err := http.Get("https://" + target + ".example.com/data/")
if err != nil {
// error handling
}
// process request response
use(resp)
}
解决此问题的一种方法是在执行请求之前使用用户输入选择一个已知的固定字符串
package main
import (
"net/http"
)
func handler1(w http.ResponseWriter, req *http.Request) {
target := req.FormValue("target")
var subdomain string
if target == "EU" {
subdomain = "europe"
} else {
subdomain = "world"
}
// GOOD: `subdomain` is controlled by the server
resp, err := http.Get("https://" + subdomain + ".example.com/data/")
if err != nil {
// error handling
}
// process request response
use(resp)
}