forked from corazawaf/coraza
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Revert "remove example directory to keep things simple"
This reverts commit 825e8ea.
- Loading branch information
Showing
11 changed files
with
286 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
http-server |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
# HTTP-Server with Coraza | ||
|
||
This example is intended to provide a straightforward way to spin up Coraza and grasp its behaviour. | ||
|
||
## Run the example | ||
|
||
```bash | ||
go run . | ||
``` | ||
|
||
The server will be reachable at `http://localhost:8090`. | ||
|
||
```bash | ||
# True positive request (403 Forbidden) | ||
curl -i 'localhost:8090/hello?id=0' | ||
# True negative request (200 OK) | ||
curl -i 'localhost:8090/hello' | ||
``` | ||
|
||
You can customise the rules to be used by using the `DIRECTIVES_FILE` environment variable to load a directives file: | ||
|
||
```bash | ||
DIRECTIVES_FILE=my_directives.conf go run . | ||
``` | ||
|
||
You can also customise response body and response headers by using `RESPONSE_HEADERS` and `RESPONSE_BODY` environment variables respectively: | ||
|
||
```bash | ||
RESPONSE_BODY=creditcard go run . | ||
``` | ||
|
||
And then | ||
|
||
```bash | ||
# True positive request (403 Forbidden) due to matching response body | ||
curl -i 'localhost:8090/hello' | ||
``` | ||
|
||
## Customize WAF rules | ||
|
||
The configuration of the WAF relies on [default.conf](https://github.com/corazawaf/coraza/blob/main/examples/http-server/default.conf). Feel free to play with it. | ||
|
||
## Customize server behaviour | ||
|
||
The following snippet shows an example of code that may be added to the [exampleHandler](https://github.com/corazawaf/coraza/blob/main/examples/http-server/main.go#L17) in order to make the example capable of echoing the body request. It comes in handy for testing rules that match the response body. | ||
|
||
```go | ||
func exampleHandler(w http.ResponseWriter, req *http.Request) { | ||
w.Header().Set("Content-Type", "text/plain") | ||
var buf bytes.Buffer | ||
_, err := io.Copy(&buf, req.Body) | ||
if err != nil { | ||
log.Fatalf("handler can not read request body: %v", err) | ||
} | ||
w.Write(buf.Bytes()) | ||
} | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
SecDebugLogLevel 9 | ||
SecDebugLog /dev/stdout | ||
|
||
SecRule ARGS:id "@eq 0" "id:1, phase:1,deny, status:403,msg:'Invalid id',log,auditlog" | ||
|
||
SecRequestBodyAccess On | ||
SecRule REQUEST_BODY "@contains password" "id:100, phase:2,deny, status:403,msg:'Invalid request body',log,auditlog" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
module github.com/corazawaf/coraza/v3/examples/http-server | ||
|
||
go 1.19 | ||
|
||
require github.com/corazawaf/coraza/v3 v3.0.0-20220914101451-05d352c89b24 | ||
|
||
require ( | ||
github.com/magefile/mage v1.15.0 // indirect | ||
github.com/tidwall/gjson v1.17.0 // indirect | ||
github.com/tidwall/match v1.1.1 // indirect | ||
github.com/tidwall/pretty v1.2.1 // indirect | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
github.com/corazawaf/coraza/v3 v3.0.0-20220914101451-05d352c89b24 h1:dy3992o5ue40g1QWKupjsBwZTRWagsuiGcOsbV0b4xs= | ||
github.com/corazawaf/coraza/v3 v3.0.0-20220914101451-05d352c89b24/go.mod h1:xhc7feR6FUfYgmBmRw3UObvLiyzT3XPQtlJD+huy+Mc= | ||
github.com/magefile/mage v1.15.0 h1:BvGheCMAsG3bWUDbZ8AyXXpCNwU9u5CB6sM+HNb9HYg= | ||
github.com/magefile/mage v1.15.0/go.mod h1:z5UZb/iS3GoOSn0JgWuiw7dxlurVYTu+/jHXqQg881A= | ||
github.com/tidwall/gjson v1.17.0 h1:/Jocvlh98kcTfpN2+JzGQWQcqrPQwDrVEMApx/M5ZwM= | ||
github.com/tidwall/gjson v1.17.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= | ||
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= | ||
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= | ||
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= | ||
github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4= | ||
github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
package main | ||
|
||
import ( | ||
"fmt" | ||
"log" | ||
"net/http" | ||
"os" | ||
"strings" | ||
|
||
"github.com/crowdsecurity/coraza/v3" | ||
txhttp "github.com/crowdsecurity/coraza/v3/http" | ||
"github.com/crowdsecurity/coraza/v3/types" | ||
) | ||
|
||
func exampleHandler(w http.ResponseWriter, req *http.Request) { | ||
w.Header().Set("Content-Type", "text/plain") | ||
resBody := "Hello world, transaction not disrupted." | ||
|
||
if body := os.Getenv("RESPONSE_BODY"); body != "" { | ||
resBody = body | ||
} | ||
|
||
if h := os.Getenv("RESPONSE_HEADERS"); h != "" { | ||
key, val, _ := strings.Cut(h, ":") | ||
w.Header().Set(key, val) | ||
} | ||
|
||
// The server generates the response | ||
w.Write([]byte(resBody)) | ||
} | ||
|
||
func main() { | ||
waf := createWAF() | ||
|
||
http.Handle("/", txhttp.WrapHandler(waf, http.HandlerFunc(exampleHandler))) | ||
|
||
fmt.Println("Server is running. Listening port: 8090") | ||
|
||
log.Fatal(http.ListenAndServe(":8090", nil)) | ||
} | ||
|
||
func createWAF() coraza.WAF { | ||
directivesFile := "./default.conf" | ||
if s := os.Getenv("DIRECTIVES_FILE"); s != "" { | ||
directivesFile = s | ||
} | ||
|
||
waf, err := coraza.NewWAF( | ||
coraza.NewWAFConfig(). | ||
WithErrorCallback(logError). | ||
WithDirectivesFromFile(directivesFile), | ||
) | ||
if err != nil { | ||
log.Fatal(err) | ||
} | ||
return waf | ||
} | ||
|
||
func logError(error types.MatchedRule) { | ||
msg := error.ErrorLog() | ||
fmt.Printf("[logError][%s] %s\n", error.Rule().Severity(), msg) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,115 @@ | ||
package main | ||
|
||
import ( | ||
"bytes" | ||
"log" | ||
"net/http" | ||
"net/http/httptest" | ||
"os" | ||
"testing" | ||
|
||
txhttp "github.com/crowdsecurity/coraza/v3/http" | ||
) | ||
|
||
func setupTestServer(t *testing.T) *httptest.Server { | ||
t.Helper() | ||
waf := createWAF() | ||
return httptest.NewServer(txhttp.WrapHandler(waf, http.HandlerFunc(exampleHandler))) | ||
} | ||
|
||
func doGetRequest(t *testing.T, getPath string) int { | ||
t.Helper() | ||
resp, err := http.Get(getPath) | ||
if err != nil { | ||
log.Fatalln(err) | ||
} | ||
resp.Body.Close() | ||
return resp.StatusCode | ||
} | ||
|
||
func doPostRequest(t *testing.T, postPath string, data []byte) int { | ||
t.Helper() | ||
resp, err := http.Post(postPath, "application/x-www-form-urlencoded", bytes.NewBuffer(data)) | ||
if err != nil { | ||
log.Fatalln(err) | ||
} | ||
resp.Body.Close() | ||
return resp.StatusCode | ||
} | ||
|
||
func TestHttpServer(t *testing.T) { | ||
tests := []struct { | ||
name string | ||
path string | ||
expStatus int | ||
envVars map[string]string | ||
body []byte // if body is populated, POST request is sent | ||
}{ | ||
{"negative", "/", 200, nil, nil}, | ||
{"positive for query parameter", "/?id=0", 403, nil, nil}, | ||
{ | ||
"positive for response body", | ||
"/", | ||
403, | ||
map[string]string{ | ||
"DIRECTIVES_FILE": "./testdata/response-body.conf", | ||
"RESPONSE_BODY": "creditcard", | ||
}, | ||
nil, | ||
}, | ||
{ | ||
"positive for response header", | ||
"/", | ||
403, | ||
map[string]string{ | ||
"DIRECTIVES_FILE": "./testdata/response-headers.conf", | ||
"RESPONSE_HEADERS": "foo:bar", | ||
}, | ||
nil, | ||
}, | ||
{ | ||
"negative for request body process partial (payload beyond processed body)", | ||
"/", | ||
200, | ||
map[string]string{ | ||
"DIRECTIVES_FILE": "./testdata/request-body-limits-processpartial.conf", | ||
}, | ||
[]byte("beyond the limit script"), | ||
}, | ||
{ | ||
"positive for response body limit reject", | ||
"/", | ||
413, | ||
map[string]string{ | ||
"DIRECTIVES_FILE": "./testdata/response-body-limits-reject.conf", | ||
"RESPONSE_BODY": "response body beyond the limit", | ||
}, | ||
nil, | ||
}, | ||
} | ||
// Perform tests | ||
for _, tc := range tests { | ||
tt := tc | ||
var statusCode int | ||
t.Run(tt.name, func(t *testing.T) { | ||
if len(tt.envVars) > 0 { | ||
for k, v := range tt.envVars { | ||
os.Setenv(k, v) | ||
defer os.Unsetenv(k) | ||
} | ||
} | ||
|
||
// Spin up the test server | ||
testServer := setupTestServer(t) | ||
defer testServer.Close() | ||
if tt.body == nil { | ||
statusCode = doGetRequest(t, testServer.URL+tt.path) | ||
} else { | ||
statusCode = doPostRequest(t, testServer.URL+tt.path, tt.body) | ||
} | ||
if want, have := tt.expStatus, statusCode; want != have { | ||
t.Errorf("Unexpected status code, want: %d, have: %d", want, have) | ||
} | ||
}) | ||
} | ||
} |
7 changes: 7 additions & 0 deletions
7
examples/http-server/testdata/request-body-limits-processpartial.conf
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
SecDebugLogLevel 9 | ||
SecDebugLog /dev/stdout | ||
SecRequestBodyAccess On | ||
SecRequestBodyInMemoryLimit 5 | ||
SecRequestBodyLimit 6 | ||
SecRequestBodyLimitAction ProcessPartial | ||
SecRule REQUEST_BODY "@contains script" "id:200, phase:2, deny, status:403, msg:'Invalid request body',log,auditlog" |
6 changes: 6 additions & 0 deletions
6
examples/http-server/testdata/response-body-limits-reject.conf
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
SecDebugLogLevel 9 | ||
SecDebugLog /dev/stdout | ||
SecResponseBodyAccess On | ||
SecResponseBodyMimeType text/plain | ||
SecResponseBodyLimit 6 | ||
SecResponseBodyLimitAction Reject |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
SecDebugLogLevel 9 | ||
SecDebugLog /dev/stdout | ||
SecResponseBodyAccess On | ||
SecResponseBodyMimeType text/plain | ||
SecRule RESPONSE_BODY "@contains creditcard" "id:200, phase:4,deny, status:403,msg:'Invalid response body',log,auditlog" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
SecDebugLogLevel 9 | ||
SecDebugLog /dev/stdout | ||
SecRule RESPONSE_HEADERS:Foo "@pm bar" "id:199,phase:3,deny,t:lowercase,deny, status:403,msg:'Invalid response header',log,auditlog" |