Heim >Backend-Entwicklung >Golang >Einführung in Waffle: In-App-WAF für Go-Anwendungen

Einführung in Waffle: In-App-WAF für Go-Anwendungen

Patricia Arquette
Patricia ArquetteOriginal
2025-01-05 22:42:45469Durchsuche

Einführung

Web Application Firewalls (WAF) sind seit langem eine Standard-Sicherheitslösung zum Schutz von Webanwendungen. Cloudbasierte WAFs wie AWS WAF und Cloudflare WAF erfreuen sich aufgrund ihrer einfachen Implementierung besonders großer Beliebtheit. Sie bringen jedoch mehrere Herausforderungen mit sich:

  • Eingeschränktes Verständnis des Anwendungskontexts
  • Hohe Rate falsch positiver Ergebnisse
  • Eingeschränkte benutzerdefinierte Logikimplementierung

Um diese Herausforderungen anzugehen, hat ein neuer Ansatz namens In-App WAF oder RASP (Runtime Application Self-Protection) an Aufmerksamkeit gewonnen.

In diesem Beitrag stelle ich Waffle vor, eine Bibliothek zur Integration von In-App-WAF-Funktionen in Go-Webanwendungen.

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

Introduction to Waffle: In-app WAF for Go Applications

Was ist In-App WAF/RASP?

In-App-WAF/RASP ist nicht dazu gedacht, bestehende Cloud-WAFs zu ersetzen, sondern sie vielmehr zu ergänzen, indem die WAF-Funktionalität direkt in Ihre Anwendung eingebettet wird, um den Schutz zu verbessern.
Es kann gängige Webanwendungsangriffe wie SQL-Injection und XSS sowie Angriffe auf die Geschäftslogik von Anwendungen wie Credential Stuffing und Brute-Force-Versuche bewältigen.

Der Hauptvorteil ist die genaue Erkennung und Prävention durch vollständige Kenntnis des Anforderungskontexts.

Bedenken Sie diese HTTP-Anfrage zum Erstellen eines Blogbeitrags:

POST /blog/post HTTP/1.1
...

{
  "title": "What is SQL ?"
  "body": "SQL example code: `SELECT * FROM users` ..."
}

Wenn Ihre Anwendung Platzhalter verwendet, um SQL-Anweisungen sicher zu erstellen, ist eine SQL-Injection nicht möglich. Cloudbasierte WAFs, die auf Mustervergleich basieren, würden diese Anfrage jedoch blockieren, da sie verdächtige SQL-ähnliche Muster enthält (die Zeichenfolge SELECT * FROM wirft Bedenken hinsichtlich der SQL-Injection auf).

Entwickler müssen oft mühsam Parameter, Endpunkte oder WAF-Regeln anpassen, um diese Fehlalarme zu reduzieren. Was für eine umständliche Aufgabe!

Im Gegensatz dazu versteht In-App WAF/RASP den Anforderungskontext. Es erkennt, wenn keine Platzhalter verwendet werden und blockiert Angriffe nur dann, wenn „SQL-Injection tatsächlich möglich ist“. Dieser kontextbewusste Ansatz führt zu weniger Fehlalarmen und kann sogar dazu beitragen, Zero-Day-Schwachstellen zu mindern.

Implementierung von In-App WAF/RASP mit Waffle in Go-Anwendungen

Waffle ist eine Bibliothek, die In-App WAF/RASP-Funktionalität in Go-Webanwendungen ermöglicht.

Sehen wir uns an, wie Sie Waffle in Ihre Anwendung integrieren und wie es Angriffe verhindert.

Beispielanwendung

Während dieses Beispiel net/http der Standardbibliothek verwendet, unterstützt Waffle auch andere Bibliotheken wie Gin und GORM.
Weitere Informationen finden Sie in der Dokumentation zu den unterstützten Bibliotheken.

Die folgende Anwendung weist eine SQL-Injection-Schwachstelle im /login-Endpunkt auf:

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
}

Integration von Waffle zur Verhinderung von SQL-Injection

Lassen Sie uns Waffle integrieren, um SQL-Injection zu verhindern:

$ 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

Ändern Sie main.go wie folgt:

$ go get github.com/sitebatch/waffle-go

Die Änderungen sind minimal:

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
}

Wenn wir jetzt einen SQL-Injection-Angriff versuchen, blockiert Waffle ihn:

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)
    }

Dieser HTML-Code ist die standardmäßig von Waffle zurückgegebene Fehlermeldung und sieht folgendermaßen aus:

Introduction to Waffle: In-app WAF for Go Applications

Bei Verwendung von Platzhaltern:

Bei der Verwendung von Platzhaltern erkennt Waffle, dass eine SQL-Injection nicht möglich ist und blockiert die Anfrage nicht:

$ 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
    }

Beachten Sie, dass Waffle selbst in diesem Fall immer noch versuchte SQL-Injection wie eine cloudbasierte WAF erkennen kann (obwohl es diese nicht blockiert):

# 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

Angriffe werden von Waffle erkannt und verhindert

Während wir die Verhinderung von SQL-Injection demonstriert haben, kann Waffle verschiedene Angriffe erkennen und verhindern:

  • Aufklärung durch bekannte Sicherheitsscanner
  • Verzeichnisdurchlauf
  • XSS
  • SQL-Injection
  • Zugriff auf vertrauliche Dateien
  • SSRF
  • Kontoübernahme

Weitere Einzelheiten finden Sie in der Dokumentation zur Regelliste.

Die Regeln werden kontinuierlich aktualisiert und Beiträge sind willkommen.

Abschluss

Durch die Integration von Waffle in Ihre Anwendung können Sie Angriffe genau erkennen und verhindern.

Framework-spezifische Implementierungsleitfäden und detaillierte Nutzungsanweisungen finden Sie im Abschnitt „Leitfäden“ in der Dokumentation.

Waffle befindet sich in aktiver Entwicklung. Wir freuen uns über Feedback und Beiträge.

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

Das obige ist der detaillierte Inhalt vonEinführung in Waffle: In-App-WAF für Go-Anwendungen. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn