首頁 > 後端開發 > Golang > Waffle 簡介:Go 應用程式的應用程式內 WAF

Waffle 簡介:Go 應用程式的應用程式內 WAF

Patricia Arquette
發布: 2025-01-05 22:42:45
原創
467 人瀏覽過

介紹

Web 應用程式防火牆 (WAF) 長期以來一直是保護 Web 應用程式的標準安全解決方案。 AWS WAF 和 Cloudflare WAF 等基於雲端的 WAF 由於易於實施而特別受歡迎。然而,它們也面臨一些挑戰:

  • 對應用程式上下文的了解有限
  • 誤報率高
  • 受限的自訂邏輯實作

為了應對這些挑戰,一種稱為應用內 WAF 或 RASP(運行時應用程式自我保護)的新方法正在引起人們的注意。

在這篇文章中,我將介紹 Waffle,一個用於將應用程式內 WAF 功能整合到 Go Web 應用程式中的程式庫。

  • https://sitebatch.github.io/waffle-website
  • https://github.com/sitebatch/waffle-go

Introduction to Waffle: In-app WAF for Go Applications

什麼是應用內 WAF/RASP?

應用程式內 WAF/RASP 並不是要取代現有的雲端 WAF,而是透過將 WAF 功能直接嵌入到您的應用程式中來補充它們,以增強保護。
它可以處理常見的 Web 應用程式攻擊,例如 SQL 注入和 XSS,以及應用程式業務邏輯攻擊,例如撞庫和暴力嘗試。

主要優勢是透過完整的請求上下文感知來準確檢測和預防。

考慮這個用於建立部落格文章的 HTTP 請求:

POST /blog/post HTTP/1.1
...

{
  "title": "What is SQL ?"
  "body": "SQL example code: `SELECT * FROM users` ..."
}
登入後複製
登入後複製

如果您的應用程式使用佔位符來安全地建構 SQL 語句,則 SQL 注入是不可能的。但是,依賴模式匹配的基於雲端的 WAF 會阻止此請求,因為它包含可疑的類似 SQL 的模式(字串 SELECT * FROM 會引發 SQL 注入問題)。

開發人員經常發現自己需要繁瑣地調整參數、端點或 WAF 規則來減少這些誤報。好麻煩的任務啊!

相較之下,應用內 WAF / RASP 可以理解請求上下文。它可以識別何時未使用佔位符,並且僅在「實際上可能發生 SQL 注入」時阻止攻擊。這種情境感知方法可以減少誤報,甚至可以幫助緩解零日漏洞。

在 Go 應用程式中使用 Waffle 實作應用程式內 WAF/RASP

Waffle 是一個在 Go Web 應用程式中啟用應用程式內 WAF / RASP 功能的函式庫。

讓我們看看如何將 Waffle 整合到您的應用程式中以及它如何防止攻擊。

應用範例

雖然此範例使用標準函式庫的 net/http,但 Waffle 也支援其他函式庫,例如 Gin 和 GORM。
有關更多詳細信息,請查看支援的庫文檔。

以下應用程式在 /login 端點中存在 SQL 注入漏洞:

POST /blog/post HTTP/1.1
...

{
  "title": "What is SQL ?"
  "body": "SQL example code: `SELECT * FROM users` ..."
}
登入後複製
登入後複製
package main

import (
    "context"
    "database/sql"
    "fmt"
    "net/http"

    _ "github.com/mattn/go-sqlite3"
)

var database *sql.DB

func init() {
    setupDB()
}

func newHTTPHandler() http.Handler {
    mux := http.NewServeMux()
    mux.Handle("/login", http.HandlerFunc(loginController))

    return mux
}

func main() {
    srv := &http.Server{
        Addr:    ":8000",
        Handler: newHTTPHandler(),
    }

    srv.ListenAndServe()
}

func loginController(w http.ResponseWriter, r *http.Request) {
    email := r.FormValue("email")
    password := r.FormValue("password")

    if err := login(r.Context(), email, password); err != nil {
        http.Error(w, err.Error(), http.StatusInternalServerError)
        return
    }

    w.Write([]byte("Login success"))
}

