Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Waf SQLI PR #2

Open
wants to merge 26 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions waf_sqli/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
.bazelrc
clang.bazelrc
/bazel-*

*/*.o
*.wasm
23 changes: 23 additions & 0 deletions waf_sqli/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
PROXY_WASM_CPP_SDK=/sdk

all: waf_sqli.wasm

# The following are copied from ${PROXY_WASM_CPP_SDK}/Makefile.base_lite
# The UTILITY_LIB and UTILITY_HEADER are newly added
ifdef NO_CONTEXT
CPP_CONTEXT_LIB =
else
CPP_CONTEXT_LIB = ${PROXY_WASM_CPP_SDK}/proxy_wasm_intrinsics.cc
endif

UTILITY_LIB = utility/*.cc utility/libinjection/*.c
UTILITY_HEADER = utility/*.h utility/libinjection/*.h utility/nlohmann/json.hpp

%.wasm %.wat: %.cc ${PROXY_WASM_CPP_SDK}/proxy_wasm_intrinsics.h ${PROXY_WASM_CPP_SDK}/proxy_wasm_enums.h ${PROXY_WASM_CPP_SDK}/proxy_wasm_externs.h ${PROXY_WASM_CPP_SDK}/proxy_wasm_api.h ${PROXY_WASM_CPP_SDK}/proxy_wasm_intrinsics.js ${CPP_CONTEXT_LIB} ${UTILITY_HEADER}
em++ -s STANDALONE_WASM=1 -s EMIT_EMSCRIPTEN_METADATA=1 -s EXPORTED_FUNCTIONS=['_malloc'] --std=c++17 -O3 -flto -s WASM_OBJECT_FILES=0 --llvm-lto 1 -DPROXY_WASM_PROTOBUF_LITE=1 -I${PROXY_WASM_CPP_SDK} -I/usr/local/include --js-library ${PROXY_WASM_CPP_SDK}/proxy_wasm_intrinsics.js $*.cc ${PROXY_WASM_CPP_SDK}/proxy_wasm_intrinsics_lite.pb.cc ${PROXY_WASM_CPP_SDK}/struct_lite.pb.cc ${CPP_CONTEXT_LIB} ${PROXY_WASM_CPP_SDK}/libprotobuf-lite.a ${UTILITY_LIB} -o $*.wasm

clean:
rm *.wasm



87 changes: 87 additions & 0 deletions waf_sqli/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
# WAF extension on Envoy proxy

This repository is forked from [`envoyproxy/envoy-wasm`](https://github.com/envoyproxy/envoy-wasm), and the example WASM extension in the envoy-wasm repository is modified to work as a Web Application Firewall(WAF) that can detect SQL injection. The rules for detection are aligned with ModSecurity rules [942100](https://github.com/coreruleset/coreruleset/blob/v3.3/dev/rules/REQUEST-942-APPLICATION-ATTACK-SQLI.conf#L45) and [942101](https://github.com/coreruleset/coreruleset/blob/v3.3/dev/rules/REQUEST-942-APPLICATION-ATTACK-SQLI.conf#L1458), and SQL injection is detected with methods from [libinjection](https://github.com/client9/libinjection).

## Build

We will build the WASM module with `proxy-wasm-cpp-sdk` on docker.
We first need to build the image of the SDK from its repository on the `envoy-release/v1.15` branch:
```
git clone [email protected]:proxy-wasm/proxy-wasm-cpp-sdk.git
cd proxy-wasm-cpp-sdk
git checkout envoy-release/v1.15
docker build -t wasmsdk:v2 -f Dockerfile-sdk .
```
Then from the root of this repository, build the WASM module with:
```
docker run -v $PWD:/work -w /work wasmsdk:v2 /build_wasm.sh
```
After the compilation completes, you should find a `waf_sqli.wasm` file in the repository.

## Deploy
We can mount the WASM module onto the docker image of Istio proxy to run it.
Pull the following image of Istio proxy:
```
docker pull istio/proxyv2:1.7.0-beta.2
```
Then run the proxy with WASM configured:
```
docker run \
-v ${PWD}/envoy.yaml:/etc/envoy.yaml \
-v ${PWD}/waf_sqli.wasm:/etc/waf_sqli.wasm \
-p 8000:8000 \
--entrypoint /usr/local/bin/envoy \
istio/proxyv2:1.7.0-beta.2 -l trace --concurrency 1 -c /etc/envoy.yaml
```

In a separate terminal, curl at `localhost:8000` to interact with the running proxy. For example, if you type the following command, you will receive a response with HTTP code 200 Okay, indicating that the request has passed SQL injection detection.
```
curl -d "hello world" -v localhost:8000
```
If you instead put something suspicious in the body, for example, enter the following command:
```
curl -d "val=-1%27+and+1%3D1%0D%0A" -v localhost:8000
```
You will receive a response with HTTP code 403 Forbidden. The body of the http request above has the parameter `val` with the value `-1' and 1=1` in URL
encoding.

## Unit Tests

Unit tests for individual utility functions in the WAF WASM extension are
available in `test` directory. To run them, execute from the root of the
repository:
```
source ./build_test.sh
```


## Configuration
The rules for SQL injection detection can be configured from YAML files. An example of configuration can be found in `envoy-config.yaml`. Configuration are passsed through the field `config.config.configuration.value` in the yaml file in JSON syntax as below:

```
{
“query_param”: {
# detect sqli on all parameters but “foo”
“Content-Type”: “application/x-www-form-urlencoded”,
“exclude”: [“foo”]
},
“header”: {
# detect sqli on “bar”, “Referrer”, and “User-Agent”
“include”: [“bar”]
}
}
```

There are three parts that can be configured for now: query parameters(`query_param`), cookies(`cookie`, not shown above), and headers(`header`). Configuration for all three parts are optional. If nothing is passed in a field, a default configuration based on ModSecurity rule 942100 will apply. ModSecurity rule 942101 requires SQL injection detection on path of request. Configuration for path will be updated later.

### Query Parameters
The "Content-Type" field is required in query parameters configuration, Currently, the WASM module only supports SQL injection detection for the content type "application/x-www-form-urlencoded" (it has the syntax `param=value&param2=value2`). If the incoming http request has a different content type, detection on its body will be skipped.

In default setting, all query parameter namesand values will be checked for SQL injection. To change this setting, you can either add an `include` or an `exclude` field. Both take a list of parameter names. If `include` is present, only the parameters in the list will be checked. If `exclude` is present, all but the parameters in the list will be checked. `include` and `exclude` are not expected to be present at the same time.

### Headers
In default setting, the `Referrer` and `User-Agent` headers will be checked for SQL injection. The `include` and `exclude` fields work similarly as above, except that `Referrer` and `User-Agent` will always be checked unless explicitly enlisted in `exlude`.

### Cookies
In default setting, all cookie names will be checked. `include` and `exclude` work exactly the same as for query parameters.

5 changes: 5 additions & 0 deletions waf_sqli/build_test.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
cd ./test
make clean
make
make clean
cd ..
19 changes: 19 additions & 0 deletions waf_sqli/build_wasm.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#!/bin/bash
# Copyright 2016-2019 Envoy Project Authors
# Copyright 2020 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

source /root/emsdk/emsdk_env.sh
export PATH=/usr/local/bin:$PATH
bazel build //...
90 changes: 90 additions & 0 deletions waf_sqli/envoy-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
static_resources:
listeners:
- name: main
address:
socket_address:
address: 0.0.0.0
port_value: 80
filter_chains:
- filters:
- name: envoy.http_connection_manager
config:
stat_prefix: ingress_http
codec_type: auto
route_config:
name: local_route
virtual_hosts:
- name: local_service
domains:
- "*"
routes:
- match:
prefix: "/"
route:
cluster: web_service
http_filters:
- name: envoy.filters.http.wasm
config:
config:
name: "my_plugin"
root_id: "my_root_id"
vm_config:
vm_id: "my_vm_id"
runtime: "envoy.wasm.runtime.v8"
code:
local:
filename: "/etc/waf_sqli.wasm"
allow_precompiled: true
configuration:
"@type": "type.googleapis.com/google.protobuf.StringValue"
value: |
{
"query_param": {
"content-type": "application/x-www-form-urlencoded"
}
}
- name: envoy.router
config: {}
- name: staticreply
address:
socket_address:
address: 127.0.0.1
port_value: 8099
filter_chains:
- filters:
- name: envoy.http_connection_manager
config:
stat_prefix: ingress_http
codec_type: auto
route_config:
name: local_route
virtual_hosts:
- name: local_service
domains:
- "*"
routes:
- match:
prefix: "/"
direct_response:
status: 200
body:
inline_string: "example body\n"
http_filters:
- name: envoy.router
config: {}

clusters:
- name: web_service
connect_timeout: 0.25s
type: static
lb_policy: round_robin
hosts:
- socket_address:
address: 127.0.0.1
port_value: 8099
admin:
access_log_path: "/dev/null"
address:
socket_address:
address: 0.0.0.0
port_value: 8001
82 changes: 82 additions & 0 deletions waf_sqli/envoy.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
static_resources:
listeners:
- name: main
address:
socket_address:
address: 0.0.0.0
port_value: 8000
filter_chains:
- filters:
- name: envoy.http_connection_manager
config:
stat_prefix: ingress_http
codec_type: auto
route_config:
name: local_route
virtual_hosts:
- name: local_service
domains:
- "*"
routes:
- match:
prefix: "/"
route:
cluster: web_service
http_filters:
- name: envoy.filters.http.wasm
config:
config:
name: "my_plugin"
root_id: "root_WAF"
vm_config:
vm_id: "my_vm_id"
runtime: "envoy.wasm.runtime.v8"
code:
local:
filename: "/etc/waf_sqli.wasm"
allow_precompiled: true
- name: envoy.router
config: {}
- name: staticreply
address:
socket_address:
address: 127.0.0.1
port_value: 8099
filter_chains:
- filters:
- name: envoy.http_connection_manager
config:
stat_prefix: ingress_http
codec_type: auto
route_config:
name: local_route
virtual_hosts:
- name: local_service
domains:
- "*"
routes:
- match:
prefix: "/"
direct_response:
status: 200
body:
inline_string: "example body\n"
http_filters:
- name: envoy.router
config: {}

clusters:
- name: web_service
connect_timeout: 0.25s
type: static
lb_policy: round_robin
hosts:
- socket_address:
address: 127.0.0.1
port_value: 8099
admin:
access_log_path: "/dev/null"
address:
socket_address:
address: 0.0.0.0
port_value: 8001
17 changes: 17 additions & 0 deletions waf_sqli/test/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
CXX = g++
BINS = config_parser http_parser sqli

all: config_parser_test http_parser_test sqli_test

config_parser_test:
g++ config_parser_test.cc ../utility/config_parser.cc -o config_parser_test
./config_parser_test
http_parser_test:
g++ http_parser_test.cc ../utility/http_parser.cc -o http_parser_test
./http_parser_test
sqli_test:
g++ sqli_test.cc ../utility/sqli.cc ../utility/libinjection/*.c -o sqli_test
./sqli_test

clean:
rm -f *_test
Loading