从用户输入创建的日志条目¶
ID: go/log-injection
Kind: path-problem
Security severity: 7.8
Severity: error
Precision: medium
Tags:
- security
- external/cwe/cwe-117
Query suites:
- go-security-extended.qls
- go-security-and-quality.qls
如果未经处理的用户输入被写入日志条目,则恶意用户可能能够伪造新的日志条目。
如果用户提供了一些在显示日志输出时会被解释的字符的输入,则可能会发生伪造。如果日志显示为纯文本文件,则恶意用户可以使用换行符。如果日志显示为 HTML,则可以包含任意 HTML 来欺骗日志条目。
建议¶
在记录用户输入之前,应先对其进行适当的编码。
如果日志条目是纯文本,则应使用 strings.Replace
或类似方法从用户输入中删除换行符。还应注意,用户输入在日志条目中应清晰标记,并且恶意用户不能以其他方式造成混淆。
对于将以 HTML 格式显示的日志条目,应在记录之前使用 html.EscapeString
或类似方法对用户输入进行 HTML 编码,以防止伪造和其他形式的 HTML 注入。
示例¶
在以下示例中,用户提供的用户名使用日志记录框架进行记录,而无需任何清理。
package main
import (
"log"
"net/http"
)
// BAD: A user-provided value is written directly to a log.
func handler(req *http.Request) {
username := req.URL.Query()["username"][0]
log.Printf("user %s logged in.\n", username)
}
在下一个示例中,使用 strings.Replace
确保用户输入中不存在换行符。
package main
import (
"log"
"net/http"
"strings"
)
// GOOD: The user-provided value is escaped before being written to the log.
func handlerGood(req *http.Request) {
username := req.URL.Query()["username"][0]
escapedUsername := strings.ReplaceAll(username, "\n", "")
escapedUsername = strings.ReplaceAll(escapedUsername, "\r", "")
log.Printf("user %s logged in.\n", escapedUsername)
}