func login(ctx context.Context, email, password string) error {
    // ⚠️ SQL INJECTION VULNERABILITY
    rows, err := database.QueryContext(ctx, fmt.Sprintf("SELECT * FROM users WHERE email = '%s' AND password = '%s'", email, password))
    if err != nil {
        return err
    }
    defer rows.Close()

    if !rows.Next() {
        return fmt.Errorf("invalid email or password")
    }

    // do something

    return nil
}

func setupDB() {
    db, err := sql.Open("sqlite3", "file::memory:?cache=shared")
    if err != nil {
        panic(err)
    }

    if _, err := db.Exec("CREATE TABLE users(id int, email text, password text);"); err != nil {
        panic(err)
    }

    if _, err := db.Exec("INSERT INTO users(id, email, password) VALUES(1, 'user@example.com', 'password');"); err != nil {
        panic(err)
    }

    database = db
}
登入後複製

整合Waffle防止SQL注入

讓我們整合Waffle來防止SQL注入:

$ go run .

# SQL injection attack
$ curl -i -X POST 'http://localhost:8000/login' \
    --data "email=user@example.com' OR 1=1--&password="
HTTP/1.1 200 OK
Date: Sun, 05 Jan 2025 10:32:50 GMT
Content-Length: 13
Content-Type: text/plain; charset=utf-8

Login success
登入後複製

修改main.go如下:

$ go get github.com/sitebatch/waffle-go
登入後複製

變化很小:

package main

import (
    "context"
    "database/sql"
    "errors"
    "fmt"
    "net/http"

    "github.com/sitebatch/waffle-go"
    "github.com/sitebatch/waffle-go/action"
    waffleSQL "github.com/sitebatch/waffle-go/contrib/database/sql"
    waffleHTTP "github.com/sitebatch/waffle-go/contrib/net/http"

    _ "github.com/mattn/go-sqlite3"
)

var database *sql.DB

func init() {
    setupDB()
}

func newHTTPHandler() http.Handler {
    mux := http.NewServeMux()
    mux.Handle("/login", http.HandlerFunc(loginController))

    handler := waffleHTTP.WafMiddleware(mux)

    return handler
}

func main() {
    srv := &http.Server{
        Addr:    ":8000",
        Handler: newHTTPHandler(),
    }

    // Start waffle with debug mode
    waffle.Start(waffle.WithDebug())

    srv.ListenAndServe()
}

func loginController(w http.ResponseWriter, r *http.Request) {
    email := r.FormValue("email")
    password := r.FormValue("password")

    if err := login(r.Context(), email, password); err != nil {
        var actionErr *action.BlockError
        if errors.As(err, &actionErr) {
            return
        }

        http.Error(w, err.Error(), http.StatusInternalServerError)
        return
    }

    w.Write([]byte("Login success"))
}

func login(ctx context.Context, email, password string) error {
    // ⚠️ SQL INJECTION VULNERABILITY
    rows, err := database.QueryContext(ctx, fmt.Sprintf("SELECT * FROM users WHERE email = '%s' AND password = '%s'", email, password))
    if err != nil {
        return err
    }
    defer rows.Close()

    if !rows.Next() {
        return fmt.Errorf("invalid email or password")
    }

    // do something

    return nil
}

func setupDB() {
    db, err := waffleSQL.Open("sqlite3", "file::memory:?cache=shared")
    if err != nil {
        panic(err)
    }

    if _, err := db.Exec("CREATE TABLE users(id int, email text, password text);"); err != nil {
        panic(err)
    }

    if _, err := db.Exec("INSERT INTO users(id, email, password) VALUES(1, 'user@example.com', 'password');"); err != nil {
        panic(err)
    }

    database = db
}
登入後複製

現在,當我們嘗試 SQL 注入攻擊時,Waffle 會阻止它:

diff --git a/main.go b/main.go
index 90b8197..9fefb06 100644
--- a/main.go
+++ b/main.go
@@ -3,9 +3,15 @@ package main
 import (
    "context"
    "database/sql"
+   "errors"
    "fmt"
    "net/http"

+   "github.com/sitebatch/waffle-go"
+   "github.com/sitebatch/waffle-go/action"
+   waffleSQL "github.com/sitebatch/waffle-go/contrib/database/sql"
+   waffleHTTP "github.com/sitebatch/waffle-go/contrib/net/http"
+
    _ "github.com/mattn/go-sqlite3"
 )

