> 백엔드 개발 > Golang > Waffle 소개: Go 애플리케이션용 인앱 WAF

Waffle 소개: Go 애플리케이션용 인앱 WAF

Patricia Arquette
풀어 주다: 2025-01-05 22:42:45
원래의
467명이 탐색했습니다.

소개

웹 애플리케이션 방화벽(WAF)은 오랫동안 웹 애플리케이션 보호를 위한 표준 보안 솔루션이었습니다. AWS WAF 및 Cloudflare WAF와 같은 클라우드 기반 WAF는 구현이 간편하기 때문에 특히 인기가 높습니다. 그러나 여기에는 몇 가지 과제가 있습니다.

  • 애플리케이션 컨텍스트에 대한 제한된 이해
  • 높은 오탐률
  • 제한된 맞춤 로직 구현

이러한 문제를 해결하기 위해 In-app WAF 또는 RASP(런타임 애플리케이션 자체 보호)라는 새로운 접근 방식이 주목을 받고 있습니다.

이번 게시물에서는 In-app WAF 기능을 Go 웹 애플리케이션에 통합하기 위한 라이브러리인 Waffle을 소개하겠습니다.

  • 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 기능을 애플리케이션에 직접 내장하여 이를 보완하기 위한 것입니다.
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 주입이 실제로 가능"할 때만 공격을 차단합니다. 이러한 상황 인식 접근 방식을 통해 오탐률이 줄어들고 제로데이 취약점을 완화하는 데도 도움이 될 수 있습니다.

Waffle in Go 애플리케이션을 사용하여 인앱 WAF/RASP 구현

Waffle은 Go 웹 애플리케이션에서 In-App 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 통합

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은 와플에서 기본적으로 반환하는 오류 메시지이며 다음과 같습니다.

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을 애플리케이션에 통합하면 공격을 정확하게 탐지하고 예방할 수 있습니다.

프레임워크별 구현 가이드 및 자세한 사용 지침은 설명서의 가이드 섹션을 참조하세요.

와플은 활발하게 개발 중입니다. 피드백과 기여를 환영합니다.

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

위 내용은 Waffle 소개: Go 애플리케이션용 인앱 WAF의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

원천:dev.to
본 웹사이트의 성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.
저자별 최신 기사
인기 튜토리얼
더>
최신 다운로드
더>
웹 효과
웹사이트 소스 코드
웹사이트 자료
프론트엔드 템플릿