Web アプリケーション ファイアウォール (WAF) は、Web アプリケーションを保護するための標準的なセキュリティ ソリューションとして長い間使用されてきました。 AWS WAF や Cloudflare WAF などのクラウドベースの WAF は、実装が簡単なため特に人気があります。ただし、いくつかの課題があります:
これらの課題に対処するために、アプリ内 WAF または RASP (Runtime Application Self-Protection) と呼ばれる新しいアプローチが注目を集めています。
この投稿では、アプリ内 WAF 機能を Go Web アプリケーションに統合するためのライブラリである Waffle を紹介します。
アプリ内 WAF/RASP は、既存のクラウド WAF を置き換えることを目的としたものではなく、WAF 機能をアプリケーションに直接埋め込んで保護を強化することでクラウド WAF を補完することを目的としています。
SQL インジェクションや XSS などの一般的な Web アプリケーション攻撃だけでなく、資格情報スタッフィングやブルート フォース攻撃などのアプリケーション ビジネス ロジック攻撃も処理できます。
主な利点は、完全なリクエスト コンテキスト認識による正確な検出と防止です。
ブログ投稿を作成するための次の 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 インジェクションが実際に可能である」場合にのみ攻撃をブロックします。このコンテキスト認識型のアプローチにより、誤検知が減少し、ゼロデイ脆弱性の軽減にも役立ちます。
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 }
SQL インジェクションを防ぐために Waffle を統合しましょう:
$ 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 によってデフォルトで返されるエラー メッセージで、次のようになります。
プレースホルダーを使用すると、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
SQL インジェクションの防止を実証しましたが、Waffle はさまざまな攻撃を検出して防止できます。
詳細については、ルール リストのドキュメントを参照してください。
ルールは継続的に更新され、貢献は歓迎されます。
Waffle をアプリケーションに統合することで、攻撃を正確に検出して防止できます。
フレームワーク固有の実装ガイドと詳細な使用手順については、ドキュメントの「ガイド」セクションを参照してください。
ワッフルは鋭意開発中です。フィードバックや貢献を歓迎します。
以上がWaffle の紹介: Go アプリケーション用のアプリ内 WAFの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。