@@ -19,7 +25,9 @@ func newHTTPHandler() http.Handler {
    mux := http.NewServeMux()
    mux.Handle("/login", http.HandlerFunc(loginController))

-   return mux
+   handler := waffleHTTP.WafMiddleware(mux)
+
+   return handler
 }

 func main() {
@@ -28,6 +36,9 @@ func main() {
        Handler: newHTTPHandler(),
    }

+   // Start waffle with debug mode
+   waffle.Start(waffle.WithDebug())
+
    srv.ListenAndServe()
 }

@@ -36,6 +47,11 @@ func loginController(w http.ResponseWriter, r *http.Request) {
    password := r.FormValue("password")

    if err := login(r.Context(), email, password); err != nil {
+       var actionErr *action.BlockError
+       if errors.As(err, &actionErr) {
+           return
+       }
+
        http.Error(w, err.Error(), http.StatusInternalServerError)
        return
    }
@@ -60,7 +76,7 @@ func login(ctx context.Context, email, password string) error {
 }

 func setupDB() {
-   db, err := sql.Open("sqlite3", "file::memory:?cache=shared")
+   db, err := waffleSQL.Open("sqlite3", "file::memory:?cache=shared")
    if err != nil {
        panic(err)
    }
登入後複製

此 HTML 是 waffle 預設回傳的錯誤訊息,如下所示:

Introduction to Waffle: In-app WAF for Go Applications

如果使用佔位符:

使用佔位符時,Waffle 會辨識出不可能進行 SQL 注入,並且不會阻止要求:

$ curl -i -X POST 'http://localhost:8000/login' \
    --data "email=user@example.com' OR 1=1--&password=" -i
HTTP/1.1 403 Forbidden
Date: Sun, 05 Jan 2025 10:38:22 GMT
Content-Length: 1574
Content-Type: text/html; charset=utf-8

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Access Denied</title>
登入後複製
# Fix SQL injection vulnerability
diff --git a/main.go
b/main.go
index 9fefb06..5b482f2 100644
--- a/main.go
+++ b/main.go
@@ -60,7 +60,7 @@ func loginController(w http.ResponseWriter, r *http.Request) {
 }

 func login(ctx context.Context, email, password string) error {
-   rows, err := database.QueryContext(ctx, fmt.Sprintf("SELECT * FROM users WHERE email = '%s' AND password = '%s'", email, password))
+   rows, err := database.QueryContext(ctx, "SELECT * FROM users WHERE email = ? AND password = ?", email, password)
    if err != nil {
        return err
    }
登入後複製

請注意,即使在這種情況下,Waffle 仍然可以像基於雲端的 WAF 一樣檢測嘗試的 SQL 注入(儘管它不會阻止它):

# Waffle won't block the request since SQL injection isn't possible
$ curl -i -X POST 'http://localhost:8000/login' \
    --data "email=user@example.com' OR 1=1--&password="
HTTP/1.1 500 Internal Server Error
Content-Type: text/plain; charset=utf-8
X-Content-Type-Options: nosniff
Date: Sun, 05 Jan 2025 10:49:05 GMT
Content-Length: 26

invalid email or password
登入後複製

Waffle 偵測與預防的攻擊

雖然我們已經示範了 SQL 注入預防,但 Waffle 可以偵測並防止各種攻擊:

  • 已知安全掃描儀的偵察
  • 目錄遍歷
  • XSS
  • SQL注入
  • 敏感檔案存取
  • SSRF
  • 帳號接管

有關更多詳細信息,請查看規則列表文件。

規則持續更新,歡迎貢獻。

結論

透過將 Waffle 整合到您的應用程式中,您可以準確地偵測和預防攻擊。

特定於框架的實作指南和詳細使用說明,請參閱文件中的指南部分。

Waffle 正在積極開發中。我們歡迎回饋和貢獻。

  • https://github.com/sitebatch/waffle-go

以上是Waffle 簡介:Go 應用程式的應用程式內 WAF的詳細內容。更多資訊請關注PHP中文網其他相關文章!

來源:dev.to
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
作者最新文章
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板