常见安全问题及处理
- 输入验证
- 问题:恶意用户可能发送不符合预期格式的请求,导致程序解析错误或执行非预期操作。
- 处理:对HTTP请求中的参数、路径等进行严格的格式验证。
- 示例:
package main
import (
"fmt"
"net/http"
"strconv"
)
func handler(w http.ResponseWriter, r *http.Request) {
idStr := r.URL.Query().Get("id")
id, err := strconv.Atoi(idStr)
if err != nil || id <= 0 {
http.Error(w, "Invalid id parameter", http.StatusBadRequest)
return
}
fmt.Fprintf(w, "Valid id: %d", id)
}
func main() {
http.HandleFunc("/", handler)
http.ListenAndServe(":8080", nil)
}
- 防止SQL注入(如果涉及数据库操作)
- 问题:恶意用户通过构造特殊的SQL语句在HTTP请求参数中,可能获取或修改数据库中的敏感数据。
- 处理:使用参数化查询。
- 示例:假设使用
database/sql
包和SQLite3数据库
package main
import (
"database/sql"
"fmt"
"net/http"
_ "github.com/mattn/go - sqlite3"
)
func queryHandler(w http.ResponseWriter, r *http.Request) {
idStr := r.URL.Query().Get("id")
id, err := strconv.Atoi(idStr)
if err != nil || id <= 0 {
http.Error(w, "Invalid id parameter", http.StatusBadRequest)
return
}
db, err := sql.Open("sqlite3", "test.db")
if err != nil {
http.Error(w, "Database connection error", http.StatusInternalServerError)
return
}
defer db.Close()
var name string
err = db.QueryRow("SELECT name FROM users WHERE id =?", id).Scan(&name)
if err != nil {
http.Error(w, "Query error", http.StatusInternalServerError)
return
}
fmt.Fprintf(w, "User name for id %d is %s", id, name)
}
func main() {
http.HandleFunc("/query", queryHandler)
http.ListenAndServe(":8080", nil)
}
- 防止跨站脚本攻击(XSS)
- 问题:恶意用户通过在输入字段中注入恶意脚本,当页面被其他用户访问时,脚本会在其浏览器上执行,可能窃取用户的会话信息等。
- 处理:对输出到页面的数据进行转义。如果使用模板引擎,大多数模板引擎会自动转义数据。如果手动处理,可使用
html.EscapeString
函数。
- 示例:
package main
import (
"html"
"fmt"
"net/http"
)
func xssHandler(w http.ResponseWriter, r *http.Request) {
input := r.URL.Query().Get("input")
escaped := html.EscapeString(input)
fmt.Fprintf(w, "Escaped input: %s", escaped)
}
func main() {
http.HandleFunc("/xss", xssHandler)
http.ListenAndServe(":8080", nil)
}
- 防止跨站请求伪造(CSRF)
- 问题:攻击者诱导用户访问一个包含恶意请求的页面,利用用户已登录的会话,在用户不知情的情况下执行操作。
- 处理:使用CSRF令牌。在用户登录时生成一个随机令牌,存储在用户会话中,并在每个表单或敏感请求中包含该令牌。服务器验证请求中的令牌与会话中的令牌是否匹配。
- 示例:实现较为复杂,这里仅给出基本思路,使用
gorilla/sessions
包来管理会话和CSRF令牌。
package main
import (
"net/http"
"github.com/gorilla/sessions"
)
var store = sessions.NewCookieStore([]byte("your - secret - key"))
func csrfHandler(w http.ResponseWriter, r *http.Request) {
session, _ := store.Get(r, "session - name")
token := session.Values["csrf_token"]
if token == nil {
token = generateCSRFToken()
session.Values["csrf_token"] = token
session.Save(r, w)
}
// 在表单中输出令牌<input type="hidden" name="csrf_token" value="{{.CSRFToken }}">
// 验证请求时:
reqToken := r.FormValue("csrf_token")
if reqToken != token {
http.Error(w, "CSRF token mismatch", http.StatusForbidden)
return
}
// 处理请求
}
func generateCSRFToken() string {
// 生成随机字符串的逻辑
return "random - token - value"
}
func main() {
http.HandleFunc("/csrf", csrfHandler)
http.ListenAndServe(":8080", nil)
}
- 限制请求大小
- 问题:恶意用户可能发送超大的请求,耗尽服务器资源。
- 处理:设置请求体大小限制。
- 示例:
package main
import (
"fmt"
"net/http"
)
func limitHandler(w http.ResponseWriter, r *http.Request) {
r.Body = http.MaxBytesReader(w, r.Body, 1024*1024) // 限制为1MB
// 处理请求
fmt.Fprintf(w, "Request within size limit")
}
func main() {
http.HandleFunc("/limit", limitHandler)
http.ListenAndServe(":8080", nil)
}
错误处理
- HTTP错误处理
- 问题:处理请求过程中如果发生错误,需要返回合适的HTTP状态码,避免泄露内部错误信息。
- 处理:使用
http.Error
函数返回不同状态码的错误信息。如上述示例中,当参数无效时返回http.StatusBadRequest
,数据库连接错误返回http.StatusInternalServerError
等。
- 一般错误处理
- 问题:在程序执行过程中,如文件读取、网络连接等操作可能失败,需要妥善处理这些错误,防止程序崩溃。
- 处理:使用Go语言的
if err!= nil
语句来捕获并处理错误。例如在打开数据库连接时,检查sql.Open
的错误并返回合适的HTTP错误响应。