├── .github ├── ISSUE_TEMPLATE │ ├── bug_report-zh-cn.md │ └── bug_report.md ├── stale.yml └── workflows │ └── test.yml ├── .gitignore ├── CHANGES-ZH-CN.md ├── CHANGES.md ├── LICENSE ├── Makefile ├── README-ZH-CN.md ├── README.md ├── assets ├── logo.png ├── rules │ ├── advanced │ ├── args │ ├── cookie │ ├── ipv4 │ ├── ipv6 │ ├── post │ ├── referer │ ├── url │ ├── user-agent │ ├── white-ipv4 │ ├── white-ipv6 │ ├── white-referer │ └── white-url ├── under-attack.html └── versioning.drawio ├── bison └── parser.yacc ├── config ├── flex └── lexer.lex ├── inc ├── ngx_http_waf_module_check.h ├── ngx_http_waf_module_config.h ├── ngx_http_waf_module_core.h ├── ngx_http_waf_module_ip_trie.h ├── ngx_http_waf_module_lru_cache.h ├── ngx_http_waf_module_macro.h ├── ngx_http_waf_module_mem_pool.h ├── ngx_http_waf_module_type.h ├── ngx_http_waf_module_under_attack.h ├── ngx_http_waf_module_util.h └── ngx_http_waf_module_vm.h ├── src ├── ngx_http_waf_module_check.c ├── ngx_http_waf_module_config.c ├── ngx_http_waf_module_core.c ├── ngx_http_waf_module_ip_trie.c ├── ngx_http_waf_module_lru_cache.c ├── ngx_http_waf_module_mem_pool.c ├── ngx_http_waf_module_under_attack.c ├── ngx_http_waf_module_util.c └── ngx_http_waf_module_vm.c └── test ├── test-nginx ├── init.sh ├── start.sh ├── t │ └── .gitkeep └── template │ ├── args.t │ ├── bad-config.t │ ├── cache.t │ ├── cc.t │ ├── cookie.t │ ├── disable.t │ ├── ipv4.t │ ├── ipv6.t │ ├── mode.t │ ├── post.t │ ├── referer.t │ ├── under-attack.t │ ├── uri.t │ ├── user-agent.t │ └── var.t └── wrk └── rand.lua /.github/ISSUE_TEMPLATE/bug_report-zh-cn.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug 报告 3 | about: 缺陷报告 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **提交时请删除以下内容。** 11 | 12 | 其实我不太喜欢按照固定的格式写东西,所以您也不需要,但是下面的内容可以帮助 Bug 的解决。 13 | 14 | ## 强烈建议提供的信息 15 | 16 | * 如何触发错误。 17 | * ngx_waf 的版本/分支。 18 | * `nginx -V` 命令的输出内容。 19 | * 调试日志([如何获取调试日志](#如何获取调试日志))。 20 | * 出错时 `shell` 的输出内容。 21 | 22 | ## 可以提供的信息 23 | 24 | 这些信息通常不会用到,但是必要时可能会向您询问。 25 | 26 | * 操作系统,包括名称和版本。 27 | * `nginx` 是否运行在某些虚拟环境内,比如有没有运行在 `Docker` 里。如果运行在 `Docker` 内请提供镜像名称。 28 | 29 | ## 如何获取调试日志 30 | 31 | 本文模块会条件满足的情况下提供调试日志,如果您提供这些日志将有利于 Bug 的定位。您可以按照下面的方式获取调试日志。 32 | 33 | 1. 在 `nginx` 配置文件中调整错误日志的等级为 `debug`,例如 `error_log logs/error.log debug;`。 34 | 2. 关闭 `nginx`,然后清空现有的 `error.log` 文件(必要的话可以在清空前备份),然后启动 `nginx`。 35 | 3. 触发 Bug。 36 | 4. 上传 `error.log` 文件,注意消除文件中的隐私信息。 37 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Please delete the following content when submitting.** 11 | 12 | I don't really like to write things in a set format, so you don't need to follow the format for this report. But please read the following, as it will help fix the bugs. 13 | 14 | ## Key Info 15 | 16 | It is strongly recommended that you provide the following information. 17 | 18 | * How to trigger this bug? 19 | * The version or branch of `ngx_waf`. 20 | * Output of `nginx -V`. 21 | * Debug log ([How to get the debug log?](how-to-get-the-debug-log)). 22 | * Output of `shell` (if any). 23 | 24 | ## Optional Info 25 | 26 | In most cases you should not provide this information, but the program maintainer will ask you for it if necessary. 27 | 28 | * The name and version of the OS. 29 | * Whether `nginx` is running in a virtual environment such as `Docker`. If it is running in `Docker`, please provide the image name. 30 | 31 | ## How to get the debug log 32 | 33 | This module will output debug logs under certain conditions to facilitate bug location.You can get the debug log by following the steps below. 34 | 35 | 1. Set the error log level to `debug` in the configuration file for `nginx`, e.g. `error_log logs/error.log debug;` 36 | 2. Shut down `nginx`, then clear `error.log` (back it up if necessary), and finally start `nginx`. 37 | 3. Triggers the bug you want to report. 38 | 4. Upload `error.log` and remember to clear the privacy information from the file. 39 | -------------------------------------------------------------------------------- /.github/stale.yml: -------------------------------------------------------------------------------- 1 | # Number of days of inactivity before an issue becomes stale 2 | daysUntilStale: 7 3 | # Number of days of inactivity before a stale issue is closed 4 | daysUntilClose: 8 5 | # Issues with these labels will never be considered stale 6 | exemptLabels: 7 | - pinned 8 | - security 9 | - long-term 10 | # Label to use when marking an issue as stale 11 | staleLabel: stale 12 | # Comment to post when marking an issue as stale. Set to `false` to disable 13 | markComment: | 14 | This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions. 15 | 此 issue 因为最近没有任何活动已经被标记,如果在此之后的一段时间内仍没有任何活动则会被关闭。感谢您对项目的支持。 16 | 17 | # Comment to post when closing a stale issue. Set to `false` to disable 18 | closeComment: false -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: test 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | - lts* 8 | - current* 9 | pull_request: 10 | schedule: 11 | - cron: '0 0 * * SUN' 12 | workflow_dispatch: 13 | 14 | 15 | defaults: 16 | run: 17 | shell: bash 18 | 19 | jobs: 20 | native: 21 | runs-on: ubuntu-latest 22 | 23 | strategy: 24 | matrix: 25 | nginx-version: 26 | - '1.18.0' 27 | - '1.20.2' 28 | - '1.22.1' 29 | - '1.24.0' 30 | - '1.26.2' 31 | - '1.27.3' 32 | install-type: ['static module', 'dynamic module'] 33 | 34 | env: 35 | LIB_INJECTION: ${{ github.workspace }}/libinjection 36 | 37 | steps: 38 | - name: Checkout sources 39 | uses: actions/checkout@v2 40 | 41 | - name: Checkout libinjection sources 42 | uses: actions/checkout@v2 43 | with: 44 | repository: 'libinjection/libinjection' 45 | path: 'libinjection-src' 46 | 47 | - uses: actions/setup-python@v2 48 | with: 49 | python-version: '3.x' 50 | architecture: 'x64' 51 | 52 | - name: Install dependencies 53 | run: | 54 | sudo apt-get --yes update 55 | sudo apt-get install --yes libsodium23 libsodium-dev build-essential zlib1g-dev libpcre3 libpcre3-dev libssl-dev libxslt1-dev libxml2-dev libgeoip-dev libgd-dev libperl-dev uthash-dev flex bison 56 | sudo apt-get remove --yes python3-urllib3 57 | sudo pip install lastversion 58 | 59 | - name: Install libinjection 60 | run: | 61 | cd libinjection-src 62 | ./autogen.sh 63 | ./configure --prefix=${{ env.LIB_INJECTION }} 64 | make -j$(nproc) 65 | sudo make install 66 | 67 | - name: Download nginx-${{ matrix.nginx-version }} 68 | run: | 69 | wget https://nginx.org/download/nginx-${{ matrix.nginx-version }}.tar.gz 70 | mkdir nginx-src 71 | tar zxf nginx-*.tar.gz --directory nginx-src --strip-components=1 72 | 73 | - name: Configure nginx-${{ matrix.install-type }} 74 | run: | 75 | make parser 76 | cd nginx-src 77 | if [ ${{ matrix.install-type }} = 'static module' ] ; then \ 78 | opt='--add-module' ;\ 79 | else \ 80 | opt='--add-dynamic-module' ;\ 81 | fi 82 | LIB_INJECTION=${{ env.LIB_INJECTION }} ./configure ${opt}=.. --with-cc-opt='-Wno-unused-but-set-variable -Wno-unused-function -fstack-protector-strong -Wno-sign-compare' --with-http_realip_module 83 | 84 | - name: Install ${{ matrix.nginx-version }} 85 | run: | 86 | cd nginx-src 87 | make -j$(nproc) 88 | sudo make install 89 | sudo useradd nginx -s /sbin/nologin -M 90 | sudo chmod 777 -R /usr/local/nginx 91 | sudo ln -s /usr/local/nginx/sbin/nginx /usr/local/bin/nginx 92 | 93 | - name: Install Test::Nginx 94 | run: | 95 | sudo cpan Test::Nginx 96 | 97 | - name: Test 98 | run: | 99 | sudo chmod 777 -R /tmp 100 | cd test/test-nginx 101 | export MODULE_TEST_PATH=/tmp/module_test 102 | sh ./init.sh 103 | exec sudo sh start.sh t/*.t 104 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode 2 | node_modules 3 | docs/.vuepress/dist 4 | yarn.lock 5 | package-lock.json 6 | test/test-nginx/t/servroot 7 | test/test-nginx/t/*.t 8 | inc/libinjection 9 | inc/libsodium 10 | inc/ngx_http_waf_module_lexer.h 11 | inc/ngx_http_waf_module_parser.tab.h 12 | src/ngx_http_waf_module_lexer.c 13 | src/ngx_http_waf_module_parser.tab.c 14 | *.so -------------------------------------------------------------------------------- /CHANGES-ZH-CN.md: -------------------------------------------------------------------------------- 1 | 见 [https://github.com/ADD-SP/ngx_waf-docs/blob/master/docs/zh-cn/changes/overview.md](https://github.com/ADD-SP/ngx_waf-docs/blob/master/docs/zh-cn/changes/overview.md)。 -------------------------------------------------------------------------------- /CHANGES.md: -------------------------------------------------------------------------------- 1 | See [https://github.com/ADD-SP/ngx_waf-docs/blob/master/docs/changes/overview.md](https://github.com/ADD-SP/ngx_waf-docs/blob/master/docs/changes/overview.md). -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2020, ADD-SP 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | 1. Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | 2. Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | 3. Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | parser: flex/lexer.lex bison/parser.yacc 2 | @flex flex/lexer.lex 3 | @bison --defines=inc/ngx_http_waf_module_parser.tab.h -L C -o src/ngx_http_waf_module_parser.tab.c bison/parser.yacc -------------------------------------------------------------------------------- /README-ZH-CN.md: -------------------------------------------------------------------------------- 1 | # ngx_waf 2 | 3 | 4 |
5 |
6 |
4 |
5 |
id_rule if_rule do_rule conditon conditon_ex primary rule
37 |
38 | %nterm str_operand
39 | %nterm str_op ip_op
40 |
41 | %token token_id
42 | %token token_str token_index
43 | %token token_int
44 |
45 | %token keyword_id keyword_if keyword_do keyword_or keyword_allow
46 | %token keyword_and keyword_matches keyword_equals
47 | %token token_break_line token_blank keyword_not
48 | %token keyword_url keyword_contains keyword_return
49 | %token keyword_query_string keyword_user_agent keyword_belong_to
50 | %token keyword_referer keyword_client_ip keyword_header_in
51 | %token keyword_sqli_detn keyword_xss_detn keyword_cookie
52 |
53 | %union {
54 | int int_val;
55 | unsigned int uint_val;
56 | char id_val[256];
57 | char str_val[256];
58 | void (*push_op_code_pt)(UT_array* array);
59 | struct {
60 | int argc;
61 | void (*no_str_pt)(UT_array* array);
62 | void (*one_str_pt)(UT_array* array, char* str);
63 | char* argv[4];
64 | } push_str_code_info;
65 | }
66 |
67 |
68 | %%
69 |
70 |
71 |
72 | base:
73 | rule end
74 | {}
75 |
76 | | %empty
77 | {}
78 | ;
79 |
80 | rule:
81 | id_rule if_rule do_rule
82 | {}
83 | ;
84 | end:
85 | token_break_line token_break_line rule { }
86 | | token_break_line token_break_line { }
87 | | %empty { }
88 | ;
89 |
90 |
91 | id_rule:
92 | keyword_id ':' token_blank token_id token_break_line
93 | { ngx_http_waf_gen_push_str_code(array, $4); }
94 | ;
95 |
96 | if_rule:
97 | keyword_if ':' token_blank conditon token_break_line
98 | { }
99 | ;
100 |
101 | do_rule:
102 | keyword_do ':' token_blank keyword_return '(' token_int ')'
103 | {
104 | ngx_http_waf_gen_act_ret_code(array, $6);
105 | }
106 |
107 | | keyword_do ':' token_blank keyword_allow
108 | {
109 | ngx_http_waf_gen_act_allow_code(array);
110 | }
111 | ;
112 |
113 | conditon:
114 | conditon keyword_and conditon_ex
115 | {
116 | ngx_http_waf_gen_op_and_code(array);
117 | }
118 |
119 | | conditon_ex
120 | { }
121 | ;
122 |
123 | conditon_ex:
124 | conditon_ex keyword_or primary
125 | {
126 | ngx_http_waf_gen_op_or_code(array);
127 | }
128 | | primary
129 | { }
130 | ;
131 | primary:
132 | '(' conditon ')' { }
133 |
134 | | keyword_not token_blank '(' conditon ')'
135 | {
136 | ngx_http_waf_gen_push_op_not_code(array);
137 | }
138 |
139 | | str_operand str_op str_operand
140 | {
141 | switch ($3.argc) {
142 | case 0:
143 | $3.no_str_pt(array);
144 | break;
145 | case 1:
146 | $3.one_str_pt(array, $3.argv[0]);
147 | break;
148 | default:
149 | YYABORT;
150 | }
151 |
152 | switch ($1.argc) {
153 | case 0:
154 | $1.no_str_pt(array);
155 | break;
156 | case 1:
157 | $1.one_str_pt(array, $1.argv[0]);
158 | break;
159 | default:
160 | YYABORT;
161 | }
162 | $2(array);
163 | }
164 |
165 | | str_operand token_blank keyword_not str_op str_operand
166 | {
167 | switch ($5.argc) {
168 | case 0:
169 | $5.no_str_pt(array);
170 | break;
171 | case 1:
172 | $5.one_str_pt(array, $5.argv[0]);
173 | break;
174 | default:
175 | YYABORT;
176 | }
177 |
178 | switch ($1.argc) {
179 | case 0:
180 | $1.no_str_pt(array);
181 | break;
182 | case 1:
183 | $1.one_str_pt(array, $1.argv[0]);
184 | break;
185 | default:
186 | YYABORT;
187 | }
188 | $4(array);
189 | ngx_http_waf_gen_push_op_not_code(array);
190 | }
191 |
192 | | keyword_sqli_detn token_blank str_operand
193 | {
194 | switch ($3.argc) {
195 | case 0:
196 | $3.no_str_pt(array);
197 | break;
198 | case 1:
199 | $3.one_str_pt(array, $3.argv[0]);
200 | break;
201 | default:
202 | YYABORT;
203 | }
204 | ngx_http_waf_gen_op_sqli_detn_code(array);
205 | }
206 |
207 | | keyword_xss_detn token_blank str_operand
208 | {
209 | switch ($3.argc) {
210 | case 0:
211 | $3.no_str_pt(array);
212 | break;
213 | case 1:
214 | $3.one_str_pt(array, $3.argv[0]);
215 | break;
216 | default:
217 | YYABORT;
218 | }
219 | ngx_http_waf_gen_op_xss_detn_code(array);
220 | }
221 |
222 | | keyword_client_ip ip_op str_operand
223 | {
224 | switch ($3.argc) {
225 | case 0:
226 | $3.no_str_pt(array);
227 | break;
228 | case 1:
229 | $3.one_str_pt(array, $3.argv[0]);
230 | break;
231 | default:
232 | YYABORT;
233 | }
234 | ngx_http_waf_gen_push_client_ip_code(array);
235 | $2(array);
236 | }
237 |
238 | | keyword_client_ip token_blank keyword_not ip_op str_operand
239 | {
240 | switch ($5.argc) {
241 | case 0:
242 | $5.no_str_pt(array);
243 | break;
244 | case 1:
245 | $5.one_str_pt(array, $5.argv[0]);
246 | break;
247 | default:
248 | YYABORT;
249 | }
250 | ngx_http_waf_gen_push_client_ip_code(array);
251 | $4(array);
252 | ngx_http_waf_gen_push_op_not_code(array);
253 | }
254 |
255 | ;
256 |
257 | str_operand:
258 | keyword_url
259 | {
260 | $$.argc = 0;
261 | $$.no_str_pt = ngx_http_waf_gen_push_url_code;
262 | }
263 |
264 | | keyword_user_agent
265 | {
266 | $$.argc = 0;
267 | $$.no_str_pt = ngx_http_waf_gen_push_user_agent_code;
268 | }
269 |
270 | | keyword_referer
271 | {
272 | $$.argc = 0;
273 | $$.no_str_pt = ngx_http_waf_gen_push_referer_code;
274 | }
275 |
276 | | token_str
277 | {
278 | $$.argc = 1;
279 | $$.one_str_pt = ngx_http_waf_gen_push_str_code;
280 | $$.argv[0] = strdup($1);
281 | }
282 |
283 | | keyword_header_in token_index
284 | {
285 | $$.argc = 1;
286 | $$.one_str_pt = ngx_http_waf_gen_push_header_in_code;
287 | $$.argv[0] = strdup($2);
288 | }
289 |
290 | | keyword_query_string token_index
291 | {
292 | $$.argc = 1;
293 | $$.one_str_pt = ngx_http_waf_gen_push_query_string_code;
294 | $$.argv[0] = strdup($2);
295 | }
296 |
297 | | keyword_cookie token_index
298 | {
299 | $$.argc = 1;
300 | $$.one_str_pt = ngx_http_waf_gen_push_cookie_code;
301 | $$.argv[0] = strdup($2);
302 | }
303 | ;
304 |
305 | str_op:
306 | keyword_contains
307 | {
308 | $$ = ngx_http_waf_gen_op_contains_code;
309 | }
310 |
311 | | keyword_matches
312 | {
313 | $$ = ngx_http_waf_gen_op_matches_code;
314 | }
315 |
316 | | keyword_equals
317 | {
318 | $$ = ngx_http_waf_gen_op_equals_code;
319 | }
320 | ;
321 |
322 | ip_op:
323 | keyword_equals
324 | {
325 | $$ = ngx_http_waf_gen_op_equals_code;
326 | }
327 |
328 | | keyword_belong_to
329 | {
330 | $$ = ngx_http_waf_gen_op_belong_to_code;
331 | }
332 | ;
333 | %%
334 |
335 |
336 | void
337 | ngx_http_waf_gen_push_str_code(UT_array* array, char* str) {
338 | vm_code_t code;
339 |
340 | code.type = VM_CODE_PUSH_STR;
341 | code.argv.argc = 1;
342 | code.argv.type[0] = VM_DATA_STR;
343 | code.argv.value[0].str_val.data = (u_char*)strdup(str);
344 | code.argv.value[0].str_val.len = strlen(str);
345 |
346 | utarray_push_back(array, &code);
347 | free(code.argv.value[0].str_val.data);
348 | }
349 |
350 |
351 | void ngx_http_waf_gen_push_query_string_code(UT_array* array, char* index) {
352 | vm_code_t code;
353 | size_t len = strlen(index);
354 | code.type = VM_CODE_PUSH_QUERY_STRING;
355 | code.argv.argc = 1;
356 | code.argv.type[0] = VM_DATA_STR;
357 | code.argv.value[0].str_val.data = (u_char*)strdup(index);
358 | code.argv.value[0].str_val.len = len;
359 |
360 | utarray_push_back(array, &code);
361 | free(code.argv.value[0].str_val.data);
362 | }
363 |
364 |
365 | void ngx_http_waf_gen_push_header_in_code(UT_array* array, char* index) {
366 | vm_code_t code;
367 | size_t len = strlen(index);
368 | code.type = VM_CODE_PUSH_HEADER_IN;
369 | code.argv.argc = 1;
370 | code.argv.type[0] = VM_DATA_STR;
371 | code.argv.value[0].str_val.data = (u_char*)strdup(index);
372 | code.argv.value[0].str_val.len = len;
373 |
374 | utarray_push_back(array, &code);
375 | free(code.argv.value[0].str_val.data);
376 | }
377 |
378 |
379 | void ngx_http_waf_gen_push_cookie_code(UT_array* array, char* index) {
380 | vm_code_t code;
381 | size_t len = strlen(index);
382 | code.type = VM_CODE_PUSH_COOKIE;
383 | code.argv.argc = 1;
384 | code.argv.type[0] = VM_DATA_STR;
385 | code.argv.value[0].str_val.data = (u_char*)strdup(index);
386 | code.argv.value[0].str_val.len = len;
387 |
388 | utarray_push_back(array, &code);
389 | free(code.argv.value[0].str_val.data);
390 | }
391 |
392 |
393 | void ngx_http_waf_gen_push_client_ip_code(UT_array* array) {
394 | vm_code_t code;
395 | code.type = VM_CODE_PUSH_CLIENT_IP;
396 | code.argv.argc = 0;
397 | utarray_push_back(array, &code);
398 | }
399 |
400 |
401 | void
402 | ngx_http_waf_gen_push_url_code(UT_array* array) {
403 | vm_code_t code;
404 | code.type = VM_CODE_PUSH_URL;
405 | code.argv.argc = 0;
406 | utarray_push_back(array, &code);
407 | }
408 |
409 |
410 | void
411 | ngx_http_waf_gen_push_user_agent_code(UT_array* array) {
412 | vm_code_t code;
413 | code.type = VM_CODE_PUSH_USER_AGENT;
414 | code.argv.argc = 0;
415 | utarray_push_back(array, &code);
416 | }
417 |
418 |
419 | void
420 | ngx_http_waf_gen_push_referer_code(UT_array* array) {
421 | vm_code_t code;
422 | code.type = VM_CODE_PUSH_REFERER;
423 | code.argv.argc = 0;
424 | utarray_push_back(array, &code);
425 | }
426 |
427 |
428 | void
429 | ngx_http_waf_gen_int_code(UT_array* array, int num) {
430 | vm_code_t code;
431 | code.type = VM_CODE_PUSH_INT;
432 | code.argv.argc = 1;
433 | code.argv.type[0] = VM_DATA_INT;
434 | code.argv.value[0].int_val = num;
435 | utarray_push_back(array, &code);
436 | }
437 |
438 |
439 | void
440 | ngx_http_waf_gen_push_op_not_code(UT_array* array) {
441 | vm_code_t code;
442 | code.type = VM_CODE_OP_NOT;
443 | code.argv.argc = 0;
444 | utarray_push_back(array, &code);
445 | }
446 |
447 |
448 | void
449 | ngx_http_waf_gen_op_and_code(UT_array* array) {
450 | vm_code_t code;
451 | code.type = VM_CODE_OP_AND;
452 | code.argv.argc = 0;
453 | utarray_push_back(array, &code);
454 | }
455 |
456 |
457 | void
458 | ngx_http_waf_gen_op_or_code(UT_array* array) {
459 | vm_code_t code;
460 | code.type = VM_CODE_OP_OR;;
461 | code.argv.argc = 0;
462 | utarray_push_back(array, &code);
463 | }
464 |
465 | void
466 | ngx_http_waf_gen_op_matches_code(UT_array* array) {
467 | vm_code_t code;
468 | code.type = VM_CODE_OP_MATCHES;
469 | code.argv.argc = 0;
470 | utarray_push_back(array, &code);
471 | }
472 |
473 |
474 |
475 | void
476 | ngx_http_waf_gen_op_contains_code(UT_array* array) {
477 | vm_code_t code;
478 | code.type = VM_CODE_OP_CONTAINS;
479 | code.argv.argc = 0;
480 | utarray_push_back(array, &code);
481 | }
482 |
483 |
484 | void
485 | ngx_http_waf_gen_op_equals_code(UT_array* array) {
486 | vm_code_t code;
487 | code.type = VM_CODE_OP_EQUALS;
488 | code.argv.argc = 0;
489 | utarray_push_back(array, &code);
490 | }
491 |
492 |
493 | void
494 | ngx_http_waf_gen_op_belong_to_code(UT_array* array) {
495 | vm_code_t code;
496 | code.type = VM_CODE_OP_BELONG_TO;
497 | code.argv.argc = 0;
498 | utarray_push_back(array, &code);
499 | }
500 |
501 |
502 | void
503 | ngx_http_waf_gen_op_sqli_detn_code(UT_array* array) {
504 | vm_code_t code;
505 | code.type = VM_CODE_OP_SQLI_DETN;
506 | code.argv.argc = 0;
507 | utarray_push_back(array, &code);
508 | }
509 |
510 |
511 | void
512 | ngx_http_waf_gen_op_xss_detn_code(UT_array* array) {
513 | vm_code_t code;
514 | code.type = VM_CODE_OP_XSS_DETN;
515 | code.argv.argc = 0;
516 | utarray_push_back(array, &code);
517 | }
518 |
519 |
520 | void
521 | ngx_http_waf_gen_act_ret_code(UT_array* array, int http_status) {
522 | vm_code_t code;
523 | code.type = VM_CODE_ACT_RETURN;
524 | code.argv.argc = 1;
525 | code.argv.type[0] = VM_DATA_INT;
526 | code.argv.value[0].int_val = http_status;
527 | utarray_push_back(array, &code);
528 | }
529 |
530 |
531 | void
532 | ngx_http_waf_gen_act_allow_code(UT_array* array) {
533 | vm_code_t code;
534 | code.type = VM_CODE_ACT_ALLOW;
535 | code.argv.argc = 0;
536 | utarray_push_back(array, &code);
537 | }
--------------------------------------------------------------------------------
/config:
--------------------------------------------------------------------------------
1 | ngx_addon_name=ngx_http_waf_module
2 |
3 | deps="$ngx_addon_dir/inc/ngx_http_waf_module_check.h \
4 | $ngx_addon_dir/inc/ngx_http_waf_module_config.h \
5 | $ngx_addon_dir/inc/ngx_http_waf_module_core.h \
6 | $ngx_addon_dir/inc/ngx_http_waf_module_macro.h \
7 | $ngx_addon_dir/inc/ngx_http_waf_module_type.h \
8 | $ngx_addon_dir/inc/ngx_http_waf_module_util.h \
9 | $ngx_addon_dir/inc/ngx_http_waf_module_ip_trie.h \
10 | $ngx_addon_dir/inc/ngx_http_waf_module_mem_pool.h \
11 | $ngx_addon_dir/inc/ngx_http_waf_module_lru_cache.h \
12 | $ngx_addon_dir/inc/ngx_http_waf_module_under_attack.h \
13 | $ngx_addon_dir/inc/ngx_http_waf_module_vm.h \
14 | $ngx_addon_dir/inc/ngx_http_waf_module_lexer.h \
15 | $ngx_addon_dir/inc/ngx_http_waf_module_parser.tab.h"
16 |
17 | srcs="$ngx_addon_dir/src/ngx_http_waf_module_core.c \
18 | $ngx_addon_dir/src/ngx_http_waf_module_check.c \
19 | $ngx_addon_dir/src/ngx_http_waf_module_config.c \
20 | $ngx_addon_dir/src/ngx_http_waf_module_ip_trie.c \
21 | $ngx_addon_dir/src/ngx_http_waf_module_lru_cache.c \
22 | $ngx_addon_dir/src/ngx_http_waf_module_mem_pool.c \
23 | $ngx_addon_dir/src/ngx_http_waf_module_under_attack.c \
24 | $ngx_addon_dir/src/ngx_http_waf_module_util.c \
25 | $ngx_addon_dir/src/ngx_http_waf_module_vm.c \
26 | $ngx_addon_dir/src/ngx_http_waf_module_lexer.c \
27 | $ngx_addon_dir/src/ngx_http_waf_module_parser.tab.c"
28 |
29 | ngx_http_waf_module_libs=""
30 |
31 | ngx_http_waf_module_inc_path="$ngx_addon_dir/inc "
32 |
33 | if [ -n "$LIB_UTHASH" ] ; then
34 | ngx_http_waf_module_inc_path="${ngx_http_waf_module_inc_path} ${LIB_UTHASH}/include"
35 | fi
36 |
37 | which flex
38 | if [ $? -ne 0 ] ; then
39 | cat << END
40 |
41 | $0: error: the $ngx_addon_name module requires the flex.
42 |
43 | Please run:
44 | On Ubuntu or Debian:
45 | apt-get update && apt-get install --yes flex
46 | On CentOS 7:
47 | yum -y install flex
48 | On Centos 8 or Fedora 33 or Fedora 34:
49 | dnf install flex
50 | On Alpine:
51 | apk update && apk add --upgrade flex
52 | On Arch:
53 | 1. Enable the core repository on /etc/pacman.conf:
54 | [core]
55 | Include = /etc/pacman.d/mirrorlist
56 | 2. Install flex xz package:
57 | pacman -Syu flex
58 | On FreeBSD 12 or FreeBSD 13:
59 | pkg install flex
60 | END
61 | exit 1
62 |
63 | fi
64 |
65 | which bison
66 | if [ $? -ne 0 ] ; then
67 | cat << END
68 |
69 | $0: error: the $ngx_addon_name module requires the bison.
70 |
71 | Please run:
72 | On Ubuntu or Debian:
73 | apt-get update && apt-get install --yes bison
74 | On CentOS 7:
75 | yum -y install bison
76 | On Centos 8 or Fedora 33 or Fedora 34:
77 | dnf install bison
78 | On Alpine:
79 | apk update && apk add --upgrade bison
80 | On Arch:
81 | 1. Enable the core repository on /etc/pacman.conf:
82 | [core]
83 | Include = /etc/pacman.d/mirrorlist
84 | 2. Install flex xz package:
85 | pacman -Syu bison
86 | On FreeBSD 12 or FreeBSD 13:
87 | pkg install bison
88 |
89 | END
90 | exit 1
91 |
92 | fi
93 |
94 |
95 | is_gen='true'
96 |
97 | if [ ! -e "${ngx_addon_dir}/inc/ngx_http_waf_module_lexer.h" ] ; then
98 | is_gen='false'
99 | elif [ ! -e "${ngx_addon_dir}/src/ngx_http_waf_module_lexer.c" ] ; then
100 | is_gen='false'
101 | elif [ ! -e "${ngx_addon_dir}/inc/ngx_http_waf_module_parser.tab.h" ] ; then
102 | is_gen='false'
103 | elif [ ! -e "${ngx_addon_dir}/src/ngx_http_waf_module_parser.tab.c" ] ; then
104 | is_gen='false'
105 | fi
106 |
107 | if [ $is_gen != 'true' ] ; then
108 | cat << END
109 |
110 | $0: error: the $ngx_addon_name module requires the following command to be run to generate the necessary files.
111 |
112 | cd $ngx_addon_dir && make && cd $(pwd)
113 |
114 | END
115 | exit 1
116 | fi
117 |
118 |
119 | # Check if the uthash library is installed.
120 | ngx_feature="uthash library"
121 | ngx_feature_name=
122 | ngx_feature_run=no
123 | ngx_feature_path=$ngx_http_waf_module_inc_path
124 | ngx_feature_incs="#include "
125 | ngx_feature_libs=$ngx_http_waf_module_libs
126 | ngx_feature_test=
127 | . auto/feature
128 | if [ $ngx_found = no ] ; then
129 | PWD=$(pwd)
130 | cat << END
131 | $0: error: the $ngx_addon_name module requires the $ngx_feature.
132 |
133 | Please run:
134 | cd /usr/local/src \\
135 | && git clone https://github.com/troydhanson/uthash.git \\
136 | && export LIB_UTHASH=/usr/local/src/uthash \\
137 | && cd $PWD
138 |
139 | END
140 | PWD=''
141 | exit 1
142 | fi
143 |
144 | # Check if the C compiler supports the C99 standard.
145 | ngx_feature="C99 features"
146 | ngx_feature_name=
147 | ngx_feature_run=yes
148 | ngx_feature_path=$ngx_http_waf_module_inc_path
149 | ngx_feature_incs=
150 | ngx_feature_libs=$ngx_http_waf_module_libs
151 | ngx_feature_test=$(cat << END
152 |
153 | /* Declare variables in loops. */
154 | for(int i = 0; i < 10; i++) {}
155 |
156 | int i = 0, j = 0;
157 |
158 | /* Short-circuit operation for logical expressions. */
159 | if (i == 0 || !(j = 1)) {}
160 |
161 | if (j == 1) { return 1; }
162 |
163 | j = 0;
164 |
165 | if (i != 0 && !(j = 1)) {}
166 |
167 | if (j == 1) { return 1; }
168 |
169 | END
170 | )
171 | . auto/feature
172 | if [ $ngx_found = no ] ; then
173 | cat << END
174 | $0: error: the $ngx_addon_name module requires the $ngx_feature, make sure your C compiler supports and enables the C99 standard.
175 |
176 | For gcc, you can enable the C99 standard by appending the parameter --with-cc-opt='-std=gnu99'.
177 |
178 | END
179 | exit 1
180 | fi
181 |
182 | if [ -n "$LIB_INJECTION" ] ; then
183 | ngx_http_waf_module_inc_path="${ngx_http_waf_module_inc_path} ${LIB_INJECTION}/include"
184 | ngx_http_waf_module_libs=" ${ngx_http_waf_module_libs} -L ${LIB_INJECTION}/lib -Wl,-Bstatic -l injection -Wl,-Bdynamic "
185 | else
186 | ngx_http_waf_module_libs=" ${ngx_http_waf_module_libs} -l injection "
187 | fi
188 |
189 |
190 | # Check if libinjection exists.
191 | ngx_feature="injection library"
192 | ngx_feature_name=
193 | ngx_feature_run=no
194 | ngx_feature_path=$ngx_http_waf_module_inc_path
195 | ngx_feature_incs=$(cat << END
196 |
197 | #include
198 | #include
199 | #include
200 | #include
201 |
202 | END
203 | )
204 |
205 | ngx_feature_libs=$ngx_http_waf_module_libs
206 | ngx_feature_test=
207 | . auto/feature
208 | if [ $ngx_found = no ] ; then
209 | PWD=$(pwd)
210 | cat << END
211 | $0: error: the $ngx_addon_name module requires the $ngx_feature.
212 |
213 | Please run
214 | # You can remove directories libinjection-src and libinjection after installing the ngx_http_waf_module.
215 | git clone https://github.com/libinjection/libinjection.git libinjection-src \\
216 | && cd libinjection-src \\
217 | && ./autogen.sh \\
218 | && ./configure --prefix=$PWD/libinjection \\
219 | && make -j\$(nproc) && make install \\
220 | && export LIB_INJECTION=$PWD/libinjection \\
221 | && cd $PWD
222 | END
223 | PWD=''
224 | exit 1
225 | fi
226 |
227 | if [ -n "$LIB_SODIUM" ] ; then
228 | ngx_http_waf_module_inc_path="${ngx_http_waf_module_inc_path} ${LIB_SODIUM}/include"
229 | ngx_http_waf_module_libs=" ${ngx_http_waf_module_libs} -L ${LIB_SODIUM}/lib -Wl,-Bstatic -l sodium -Wl,-Bdynamic "
230 | else
231 | ngx_http_waf_module_libs=" ${ngx_http_waf_module_libs} -l sodium "
232 | fi
233 |
234 | # Check if libsodium exists.
235 | ngx_feature="sodium library"
236 | ngx_feature_name=
237 | ngx_feature_run=no
238 | ngx_feature_path=$ngx_http_waf_module_inc_path
239 | ngx_feature_incs='#include '
240 | ngx_feature_libs=$ngx_http_waf_module_libs
241 | ngx_feature_test=
242 | . auto/feature
243 | if [ $ngx_found = no ] ; then
244 | PWD=$(pwd)
245 | cat << END
246 | $0: error: the $ngx_addon_name module requires the $ngx_feature.
247 |
248 | Please run:
249 | On Ubuntu or Debian:
250 | apt-get update && apt-get install --yes libsodium23 libsodium-dev
251 | On Alpine:
252 | apk update && apk add libsodium libsodium-dev
253 | On other OS:
254 | # You can remove directories libsodium-src and libsodium after installing the ngx_http_waf_module.
255 | git clone https://github.com/jedisct1/libsodium.git --branch stable libsodium-src \\
256 | && cd libsodium-src \\
257 | && ./configure --prefix=$PWD/libsodium --with-pic \\
258 | && make -j\$(nproc) && make check -j \$(nproc) && make install \\
259 | && export LIB_SODIUM=$PWD/libsodium \\
260 | && cd $PWD
261 |
262 | END
263 | PWD=''
264 | exit 1
265 | fi
266 |
267 |
268 |
269 | # Clear these variables to avoid affecting the installation of other modules.
270 | ngx_feature=
271 | ngx_feature_name=
272 | ngx_feature_run=
273 | ngx_feature_path=
274 | ngx_feature_incs=
275 | ngx_feature_libs=
276 | ngx_feature_test=
277 |
278 |
279 | if test -n "$ngx_module_link"; then
280 | ngx_module_type=HTTP
281 | ngx_module_name=$ngx_addon_name
282 | ngx_module_deps=$deps
283 | ngx_module_incs=$ngx_http_waf_module_inc_path
284 | ngx_module_srcs=$srcs
285 | ngx_module_libs=$ngx_http_waf_module_libs
286 |
287 | # Let ngx_http_access_module initialize before this module,
288 | # so that this module can take effect after ngx_http_access_module,
289 | # because the initialization order and the effective order are reversed.
290 | ngx_module_order="${ngx_addon_name} ngx_http_access_module"
291 |
292 | . auto/module
293 | else
294 | HTTP_MODULES="$HTTP_MODULES ${ngx_addon_name}"
295 | HTTP_DEPS-"$HTTP_DEPS $deps"
296 | HTTP_INCS="$HTTP_INCS -I $ngx_addon_dir/inc $ngx_addon_dir/inc/libinjection/src"
297 | NGX_ADDON_SRCS="$NGX_ADDON_SRCS $srcs"
298 | fi
--------------------------------------------------------------------------------
/flex/lexer.lex:
--------------------------------------------------------------------------------
1 | %option noyywrap
2 | %option noinput
3 | %option nounput
4 | %option yylineno
5 | %option outfile="src/ngx_http_waf_module_lexer.c" header-file="inc/ngx_http_waf_module_lexer.h"
6 | %option prefix="ngx_http_waf_"
7 |
8 | %{
9 | #include
10 | #include
11 | #include
12 | #include
13 | #include
14 | // #define VM_DEBUG
15 | void ngx_http_waf_error(UT_array* array, ngx_pool_t* pool, const char* msg);
16 | %}
17 |
18 |
19 | KEYWORD_ID ^(?i:id)
20 |
21 | KEYWORD_IF ^(?i:if)
22 |
23 | KEYWORD_DO ^(?i:do)
24 |
25 | KEYWORD_URL (?i:url)
26 |
27 | KEYWORD_QUERY_STRING (?i:query_string)
28 |
29 | KEYWORD_USER_AGENT (?i:user_agent)
30 |
31 | KEYWORD_REFERER (?i:referer)
32 |
33 | KEYWORD_CLIENT_IP (?i:client_ip)
34 |
35 | KEYWORD_HEADER_IN (?i:header_in)
36 |
37 | KEYWORD_COOKIE (?i:cookie)
38 |
39 | KEYWORD_CONTAINS [[:blank:]]+(?i:contains)[[:blank:]]+
40 |
41 | KEYWORD_MATCHES [[:blank:]]+(?i:matches)[[:blank:]]+
42 |
43 | KEYWORD_EQUALS [[:blank:]]+(?i:equals)[[:blank:]]+
44 |
45 | KEYWORD_BELONG_TO [[:blank:]]+(?i:belong_to)[[:blank:]]+
46 |
47 | KEYWORD_SQLI_DETN (?i:sqli_detn)
48 |
49 | KEYWORD_XSS_DETN (?i:xss_detn)
50 |
51 | KEYWORD_RETURN (?i:return)
52 |
53 | KEYWORD_ALLOW (?i:allow)
54 |
55 | KEYWORD_NOT (?i:not)
56 |
57 | KEYWORD_OR [[:blank:]]+(?i:or)[[:blank:]]+
58 |
59 | KEYWORD_AND [[:blank:]]+(?i:and)[[:blank:]]+
60 |
61 | INDEX \[((-|_|[[:alnum:]])+)\]
62 |
63 | ID (_|[[:alpha:]])((_|[[:alnum:]]){1,49})
64 |
65 | STRING (\"[^\"]*\")|('[^']*')
66 |
67 | INTEGER (-)?[[:digit:]]+
68 |
69 | BREAK_LINE (\r)?\n
70 |
71 |
72 | %%
73 |
74 |
75 | {KEYWORD_ID} {
76 | #ifdef VM_DEBUG
77 | printf("Lexer - KEYWORD_ID\n");
78 | #endif
79 | return keyword_id;
80 | }
81 |
82 | {KEYWORD_IF} {
83 | #ifdef VM_DEBUG
84 | printf("Lexer - KEYWORD_IF\n");
85 | #endif
86 | return keyword_if;
87 | }
88 |
89 | {KEYWORD_DO} {
90 | #ifdef VM_DEBUG
91 | printf("Lexer - KEYWORD_DO\n");
92 | #endif
93 | return keyword_do;
94 | }
95 |
96 |
97 | {KEYWORD_URL} {
98 | #ifdef VM_DEBUG
99 | printf("Lexer - KEYWORD_URL\n");
100 | #endif
101 | return keyword_url;
102 | }
103 |
104 | {KEYWORD_QUERY_STRING} {
105 | #ifdef VM_DEBUG
106 | printf("Lexer - KEYWORD_QUERY_STRING\n");
107 | #endif
108 | return keyword_query_string;
109 | }
110 |
111 |
112 | {KEYWORD_USER_AGENT} {
113 | #ifdef VM_DEBUG
114 | printf("Lexer - KEYWORD_USER_AGENT\n");
115 | #endif
116 | return keyword_user_agent;
117 | }
118 |
119 | {KEYWORD_REFERER} {
120 | #ifdef VM_DEBUG
121 | printf("Lexer - KEYWORD_REFERER\n");
122 | #endif
123 | return keyword_referer;
124 | }
125 |
126 |
127 | {KEYWORD_CLIENT_IP} {
128 | #ifdef VM_DEBUG
129 | printf("Lexer - KEYWORD_CLIENT_IP\n");
130 | #endif
131 | return keyword_client_ip;
132 | }
133 |
134 |
135 | {KEYWORD_HEADER_IN} {
136 | #ifdef VM_DEBUG
137 | printf("Lexer - KEYWORD_HEADER_IN\n");
138 | #endif
139 | return keyword_header_in;
140 | }
141 |
142 | {KEYWORD_COOKIE} {
143 | #ifdef VM_DEBUG
144 | printf("Lexer - KEYWORD_COOKIE\n");
145 | #endif
146 | return keyword_cookie;
147 | }
148 |
149 | {KEYWORD_CONTAINS} {
150 | #ifdef VM_DEBUG
151 | printf("Lexer - KEYWORD_CONTAINS\n");
152 | #endif
153 | return keyword_contains;
154 | }
155 |
156 | {KEYWORD_MATCHES} {
157 | #ifdef VM_DEBUG
158 | printf("Lexer - KEYWORD_MATCHES\n");
159 | #endif
160 | return keyword_matches;
161 | }
162 |
163 | {KEYWORD_EQUALS} {
164 | #ifdef VM_DEBUG
165 | printf("Lexer - KEYWORD_EQUALS\n");
166 | #endif
167 | return keyword_equals;
168 | }
169 |
170 | {KEYWORD_BELONG_TO} {
171 | #ifdef VM_DEBUG
172 | printf("Lexer - KEYWORD_BELONG_TO");
173 | #endif
174 | return keyword_belong_to;
175 | }
176 |
177 | {KEYWORD_SQLI_DETN} {
178 | #ifdef VM_DEBUG
179 | printf("Lexer - KEYWORD_SQLI_DETN\n");
180 | #endif
181 | return keyword_sqli_detn;
182 | }
183 |
184 | {KEYWORD_XSS_DETN} {
185 | #ifdef VM_DEBUG
186 | printf("Lexer - KEYWORD_XSS_DETN\n");
187 | #endif
188 | return keyword_xss_detn;
189 | }
190 |
191 | {KEYWORD_RETURN} {
192 | #ifdef VM_DEBUG
193 | printf("Lexer - KEYWORD_RETURN\n");
194 | #endif
195 | return keyword_return;
196 | }
197 |
198 | {KEYWORD_ALLOW} {
199 | #ifdef VM_DEBUG
200 | printf("Lexer - KEYWORD_ALLOW\n");
201 | #endif
202 | return keyword_allow;
203 | }
204 |
205 | {INDEX} {
206 | strcpy(ngx_http_waf_lval.str_val, yytext + 1);
207 | ngx_http_waf_lval.str_val[yyleng - 2] = '\0';
208 | #ifdef VM_DEBUG
209 | printf("Lexer - INDEX: %s\n", yytext);
210 | #endif
211 | return token_index;
212 | }
213 |
214 |
215 | {BREAK_LINE} {
216 | #ifdef VM_DEBUG
217 | printf("Lexer - BREAK_LINE\n");
218 | #endif
219 | return token_break_line;
220 | }
221 |
222 | {KEYWORD_NOT} {
223 | #ifdef VM_DEBUG
224 | printf("Lexer - KEYWORD_NOT\n");
225 | #endif
226 | return keyword_not;
227 | }
228 |
229 | {KEYWORD_OR} {
230 | #ifdef VM_DEBUG
231 | printf("Lexer - KEYWORD_or\n");
232 | #endif
233 | return keyword_or;
234 | }
235 |
236 | {KEYWORD_AND} {
237 | #ifdef VM_DEBUG
238 | printf("Lexer - KEYWORD_and\n");
239 | #endif
240 | return keyword_and;
241 | }
242 |
243 | {STRING} {
244 | #ifdef VM_DEBUG
245 | printf("Lexer - STRING: %s\n", yytext);
246 | #endif
247 | strcpy(ngx_http_waf_lval.str_val, yytext + 1);
248 | ngx_http_waf_lval.str_val[yyleng - 2] = '\0';
249 | return token_str;
250 | }
251 |
252 | {ID} {
253 | #ifdef VM_DEBUG
254 | printf("Lexer - ID: %s\n", yytext);
255 | #endif
256 | strcpy(ngx_http_waf_lval.id_val, yytext);
257 | return token_id;
258 | }
259 |
260 | {INTEGER} {
261 | #ifdef VM_DEBUG
262 | printf("Lexer - INTEGER: %s\n", yytext);
263 | #endif
264 | ngx_http_waf_lval.int_val = atoi(yytext);
265 | return token_int;
266 | }
267 |
268 | [[:blank:]]+ {
269 | #ifdef VM_DEBUG
270 | printf("Lexer - [[:blank:]]+\n");
271 | #endif
272 | return token_blank;
273 | }
274 |
275 | . {
276 | #ifdef VM_DEBUG
277 | printf("Lexer - Other: %s\n", yytext);
278 | #endif
279 | return *yytext;
280 | }
281 |
282 | %%
283 |
284 | void ngx_http_waf_error(UT_array* array, ngx_pool_t* pool, const char* msg) {
285 | printf("error: %s in line %d\n", msg, yylineno);
286 | }
--------------------------------------------------------------------------------
/inc/ngx_http_waf_module_check.h:
--------------------------------------------------------------------------------
1 | /**
2 | * @file ngx_http_waf_module_check.h
3 | * @brief 检查诸如 IP,URL 等是否命中规则。
4 | */
5 |
6 | #include
7 | #include
8 | #include
9 | #include
10 | #include
11 | #include
12 | #include
13 | #include
14 | #include
15 | #include
16 |
17 | #ifndef NGX_HTTP_WAF_MODLULE_CHECK_H
18 | #define NGX_HTTP_WAF_MODLULE_CHECK_H
19 |
20 |
21 | /**
22 | * @brief 用来挂载到清理请求资源的函数,主要用来存储和获取 ngx_http_waf_ctx_t。
23 | */
24 | void ngx_http_waf_handler_cleanup(void *data);
25 |
26 | /**
27 | * @defgroup check 规则匹配模块
28 | * @brief 检查诸如 IP,URL 等是否命中规则。
29 | * @addtogroup check 规则匹配模块
30 | * @{
31 | */
32 |
33 |
34 | /**
35 | * @brief 检查客户端 IP 地址是否在白名单中。
36 | * @param[out] out_http_status 当出发规则时需要返回的 HTTP 状态码。
37 | * @return 如果在返回 MATCHED,反之返回 NOT_MATCHED。
38 | * @retval MATCHED IP 地址在白名单中。
39 | * @retval NOT_MATCHED IP 地址不在白名单中。
40 | */
41 | ngx_int_t ngx_http_waf_handler_check_white_ip(ngx_http_request_t* r, ngx_int_t* out_http_status);
42 |
43 |
44 | /**
45 | * @brief 检查客户端 IP 地址是否在黑名单中。
46 | * @param[out] out_http_status 当触发规则时需要返回的 HTTP 状态码。
47 | * @return 如果在返回 MATCHED,反之返回 NOT_MATCHED。
48 | * @retval MATCHED IP 地址在黑名单中。
49 | * @retval NOT_MATCHED IP 地址不在黑名单中。
50 | */
51 | ngx_int_t ngx_http_waf_handler_check_black_ip(ngx_http_request_t* r, ngx_int_t* out_http_status);
52 |
53 |
54 | /**
55 | * @brief 检查客户端 IP 地址的访问频次(60 秒内)是否超出了限制。
56 | * @param[out] out_http_status 当触发规则时需要返回的 HTTP 状态码。
57 | * @return 如果超出 MATCHED,反之返回 NOT_MATCHED。
58 | * @retval MATCHED 超出限制。
59 | * @retval NOT_MATCHED 未超出限制。
60 | */
61 | ngx_int_t ngx_http_waf_handler_check_cc(ngx_http_request_t* r, ngx_int_t* out_http_status);
62 |
63 |
64 | /**
65 | * @brief 检查 URL 是否在白名单中。
66 | * @param[out] out_http_status 当触发规则时需要返回的 HTTP 状态码。
67 | * @return 如果在返回 MATCHED,反之返回 NOT_MATCHED。
68 | * @retval MATCHED 在白名单中。
69 | * @retval NOT_MATCHED 不在白名单中
70 | */
71 | ngx_int_t ngx_http_waf_handler_check_white_url(ngx_http_request_t* r, ngx_int_t* out_http_status);
72 |
73 |
74 | /**
75 | * @brief 检查 URL 是否在黑名单中
76 | * @param[out] out_http_status 当触发规则时需要返回的 HTTP 状态码。
77 | * @return 如果在返回 MATCHED,反之返回 NOT_MATCHED。
78 | * @retval MATCHED 在黑名单中。
79 | * @retval NOT_MATCHED 不在黑名单中
80 | */
81 | ngx_int_t ngx_http_waf_handler_check_black_url(ngx_http_request_t* r, ngx_int_t* out_http_status);
82 |
83 |
84 | /**
85 | * @brief 检查请求参数是否在黑名单中
86 | * @param[out] out_http_status 当触发规则时需要返回的 HTTP 状态码。
87 | * @return 如果在返回 MATCHED,反之返回 NOT_MATCHED。
88 | * @retval MATCHED 在黑名单中。
89 | * @retval NOT_MATCHED 不在黑名单中
90 | */
91 | ngx_int_t ngx_http_waf_handler_check_black_args(ngx_http_request_t* r, ngx_int_t* out_http_status);
92 |
93 |
94 | /**
95 | * @brief 检查 UserAgent 是否在黑名单中
96 | * @param[out] out_http_status 当触发规则时需要返回的 HTTP 状态码。
97 | * @return 如果在返回 MATCHED,反之返回 NOT_MATCHED。
98 | * @retval MATCHED 在黑名单中。
99 | * @retval NOT_MATCHED 不在黑名单中
100 | */
101 | ngx_int_t ngx_http_waf_handler_check_black_user_agent(ngx_http_request_t* r, ngx_int_t* out_http_status);
102 |
103 |
104 | /**
105 | * @brief 检查 Referer 是否在白名单中
106 | * @param[out] out_http_status 当触发规则时需要返回的 HTTP 状态码。
107 | * @return 如果在返回 MATCHED,反之返回 NOT_MATCHED。
108 | * @retval MATCHED 在白名单中。
109 | * @retval NOT_MATCHED 不在白黑名单中
110 | */
111 | ngx_int_t ngx_http_waf_handler_check_white_referer(ngx_http_request_t* r, ngx_int_t* out_http_status);
112 |
113 |
114 | /**
115 | * @brief 检查 Referer 是否在黑名单中
116 | * @param[out] out_http_status 当触发规则时需要返回的 HTTP 状态码。
117 | * @return 如果在返回 MATCHED,反之返回 NOT_MATCHED。
118 | * @retval MATCHED 在黑名单中。
119 | * @retval NOT_MATCHED 不在黑名单中
120 | */
121 | ngx_int_t ngx_http_waf_handler_check_black_referer(ngx_http_request_t* r, ngx_int_t* out_http_status);
122 |
123 |
124 | /**
125 | * @brief 检查 Cookie 是否在黑名单中
126 | * @param[out] out_http_status 当触发规则时需要返回的 HTTP 状态码。
127 | * @return 如果在返回 MATCHED,反之返回 NOT_MATCHED。
128 | * @retval MATCHED 在黑名单中。
129 | * @retval NOT_MATCHED 不在黑名单中
130 | */
131 | ngx_int_t ngx_http_waf_handler_check_black_cookie(ngx_http_request_t* r, ngx_int_t* out_http_status);
132 |
133 |
134 | /**
135 | * @brief 检查请求体内容是否存在于黑名单中,存在则拦截,反之放行。
136 | */
137 | ngx_int_t ngx_http_waf_handler_check_black_post(ngx_http_request_t* r, ngx_int_t* out_http_status);
138 |
139 |
140 | /**
141 | * @brief 获取模块上下文和 server 块配置。
142 | */
143 | void ngx_http_waf_get_ctx_and_conf(ngx_http_request_t* r, ngx_http_waf_loc_conf_t** conf, ngx_http_waf_ctx_t** ctx);
144 |
145 |
146 | /**
147 | * @brief 测试数组内的所有正则
148 | * @param[in] str 被测试的字符串
149 | * @param[in] array 包含若干个正则的数组
150 | * @param[in] rule_type 触发规则时的规则类型
151 | * @param[in] cache 检测时所使用的缓存管理器
152 | * @return 如果匹配到返回 NGX_HTTP_WAF_MATCHED,反之则为 NGX_HTTP_WAF_NOT_MATCHED。
153 | */
154 | ngx_int_t ngx_http_waf_regex_exec_arrray_sqli_xss(ngx_http_request_t* r,
155 | ngx_str_t* str,
156 | ngx_array_t* array,
157 | const u_char* rule_type,
158 | lru_cache_t* cache,
159 | int check_sql_injection,
160 | int check_xss);
161 |
162 | /**
163 | * @}
164 | */
165 |
166 |
167 | #endif
168 |
--------------------------------------------------------------------------------
/inc/ngx_http_waf_module_config.h:
--------------------------------------------------------------------------------
1 | /**
2 | * @file ngx_http_waf_module_config.h
3 | * @brief 读取 nginx.conf 内的配置以及规则文件。
4 | */
5 |
6 | #include
7 | #include
8 | #include
9 | #include
10 | #include
11 | #include
12 | #include
13 | #include
14 | #include
15 |
16 |
17 | #ifndef __STDC_WANT_LIB_EXT1__
18 | #define __STDC_WANT_LIB_EXT1__ 1
19 | #endif
20 |
21 | #include
22 | #include
23 | #include
24 |
25 | #ifndef NGX_HTTP_WAF_MODULE_CONFIG_H
26 | #define NGX_HTTP_WAF_MODULE_CONFIG_H
27 |
28 |
29 | ngx_int_t ngx_http_waf_handler_access_phase(ngx_http_request_t* r);
30 |
31 | /**
32 | * @defgroup config 配置读取和处理模块
33 | * @brief 读取 nginx.conf 内的配置以及规则文件。
34 | * @addtogroup config 配置读取和处理模块
35 | * @{
36 | */
37 |
38 |
39 | /**
40 | * @brief 读取配置项 waf,该项表示是否启用模块。
41 | */
42 | char* ngx_http_waf_conf(ngx_conf_t* cf, ngx_command_t* cmd, void* conf);
43 |
44 |
45 | /**
46 | * @brief 读取配置项 waf_rule_path,该项表示存有配置文件的文件夹的绝对路径,必须以 '/' 结尾。
47 | */
48 | char* ngx_http_waf_rule_path_conf(ngx_conf_t* cf, ngx_command_t* cmd, void* conf);
49 |
50 |
51 | /**
52 | * @brief 读取配置项 waf_mode,该项表示拦截模式。
53 | */
54 | char* ngx_http_waf_mode_conf(ngx_conf_t* cf, ngx_command_t* cmd, void* conf);
55 |
56 |
57 | /**
58 | * @brief 读取配置项 waf_cc_deny,该项表示最高的访问频次以及超出后的拉黑时间。
59 | */
60 | char* ngx_http_waf_cc_deny_conf(ngx_conf_t* cf, ngx_command_t* cmd, void* conf);
61 |
62 |
63 | /**
64 | * @brief 读取配置项 waf_cache,该项表示缓存相关的参数。
65 | */
66 | char* ngx_http_waf_cache_conf(ngx_conf_t* cf, ngx_command_t* cmd, void* conf);
67 |
68 |
69 | /**
70 | * @brief 读取配置项 waf_under_attack,该项用来设置五秒盾相关的参数。
71 | */
72 | char* ngx_http_waf_under_attack_conf(ngx_conf_t* cf, ngx_command_t* cmd, void* conf);
73 |
74 |
75 | /**
76 | * @brief 读取配置项 waf_priority,该项用来设置检查项目的优先级。
77 | */
78 | char* ngx_http_waf_priority_conf(ngx_conf_t* cf, ngx_command_t* cmd, void* conf);
79 |
80 |
81 | /**
82 | * @brief 读取配置项 waf_http_status,该项用来设置检查项目的优先级。
83 | */
84 | char* ngx_http_waf_http_status_conf(ngx_conf_t* cf, ngx_command_t* cmd, void* conf);
85 |
86 |
87 | /**
88 | * @brief 当读取 waf_log 变量时的回调函数,这个变量当启动检查时不为空,反之为空字符串。
89 | */
90 | ngx_int_t ngx_http_waf_log_get_handler(ngx_http_request_t* r, ngx_http_variable_value_t* v, uintptr_t data);
91 |
92 |
93 | /**
94 | * @brief 当读取 waf_blocking_log 变量时的回调函数,这个变量当拦截时不为空,反之为空字符串。
95 | */
96 | ngx_int_t ngx_http_waf_blocking_log_get_handler(ngx_http_request_t* r, ngx_http_variable_value_t* v, uintptr_t data);
97 |
98 |
99 | /**
100 | * @brief 当读取 waf_blocked 变量时的回调函数,这个变量当请求被拦截的时候是 "true",反之是 "false"。
101 | */
102 | ngx_int_t ngx_http_waf_blocked_get_handler(ngx_http_request_t* r, ngx_http_variable_value_t* v, uintptr_t data);
103 |
104 |
105 | /**
106 | * @brief 当读取 waf_rule_type 变量时的回调函数,这个变量会显示触发了的规则类型。
107 | */
108 | ngx_int_t ngx_http_waf_rule_type_get_handler(ngx_http_request_t* r, ngx_http_variable_value_t* v, uintptr_t data);
109 |
110 |
111 | /**
112 | * @brief 当读取 waf_rule_deatils 变量时的回调函数,这个变量会显示触发了的规则的细节。
113 | */
114 | ngx_int_t ngx_http_waf_rule_deatils_handler(ngx_http_request_t* r, ngx_http_variable_value_t* v, uintptr_t data);
115 |
116 |
117 | /**
118 | * @brief 当读取 waf_spend 变量时的回调函数,这个变量表示本次检查花费的时间(毫秒)。
119 | */
120 | ngx_int_t ngx_http_waf_spend_handler(ngx_http_request_t* r, ngx_http_variable_value_t* v, uintptr_t data);
121 |
122 |
123 | /**
124 | * @brief 初始化结构体 ngx_http_waf_main_conf_t
125 | */
126 | void* ngx_http_waf_create_main_conf(ngx_conf_t* cf);
127 |
128 |
129 | /**
130 | * @brief 初始化结构体 ngx_http_waf_loc_conf_t
131 | */
132 | void* ngx_http_waf_create_loc_conf(ngx_conf_t* cf);
133 |
134 |
135 | /**
136 | * @brief 合并各个配置段的 ngx_http_waf_loc_conf_t
137 | */
138 | char* ngx_http_waf_merge_loc_conf(ngx_conf_t *cf, void *prev, void *conf);
139 |
140 |
141 | /**
142 | * @brief 在读取完全部配置后进行一些操作。
143 | * @li 将处理函数挂载到对应的请求处理阶段。
144 | * @li 初始化相关的 nginx 变量。
145 | */
146 | ngx_int_t ngx_http_waf_init_after_load_config(ngx_conf_t* cf);
147 |
148 |
149 | /**
150 | * @brief 用于 CC 防护的共享内存的初始时的回调函数
151 | * @param[in] zone 正在初始化的共享内存
152 | * @param[in] data ngx_http_waf_loc_conf_t
153 | */
154 | ngx_int_t ngx_http_waf_shm_zone_cc_deny_init(ngx_shm_zone_t *zone, void *data);
155 |
156 |
157 | /**
158 | * @brief 初始化结构体 ngx_http_waf_loc_conf_t
159 | */
160 | ngx_http_waf_loc_conf_t* ngx_http_waf_init_conf(ngx_conf_t* cf);
161 |
162 |
163 | /**
164 | * @brief 初始化用于 CC 防护的共享内存。
165 | */
166 | ngx_int_t ngx_http_waf_init_cc_shm(ngx_conf_t* cf, ngx_http_waf_loc_conf_t* conf);
167 |
168 |
169 | /**
170 | * @brief 初始化 LRU 缓存。
171 | */
172 | ngx_int_t ngx_http_waf_init_lru_cache(ngx_conf_t* cf, ngx_http_waf_loc_conf_t* conf);
173 |
174 |
175 | /**
176 | * @brief 读取所有的规则。
177 | */
178 | ngx_int_t ngx_http_waf_load_all_rule(ngx_conf_t* cf, ngx_http_waf_loc_conf_t* conf);
179 |
180 |
181 | /**
182 | * @brief 读取指定文件的内容到容器中。
183 | * @param[in] file_name 要读取的配置文件完整路径。
184 | * @param[out] container 存放读取结果的容器。
185 | * @param[in] mode 读取模式
186 | * @li 当 mode = 0 时会将读取到文本编译成正则表达式再存储。容器类型为 ngx_array_t。
187 | * @li 当 mode = 1 时会将读取到的文本转化为 ipv4_t 再存储。容器类型为 ip_trie_t。
188 | * @li 当 mode = 2 时会将读取到的文本转化为 ipv6_t 再存储。容器类型为 ip_trie_t。
189 | * @return 读取操作的结果。
190 | * @retval NGX_HTTP_WAF_SUCCESS 读取成功。
191 | * @retval FAIL 读取中发生错误。
192 | */
193 | ngx_int_t load_into_container(ngx_conf_t* cf, const char* file_name, void* container, ngx_int_t mode);
194 |
195 |
196 | /**
197 | * @brief 分配用于存放规则的内存。
198 | * @note 如果已经分配则什么都不做。
199 | */
200 | ngx_int_t ngx_http_waf_alloc_memory(ngx_conf_t* cf, ngx_http_waf_loc_conf_t* conf);
201 |
202 |
203 | /**
204 | * @brief 释放用于存储规则的内存。
205 | * @note 如果已经释放或者从未分配则什么都不做。
206 | */
207 | ngx_int_t ngx_http_waf_free_memory(ngx_conf_t* cf, ngx_http_waf_loc_conf_t* conf);
208 |
209 | /**
210 | * @}
211 | */
212 |
213 | #endif // !NGX_HTTP_WAF_MODULE_CONFIG_H
214 |
--------------------------------------------------------------------------------
/inc/ngx_http_waf_module_core.h:
--------------------------------------------------------------------------------
1 | /**
2 | * @file ngx_http_waf_module_core.h
3 | * @brief 配置块的初始化和请求检测函数。
4 | */
5 |
6 |
7 | #include
8 | #include
9 | #include
10 | #include
11 | #include
12 | #include
13 | #include
14 | #include
15 | #include
16 |
17 |
18 |
19 | #ifndef NGX_HTTP_WAF_MODULE_CORE_H
20 | #define NGX_HTTP_WAF_MODULE_CORE_H
21 |
22 |
23 | /**
24 | * @defgroup core 核心模块
25 | * @brief 配置块的初始化和请求检测函数。
26 | * @addtogroup core 核心模块
27 | * @{
28 | */
29 |
30 |
31 | /**
32 | * @brief 当 Worker 进程启动时调用的函数,用于重置随机数种子。
33 | */
34 | ngx_int_t ngx_http_waf_init_process(ngx_cycle_t *cycle);
35 |
36 |
37 | /**
38 | * @brief NGX_HTTP_ACCESS_PHASE 阶段的处理函数
39 | */
40 | ngx_int_t ngx_http_waf_handler_access_phase(ngx_http_request_t* r);
41 |
42 |
43 | /**
44 | * @brief 执行全部的检查项目
45 | * @param r 本次要处理的请求
46 | * @param is_check_cc 是否执行 CC 防护逻辑
47 | * @return http 状态码或者 nginx 控制量
48 | * @retval NGX_DECLINED 放行本次请求
49 | * @retval NGX_DONE 将在其它地方进行检查,通常是因为执行了 POST 检测
50 | */
51 | ngx_int_t ngx_http_waf_check_all(ngx_http_request_t* r, ngx_int_t is_check_cc);
52 |
53 |
54 | void ngx_http_waf_handler_cleanup(void *data);
55 |
56 | /**
57 | * @}
58 | */
59 |
60 | #endif // !NGX_HTTP_WAF_MODULE_CORE_H
61 |
--------------------------------------------------------------------------------
/inc/ngx_http_waf_module_ip_trie.h:
--------------------------------------------------------------------------------
1 | /**
2 | * @file ngx_http_waf_module_ip_trie.h
3 | * @brief IP 前缀树。
4 | */
5 |
6 | #include
7 | #include
8 | #include
9 |
10 | #ifndef NGX_HTTP_WAF_MODULE_IP_TRIE_h
11 | #define NGX_HTTP_WAF_MODULE_IP_TRIE_h
12 |
13 | /**
14 | * @defgroup ip_trie IP 前缀树
15 | * @addtogroup ip_trie IP 前缀树
16 | * @{
17 | */
18 |
19 | /**
20 | * @brief 初始化一个前缀树。
21 | * @param[out] trie 要初始化的前缀树。
22 | * @param[in] memory_pool 初始化、添加、删除节点所用的内存池。
23 | * @param[in] ip_type 存储的 IP 地址类型。
24 | * @return 返回 NGX_HTTP_WAF_SUCCESS 表示初始化成功,反之为 NGX_HTTP_WAF_FAIL。
25 | */
26 | ngx_int_t ip_trie_init(ip_trie_t* trie, mem_pool_type_e pool_type, void* native_pool, int ip_type);
27 |
28 |
29 | /**
30 | * @brief 插入一个 IP 地址。
31 | * @param[in] trie 要操作的前缀树。
32 | * @param[in] inx_addr IP 地址。
33 | * @param[in] suffix_num IP 网段长度。
34 | * @param[in] text IP 的字符串形式。
35 | * @return 返回 NGX_HTTP_WAF_SUCCESS 表示成功,反之为 NGX_HTTP_WAF_FAIL。
36 | */
37 | ngx_int_t ip_trie_add(ip_trie_t* trie, inx_addr_t* inx_addr, uint32_t suffix_num, void* data, size_t data_byte_length);
38 |
39 | /**
40 | * @brief 查找 IP 是否存在。
41 | * @param[in] trie 要操作的前缀树。
42 | * @param[in] inx_addr IP 地址。
43 | * @param[out] ip_trie_node 找到之后此指针将指向对应的节点。
44 | * @return 返回 NGX_HTTP_WAF_SUCCESS 表示找到,反之为 FAIL。
45 | */
46 | ngx_int_t ip_trie_find(ip_trie_t* trie, inx_addr_t* inx_addr, ip_trie_node_t** ip_trie_node);
47 |
48 | /**
49 | * @brief 删除一个 IP 地址。
50 | * @param[in] trie 要操作的前缀树。
51 | * @param[in] inx_addr IP 地址。
52 | * @return 成功返回 NGX_HTTP_WAF_SUCCESS,反之则不是
53 | * @warning 不会释放节点所在占用的内存,但是会释放节点的 data 域所指向的内存。
54 | */
55 | // static ngx_int_t ip_trie_delete(ip_trie_t* trie, inx_addr_t* inx_addr);
56 |
57 | /**
58 | * @brief 清空整个树,除根节点以外的全部节点的内存和 data 域指向的内存。
59 | * @param[in] trie 要操作的前缀树。
60 | * @return 成功返回 NGX_HTTP_WAF_SUCCESS,反之则不是
61 | */
62 | // static ngx_int_t ip_trie_clear(ip_trie_t* trie);
63 |
64 |
65 | /**
66 | * @brief 先序遍历,并将每个节点存入 head 为头的链表里。
67 | * @param[in] node 开始遍历的节点
68 | * @param[out] head 链表头
69 | */
70 | // static void _ip_trie_traversal(ip_trie_node_t* node, circular_doublly_linked_list_t** head);
71 |
72 | /**
73 | * @}
74 | */
75 |
76 | #endif
77 |
--------------------------------------------------------------------------------
/inc/ngx_http_waf_module_lru_cache.h:
--------------------------------------------------------------------------------
1 | /**
2 | * @file ngx_http_waf_module_lru_cache.h.h
3 | * @brief LRU 缓存管理器
4 | */
5 |
6 | #include
7 | #include
8 | #include
9 |
10 | #ifndef __NGX_HTTP_WAF_MODULE_LRU_CACHE_H__
11 | #define __NGX_HTTP_WAF_MODULE_LRU_CACHE_H__
12 |
13 |
14 | void lru_cache_init(lru_cache_t** lru, size_t capacity, mem_pool_type_e pool_type, void* native_pool);
15 |
16 |
17 | lru_cache_add_result_t lru_cache_add(lru_cache_t* lru, void* key, size_t key_len);
18 |
19 |
20 | lru_cache_find_result_t lru_cache_find(lru_cache_t* lru, void* key, size_t key_len);
21 |
22 |
23 | void* lru_cache_calloc(lru_cache_t* lru, size_t size);
24 |
25 |
26 | void lru_cache_free(lru_cache_t* lru, void* addr);
27 |
28 |
29 | void lru_cache_delete(lru_cache_t* lru, void* key, size_t key_len);
30 |
31 |
32 | void lru_cache_eliminate(lru_cache_t* lru, size_t count);
33 |
34 |
35 | void lru_cache_destory(lru_cache_t* lru);
36 |
37 |
38 | #endif
--------------------------------------------------------------------------------
/inc/ngx_http_waf_module_macro.h:
--------------------------------------------------------------------------------
1 | /**
2 | * @file ngx_http_waf_module_macro.h
3 | * @brief 定义一些必要的宏
4 | */
5 |
6 | #ifndef NGX_HTTP_WAF_MODULE_MACRO_H
7 | #define NGX_HTTP_WAF_MODULE_MACRO_H
8 |
9 | /* 对应配置文件的文件名 */
10 | #define NGX_HTTP_WAF_IPV4_FILE ("ipv4")
11 | #define NGX_HTTP_WAF_IPV6_FILE ("ipv6")
12 | #define NGX_HTTP_WAF_URL_FILE ("url")
13 | #define NGX_HTTP_WAF_ARGS_FILE ("args")
14 | #define NGX_HTTP_WAF_UA_FILE ("user-agent")
15 | #define NGX_HTTP_WAF_REFERER_FILE ("referer")
16 | #define NGX_HTTP_WAF_COOKIE_FILE ("cookie")
17 | #define NGX_HTTP_WAF_POST_FILE ("post")
18 | #define NGX_HTTP_WAF_WHITE_IPV4_FILE ("white-ipv4")
19 | #define NGX_HTTP_WAF_WHITE_IPV6_FILE ("white-ipv6")
20 | #define NGX_HTTP_WAF_WHITE_URL_FILE ("white-url")
21 | #define NGX_HTTP_WAF_WHITE_REFERER_FILE ("white-referer")
22 | #define NGX_HTTP_WAF_ADVANCED_FILE ("advanced")
23 |
24 |
25 | #define NGX_HTTP_WAF_FALSE (0)
26 |
27 | #define NGX_HTTP_WAF_FAIL (0)
28 |
29 | #define NGX_HTTP_WAF_NOT_MATCHED (0)
30 |
31 | #define NGX_HTTP_WAF_TRUE (1)
32 |
33 | #define NGX_HTTP_WAF_SUCCESS (1)
34 |
35 | #define NGX_HTTP_WAF_MATCHED (1)
36 |
37 | #define NGX_HTTP_WAF_PROCESSING (2)
38 |
39 | #define NGX_HTTP_WAF_MALLOC_ERROR (3)
40 |
41 | #define NGX_HTTP_WAF_KEY_EXISTS (4)
42 |
43 | #define NGX_HTTP_WAF_KEY_NOT_EXISTS (5)
44 |
45 | #define NGX_HTTP_WAF_BAD (6)
46 |
47 |
48 | /**
49 | * @def NGX_HTTP_WAF_RULE_MAX_LEN
50 | * @brief 每条规则的占用的最大字节数。
51 | */
52 | #define NGX_HTTP_WAF_RULE_MAX_LEN (256 * 4 * 8)
53 |
54 | /**
55 | * @def NGX_HTTP_WAF_INITIAL_SIZE
56 | * @brief 初始化配置块内存池时的初始内存池大小。
57 | */
58 | #define NGX_HTTP_WAF_INITIAL_SIZE (1024 * 1024 * 5)
59 |
60 | #define NGX_HTTP_WAF_MAX_ALLOC_TIMES (100000)
61 |
62 | /**
63 | * @def NGX_HTTP_WAF_SHARE_MEMORY_NAME
64 | * @brief 用于 CC 防护的共享内存的名称
65 | */
66 | #define NGX_HTTP_WAF_SHARE_MEMORY_CC_DNEY_NAME ("__ADD-SP_NGX_WAF_CC_DENY_SHM__")
67 |
68 | /**
69 | * @def NGX_HTTP_WAF_SHATE_MEMORY_CC_DENY_MIN_SIZE
70 | * @brief 用于 CC 防护的共享内存的最小大小(字节)
71 | */
72 | #define NGX_HTTP_WAF_SHARE_MEMORY_CC_DENY_MIN_SIZE (1024 * 1024 * 20)
73 |
74 |
75 | #define NGX_HTTP_WAF_UNDER_ATTACH_UID_LEN (64)
76 |
77 |
78 | #define NGX_HTTP_WAF_UNDER_ATTACH_TIME_LEN (16)
79 |
80 |
81 | #define NGX_HTTP_WAF_SHA256_HEX_LEN (crypto_hash_sha256_BYTES * 2)
82 |
83 |
84 | /**
85 | * @def CACHE_ITEM_MIN_SIZE
86 | * @brief 用于设置缓存项数的上限
87 | */
88 | #define NGX_HTTP_WAF_CACHE_ITEM_MIN_NUM (50)
89 |
90 | /**
91 | * @def NGX_HTTP_WAF_MODE_INSPECT_GET
92 | * @brief 对 GET 请求进行检查
93 | */
94 | #define NGX_HTTP_WAF_MODE_INSPECT_GET NGX_HTTP_GET
95 |
96 | /**
97 | * @def NGX_HTTP_WAF_MODE_INSPECT_HEAD
98 | * @brief 对 HEAD 请求进行检查
99 | */
100 | #define NGX_HTTP_WAF_MODE_INSPECT_HEAD NGX_HTTP_HEAD
101 |
102 | /**
103 | * @def NGX_HTTP_WAF_MODE_INSPECT_POST
104 | * @brief 对 POST 请求进行检查
105 | */
106 | #define NGX_HTTP_WAF_MODE_INSPECT_POST NGX_HTTP_POST
107 |
108 | /**
109 | * @def NGX_HTTP_WAF_MODE_INSPECT_PUT
110 | * @brief 对 PUT 请求进行检查
111 | */
112 | #define NGX_HTTP_WAF_MODE_INSPECT_PUT NGX_HTTP_PUT
113 |
114 | /**
115 | * @def NGX_HTTP_WAF_MODE_INSPECT_DELETE
116 | * @brief 对 DELETE 请求进行检查
117 | */
118 | #define NGX_HTTP_WAF_MODE_INSPECT_DELETE NGX_HTTP_DELETE
119 |
120 | /**
121 | * @def NGX_HTTP_WAF_MODE_INSPECT_MKCOL
122 | * @brief 对 MKCOL 请求进行检查
123 | */
124 | #define NGX_HTTP_WAF_MODE_INSPECT_MKCOL NGX_HTTP_MKCOL
125 |
126 | /**
127 | * @def NGX_HTTP_WAF_MODE_INSPECT_COPY
128 | * @brief 对 COPY 请求进行检查
129 | */
130 | #define NGX_HTTP_WAF_MODE_INSPECT_COPY NGX_HTTP_COPY
131 |
132 | /**
133 | * @def NGX_HTTP_WAF_MODE_INSPECT_MOVE
134 | * @brief 对 MOVE 请求进行检查
135 | */
136 | #define NGX_HTTP_WAF_MODE_INSPECT_MOVE NGX_HTTP_MOVE
137 |
138 | /**
139 | * @def NGX_HTTP_WAF_MODE_INSPECT_OPTIONS
140 | * @brief 对 OPTIONS 请求进行检查
141 | */
142 | #define NGX_HTTP_WAF_MODE_INSPECT_OPTIONS NGX_HTTP_OPTIONS
143 |
144 | /**
145 | * @def NGX_HTTP_WAF_MODE_INSPECT_PROPFIND
146 | * @brief 对 PROPFIND 请求进行检查
147 | */
148 | #define NGX_HTTP_WAF_MODE_INSPECT_PROPFIND NGX_HTTP_PROPFIND
149 |
150 | /**
151 | * @def NGX_HTTP_WAF_MODE_INSPECT_PROPPATCH
152 | * @brief 对 PROPPATCH 请求进行检查
153 | */
154 | #define NGX_HTTP_WAF_MODE_INSPECT_PROPPATCH NGX_HTTP_PROPPATCH
155 |
156 | /**
157 | * @def NGX_HTTP_WAF_MODE_INSPECT_LOCK
158 | * @brief 对 LOCK 请求进行检查
159 | */
160 | #define NGX_HTTP_WAF_MODE_INSPECT_LOCK NGX_HTTP_LOCK
161 |
162 | /**
163 | * @def NGX_HTTP_WAF_MODE_INSPECT_UNLOCK
164 | * @brief 对 UNLOCK 请求进行检查
165 | */
166 | #define NGX_HTTP_WAF_MODE_INSPECT_UNLOCK NGX_HTTP_UNLOCK
167 |
168 | /**
169 | * @def NGX_HTTP_WAF_MODE_INSPECT_PATCH
170 | * @brief 对 PATCH 请求进行检查
171 | */
172 | #define NGX_HTTP_WAF_MODE_INSPECT_PATCH NGX_HTTP_PATCH
173 |
174 | /**
175 | * @def NGX_HTTP_WAF_MODE_INSPECT_TRACE
176 | * @brief 对 TRACE 请求进行检查
177 | */
178 | #define NGX_HTTP_WAF_MODE_INSPECT_TRACE NGX_HTTP_TRACE
179 |
180 | /**
181 | * @def NGX_HTTP_WAF_MODE_INSPECT_IP
182 | * @brief 启用 IP 检查规则
183 | */
184 | #define NGX_HTTP_WAF_MODE_INSPECT_IP (NGX_HTTP_WAF_MODE_INSPECT_TRACE << 1)
185 |
186 | /**
187 | * @def NGX_HTTP_WAF_MODE_INSPECT_URL
188 | * @brief 启用 URL 检查规则
189 | */
190 | #define NGX_HTTP_WAF_MODE_INSPECT_URL (NGX_HTTP_WAF_MODE_INSPECT_IP << 1)
191 |
192 | /**
193 | * @def NGX_HTTP_WAF_MODE_INSPECT_RB
194 | * @brief 启用 Request Body 检查规则
195 | */
196 | #define NGX_HTTP_WAF_MODE_INSPECT_RB (NGX_HTTP_WAF_MODE_INSPECT_URL << 1)
197 |
198 | /**
199 | * @def NGX_HTTP_WAF_MODE_INSPECT_ARGS
200 | * @brief 启用 ARGS(GET 请求参数) 检查规则
201 | */
202 | #define NGX_HTTP_WAF_MODE_INSPECT_ARGS (NGX_HTTP_WAF_MODE_INSPECT_RB << 1)
203 |
204 | /**
205 | * @def NGX_HTTP_WAF_MODE_INSPECT_UA
206 | * @brief 启用 UserAgent 检查规则
207 | */
208 | #define NGX_HTTP_WAF_MODE_INSPECT_UA (NGX_HTTP_WAF_MODE_INSPECT_ARGS << 1)
209 |
210 | /**
211 | * @def NGX_HTTP_WAF_MODE_INSPECT_COOKIE
212 | * @brief 启用 COOKIE 检查规则
213 | */
214 | #define NGX_HTTP_WAF_MODE_INSPECT_COOKIE (NGX_HTTP_WAF_MODE_INSPECT_UA << 1)
215 |
216 | /**
217 | * @def NGX_HTTP_WAF_MODE_INSPECT_REFERER
218 | * @brief 启用 Referer 检查规则
219 | */
220 | #define NGX_HTTP_WAF_MODE_INSPECT_REFERER (NGX_HTTP_WAF_MODE_INSPECT_COOKIE << 1)
221 |
222 | /**
223 | * @def NGX_HTTP_WAF_MODE_INSPECT_CC
224 | * @brief 启用 CC 防御
225 | */
226 | #define NGX_HTTP_WAF_MODE_INSPECT_CC (NGX_HTTP_WAF_MODE_INSPECT_REFERER << 1)
227 |
228 |
229 | /**
230 | * @def NGX_HTTP_WAF_MODE_INSPECT_ADV
231 | * @brief 启用高级规则
232 | */
233 | #define NGX_HTTP_WAF_MODE_INSPECT_ADV (NGX_HTTP_WAF_MODE_INSPECT_CC << 1)
234 |
235 |
236 | /**
237 | * @def NGX_HTTP_WAF_MODE_EXTRA_CACHE
238 | * @brief 启用缓存,但是不缓存 POST 检查。
239 | */
240 | #define NGX_HTTP_WAF_MODE_EXTRA_CACHE (NGX_HTTP_WAF_MODE_INSPECT_ADV << 1)
241 |
242 |
243 | /**
244 | * @def NGX_HTTP_WAF_MODE_LIB_INJECTION_SQLI
245 | * @brief 启用 libinjection 进行 SQL 注入检查。
246 | */
247 | #define NGX_HTTP_WAF_MODE_LIB_INJECTION_SQLI (NGX_HTTP_WAF_MODE_EXTRA_CACHE << 1)
248 |
249 |
250 | /**
251 | * @def NGX_HTTP_WAF_MODE_LIB_INJECTION_XSS
252 | * @brief 启用 libinjection 进行 XSS 检查。
253 | */
254 | #define NGX_HTTP_WAF_MODE_LIB_INJECTION_XSS (NGX_HTTP_WAF_MODE_LIB_INJECTION_SQLI << 1)
255 |
256 |
257 | /**
258 | * @def NGX_HTTP_WAF_MODE_LIB_INJECTION
259 | * @brief 启用 libinjection 进行 SQL 注入检查和 XSS 检查。
260 | */
261 | #define NGX_HTTP_WAF_MODE_LIB_INJECTION (NGX_HTTP_WAF_MODE_LIB_INJECTION_SQLI \
262 | | NGX_HTTP_WAF_MODE_LIB_INJECTION_XSS)
263 |
264 |
265 | /**
266 | * @def NGX_HTTP_WAF_MODE_CMN_METH
267 | * @brief 常见的请求方法
268 | */
269 | #define NGX_HTTP_WAF_MODE_CMN_METH (NGX_HTTP_WAF_MODE_INSPECT_GET \
270 | | NGX_HTTP_WAF_MODE_INSPECT_POST \
271 | | NGX_HTTP_WAF_MODE_INSPECT_HEAD)
272 |
273 |
274 | /**
275 | * @def NGX_HTTP_WAF_MODE_ALL_METH
276 | * @brief 所有的
277 | */
278 | #define NGX_HTTP_WAF_MODE_ALL_METH (NGX_HTTP_WAF_MODE_INSPECT_GET \
279 | | NGX_HTTP_WAF_MODE_INSPECT_HEAD \
280 | | NGX_HTTP_WAF_MODE_INSPECT_POST \
281 | | NGX_HTTP_WAF_MODE_INSPECT_PUT \
282 | | NGX_HTTP_WAF_MODE_INSPECT_DELETE \
283 | | NGX_HTTP_WAF_MODE_INSPECT_MKCOL \
284 | | NGX_HTTP_WAF_MODE_INSPECT_COPY \
285 | | NGX_HTTP_WAF_MODE_INSPECT_MOVE \
286 | | NGX_HTTP_WAF_MODE_INSPECT_OPTIONS \
287 | | NGX_HTTP_WAF_MODE_INSPECT_PROPFIND \
288 | | NGX_HTTP_WAF_MODE_INSPECT_PROPPATCH \
289 | | NGX_HTTP_WAF_MODE_INSPECT_LOCK \
290 | | NGX_HTTP_WAF_MODE_INSPECT_UNLOCK \
291 | | NGX_HTTP_WAF_MODE_INSPECT_PATCH \
292 | | NGX_HTTP_WAF_MODE_INSPECT_TRACE)
293 |
294 |
295 |
296 | /**
297 | * @def MODE_STD
298 | * @brief 标准工作模式
299 | */
300 | #define NGX_HTTP_WAF_MODE_STD (NGX_HTTP_WAF_MODE_INSPECT_IP \
301 | | NGX_HTTP_WAF_MODE_INSPECT_URL \
302 | | NGX_HTTP_WAF_MODE_INSPECT_RB \
303 | | NGX_HTTP_WAF_MODE_INSPECT_ARGS \
304 | | NGX_HTTP_WAF_MODE_INSPECT_UA \
305 | | NGX_HTTP_WAF_MODE_CMN_METH \
306 | | NGX_HTTP_WAF_MODE_INSPECT_CC \
307 | | NGX_HTTP_WAF_MODE_EXTRA_CACHE \
308 | | NGX_HTTP_WAF_MODE_LIB_INJECTION_SQLI)
309 | /**
310 | * @def MODE_STATIC
311 | * @brief 适用于静态站点的工作模式
312 | */
313 | #define NGX_HTTP_WAF_MODE_STATIC (NGX_HTTP_WAF_MODE_INSPECT_IP \
314 | | NGX_HTTP_WAF_MODE_INSPECT_URL \
315 | | NGX_HTTP_WAF_MODE_INSPECT_UA \
316 | | NGX_HTTP_WAF_MODE_INSPECT_GET \
317 | | NGX_HTTP_WAF_MODE_INSPECT_HEAD \
318 | | NGX_HTTP_WAF_MODE_INSPECT_CC \
319 | | NGX_HTTP_WAF_MODE_EXTRA_CACHE)
320 |
321 | /**
322 | * @def MODE_DYNAMIC
323 | * @brief 适用于动态站点的工作模式
324 | */
325 | #define NGX_HTTP_WAF_MODE_DYNAMIC (NGX_HTTP_WAF_MODE_INSPECT_IP \
326 | | NGX_HTTP_WAF_MODE_INSPECT_URL \
327 | | NGX_HTTP_WAF_MODE_INSPECT_RB \
328 | | NGX_HTTP_WAF_MODE_INSPECT_ARGS \
329 | | NGX_HTTP_WAF_MODE_INSPECT_UA \
330 | | NGX_HTTP_WAF_MODE_INSPECT_COOKIE \
331 | | NGX_HTTP_WAF_MODE_CMN_METH \
332 | | NGX_HTTP_WAF_MODE_INSPECT_CC \
333 | | NGX_HTTP_WAF_MODE_EXTRA_CACHE \
334 | | NGX_HTTP_WAF_MODE_LIB_INJECTION_SQLI \
335 | | NGX_HTTP_WAF_MODE_INSPECT_ADV)
336 |
337 |
338 | /**
339 | * @def MODE_FULL
340 | * @brief 启用所有的模式
341 | */
342 | #define NGX_HTTP_WAF_MODE_FULL (NGX_HTTP_WAF_MODE_INSPECT_IP \
343 | | NGX_HTTP_WAF_MODE_INSPECT_URL \
344 | | NGX_HTTP_WAF_MODE_INSPECT_RB \
345 | | NGX_HTTP_WAF_MODE_INSPECT_ARGS \
346 | | NGX_HTTP_WAF_MODE_INSPECT_UA \
347 | | NGX_HTTP_WAF_MODE_INSPECT_COOKIE \
348 | | NGX_HTTP_WAF_MODE_INSPECT_REFERER \
349 | | NGX_HTTP_WAF_MODE_ALL_METH \
350 | | NGX_HTTP_WAF_MODE_INSPECT_CC \
351 | | NGX_HTTP_WAF_MODE_EXTRA_CACHE \
352 | | NGX_HTTP_WAF_MODE_INSPECT_ADV \
353 | | NGX_HTTP_WAF_MODE_LIB_INJECTION)
354 |
355 |
356 | /* 检查对应文件是否存在,如果存在则根据 mode 的值将数据处理后存入容器中 */
357 | /**
358 | * @def ngx_http_waf_check_and_load_conf(cf, folder, end, filename, container, mode)
359 | * @brief 检查对应文件是否存在,如果存在则根据 mode 的值将数据处理后存入数组中。
360 | * @param[in] folder 配置文件所在文件夹的绝对路径。
361 | * @param[in] end folder 字符数组的 '\0' 的地址。
362 | * @param[in] filename 配置文件名。
363 | * @param[out] container 存储配置读取结果的容器。
364 | * @param[in] mode 配置读取模式。
365 | * @warning 当文件不存在的时候会直接执行 @code return NGX_CONF_ERROR; @endcode 语句。
366 | */
367 | #define ngx_http_waf_check_and_load_conf(cf, folder, end, filename, container, mode) { \
368 | strcat((folder), (filename)); \
369 | if (access((folder), R_OK) != 0) { \
370 | ngx_conf_log_error(NGX_LOG_ERR, (cf), 0, "ngx_waf: %s: %s", (folder), "No such file or directory"); \
371 | return NGX_HTTP_WAF_FAIL; \
372 | } \
373 | if (load_into_container((cf), (folder), (container), (mode)) == NGX_HTTP_WAF_FAIL) { \
374 | ngx_conf_log_error(NGX_LOG_ERR, (cf), 0, "ngx_waf: %s: %s", (folder), "Cannot read configuration."); \
375 | return NGX_HTTP_WAF_FAIL; \
376 | } \
377 | *(end) = '\0'; \
378 | }
379 |
380 | /**
381 | * @def ngx_http_waf_check_flag(origin, flag)
382 | * @brief 检查 flag 是否存在于 origin 中,即位操作。
383 | * @return 存在则返回 NGX_HTTP_WAF_TRUE,反之返回 NGX_HTTP_WAF_FALSE。
384 | * @retval NGX_HTTP_WAF_TRUE 存在。
385 | * @retval NGX_HTTP_WAF_FALSE 不存在。
386 | */
387 | #define ngx_http_waf_check_flag(origin, flag) (((origin) & (flag)) == (flag) ? NGX_HTTP_WAF_TRUE : NGX_HTTP_WAF_FALSE)
388 |
389 |
390 | /**
391 | * @def ngx_http_waf_check_bit(origin, bit_index)
392 | * @brief 检查 origin 的某一位是否为 1。
393 | * @return 如果为一则返回 NGX_HTTP_WAF_TRUE,反之返回 NGX_HTTP_WAF_FALSE。
394 | * @retval NGX_HTTP_WAF_TRUE 被测试的位为一。
395 | * @retval NGX_HTTP_WAF_FALSE 被测试的位为零。
396 | * @note bit_index 从 0 开始计数,其中 0 代表最低位。
397 | */
398 | #define ngx_http_waf_check_bit(origin, bit_index) (ngx_http_waf_check_flag((origin), 1 << (bit_index)))
399 |
400 |
401 | #define ngx_http_waf_make_utarray_ngx_str_icd() { sizeof(ngx_str_t), NULL, ngx_http_waf_utarray_ngx_str_ctor, ngx_http_waf_utarray_ngx_str_dtor }
402 |
403 | #define ngx_http_waf_make_utarray_vm_code_icd() { sizeof(vm_code_t), NULL, ngx_http_waf_utarray_vm_code_ctor, ngx_http_waf_utarray_vm_code_dtor }
404 |
405 | #define ngx_strdup(s) ((u_char*)strdup((char*)(s)));
406 |
407 | #define ngx_strcpy(d, s) (strcpy((char*)d, (const char*)s))
408 |
409 |
410 | #endif // !NGX_HTTP_WAF_MODULE_MACRO_H
411 |
--------------------------------------------------------------------------------
/inc/ngx_http_waf_module_mem_pool.h:
--------------------------------------------------------------------------------
1 | /**
2 | * @file ngx_http_waf_module_memory_pool.h
3 | * @brief 内存池
4 | */
5 |
6 | #include
7 | #include
8 |
9 |
10 | #ifndef __NGX_HTTP_WAF_MODULE_MEMORY_POOL_H__
11 | #define __NGX_HTTP_WAF_MODULE_MEMORY_POOL_H__
12 |
13 |
14 | /**
15 | * @brief 初始化一个内存池
16 | * @param[out] pool 要初始化的内存池
17 | * @param[in] type 内存池类型
18 | * @param[in] native_pool 内存池
19 | * @return 如果成功返回 NGX_HTTP_WAF_SUCCESS,反之则不是。
20 | */
21 | ngx_int_t mem_pool_init(mem_pool_t* pool, mem_pool_type_e type, void* native_pool);
22 |
23 | /**
24 | * @brief 申请一段连续的内存
25 | * @param[in] pool 要操作的内存池
26 | * @param[in] byte_size 内存的字节数
27 | * @return 成功则返回内存首地址,反之为 NULL。
28 | */
29 | void* mem_pool_calloc(mem_pool_t* pool, ngx_uint_t byte_size);
30 |
31 | /**
32 | * @brief 释放一段连续的内存
33 | * @param[in] pool 要操作的内存池
34 | * @param[in] buffer 内存的首地址
35 | * @return 成功则返回 NGX_HTTP_WAF_SUCCESS,反之则不是。
36 | */
37 | ngx_int_t mem_pool_free(mem_pool_t* pool, void* buffer);
38 |
39 | #endif
--------------------------------------------------------------------------------
/inc/ngx_http_waf_module_type.h:
--------------------------------------------------------------------------------
1 | /**
2 | * @file ngx_http_waf_module_type.h
3 | * @brief 相关结构体的定义
4 | */
5 |
6 | #include
7 | #include
8 | #include
9 | // #include
10 | // #include
11 | #include
12 | #include
13 | #include
14 | #include
15 |
16 |
17 | #ifndef NGX_HTTP_WAF_MODULE_TYPE_H
18 | #define NGX_HTTP_WAF_MODULE_TYPE_H
19 |
20 | /**
21 | * @typedef ngx_http_waf_check
22 | * @brief 请求检查函数的函数指针
23 | * @param[out] out_http_status 当触发规则时需要返回的 HTTP 状态码。
24 | */
25 | typedef ngx_int_t (*ngx_http_waf_check_pt)(ngx_http_request_t* r, ngx_int_t* out_http_status);
26 |
27 |
28 | /**
29 | * @struct inx_addr_t
30 | * @brief 代表 ipv4 或 ipv6 地址。
31 | */
32 | typedef union inx_addr_u {
33 | struct in_addr ipv4;
34 | #if (NGX_HAVE_INET6)
35 | struct in6_addr ipv6;
36 | #endif
37 | } inx_addr_t;
38 |
39 |
40 | /**
41 | * @struct singly_linked_list_t
42 | * @brief 单链表
43 | */
44 | typedef struct singly_linked_list_s {
45 | void *data; /**< 链表的数据项 */
46 | size_t data_byte_length; /**< data 指针指向的内存的长度(字节) */
47 | struct singly_linked_list_s *next; /**< utlist 关键成员 */
48 | } singly_linked_list_t;
49 |
50 |
51 | /**
52 | * @struct circular_doublly_linked_list_t
53 | * @brief 双向循环链表
54 | */
55 | typedef struct circular_doublly_linked_list_s {
56 | void *data; /**< 链表的数据项 */
57 | size_t data_byte_length; /**< data 指针指向的内存的长度(字节) */
58 | struct circular_doublly_linked_list_s *prev; /**< utlist 关键成员 */
59 | struct circular_doublly_linked_list_s *next; /**< utlist 关键成员 */
60 | } circular_doublly_linked_list_t;
61 |
62 |
63 | /**
64 | * @struct ip_statis_t
65 | * @brief 用于记录 CC 防护信息
66 | */
67 | typedef struct ip_statis_s {
68 | ngx_int_t count; /**< 访问次数 */
69 | ngx_int_t is_blocked; /**< 是否已经被拦截 */
70 | time_t record_time; /**< 何时开始记录 */
71 | time_t block_time; /**< 何时开始拦截 */
72 | } ip_statis_t;
73 |
74 |
75 | /**
76 | * @struct check_result_t
77 | * @brief 规则减价结果
78 | */
79 | typedef struct check_result_s {
80 | ngx_int_t is_matched; /**< 是否被某条规则匹配到 */
81 | u_char *detail; /**< 匹配到的规则的详情 */
82 | } check_result_t;
83 |
84 |
85 | /**
86 | * @enum memory_pool_type_e
87 | * @brief 内存池类型
88 | */
89 | typedef enum {
90 | std, /**< malloc */
91 | gernal_pool, /**< ngx_pool_t */
92 | slab_pool /**< ngx_slab_pool_t */
93 | } mem_pool_type_e;
94 |
95 |
96 | /**
97 | * @enum vm_code_type_e
98 | * @brief 虚拟机指令类型
99 | */
100 | typedef enum {
101 | VM_CODE_NOP, /**< 空指令,什么都不做,继续执行下一条指令。 */
102 | VM_CODE_PUSH_INT, /**< 将一个整数压入栈中。 */
103 | VM_CODE_PUSH_STR, /**< 将一个字符串压入栈中。 */
104 | VM_CODE_PUSH_CLIENT_IP, /**< 将客户端 IP(struct in_addr 或 struct_in6addr)压入栈中。 */
105 | VM_CODE_PUSH_URL, /**< 将 URL 压入栈中。 */
106 | VM_CODE_PUSH_QUERY_STRING, /**< 将查询字符串的 key 对应的 value 压入栈中。 */
107 | VM_CODE_PUSH_REFERER, /**< 将 referer 压入栈中。 */
108 | VM_CODE_PUSH_USER_AGENT, /**< 将 user-agent 压入栈中。 */
109 | VM_CODE_PUSH_HEADER_IN, /**< 将请求头中的 key 对应的 value 压入栈中。 */
110 | VM_CODE_PUSH_COOKIE, /**< 将 cookie 中的 key 对应的 value 压入栈中。 */
111 | // VM_CODE_POP, /**< */
112 | // VM_CODE_TOP, /**< */
113 | VM_CODE_OP_NOT, /**< 将栈顶的布尔值反转 */
114 | VM_CODE_OP_AND, /**< 弹出两个布尔值,逻辑与后压入栈中。 */
115 | VM_CODE_OP_OR, /**< 弹出两个布尔值,逻辑或后压入栈中 */
116 | VM_CODE_OP_CONTAINS, /**< 弹出两个字符串,判断第一个弹出的字符串是否是第二个弹出的字符串的子串,并将结果压入栈中 */
117 | VM_CODE_OP_MATCHES, /**< 弹出两个字符串,将第一个字符串编译为正则表达式对第二个字符串进行正则匹配,并将结果压入栈中。 */
118 | VM_CODE_OP_EQUALS, /**< 弹出两个字符串判断两个字符串是否相等,并将结果压入栈中。 */
119 | VM_CODE_OP_BELONG_TO, /**< 依次弹出一个 IP 块和一个 IP,判断 IP 是否包含在 IP 块中,并将结果压入栈中。 */
120 | VM_CODE_OP_SQLI_DETN, /**< 弹出一个字符串检测其中是否存在 SQL 注入,并将结果压入栈中。 */
121 | VM_CODE_OP_XSS_DETN, /**< 弹出一个字符串检测其中是否存在 XSS 攻击,并将结果压入栈中。 */
122 | VM_CODE_ACT_RETURN, /**< 如果栈顶的布尔值为真则返回指定的 http 状态码。 */
123 | VM_CODE_ACT_ALLOW /**< 如果栈顶的布尔值为真则放行本次请求。 */
124 | } vm_code_type_e;
125 |
126 |
127 | /**
128 | * @enum vm_data_type_e
129 | * @brief 虚拟机数据类型
130 | */
131 | typedef enum {
132 | VM_DATA_VOID, /**< 无类型数据,应该被忽略。 */
133 | VM_DATA_STR, /**< 字符串类型 */
134 | VM_DATA_INT, /**< 整数类型 */
135 | VM_DATA_BOOL, /**< 布尔类型 */
136 | VM_DATA_IPV4, /**< IPV4 */
137 | #if (NGX_HAVE_INET6)
138 | VM_DATA_IPV6 /**< IPV6 */
139 | #endif
140 | } vm_data_type_e;
141 |
142 |
143 | /**
144 | * @struct key_value_t
145 | * @brief 哈希表(字符串 -> 字符串)
146 | */
147 | typedef struct key_value_s {
148 | ngx_str_t key; /**< 键 */
149 | ngx_str_t value; /**< 值 */
150 | UT_hash_handle hh; /**< uthash 关键成员 */
151 | } key_value_t;
152 |
153 |
154 | /**
155 | * @struct mem_pool_t
156 | * @brief 包含常规内存池或 slab 内存池
157 | */
158 | typedef struct memo_pool_s {
159 | mem_pool_type_e type; /**< 标识内存池的类型 */
160 | size_t used_mem; /**< 正在使用的内存大小(字节) */
161 | union {
162 | ngx_pool_t *gernal_pool; /**< 常规内存池 */
163 | ngx_slab_pool_t *slab_pool; /**< slab 内存池 */
164 | } native_pool; /**< 内存池 */
165 | } mem_pool_t;
166 |
167 |
168 | /**
169 | * @struct lru_cache_result_t
170 | * @brief LRU 操作结果
171 | */
172 | typedef struct lru_cache_result_s {
173 | int status;
174 | void **data;
175 | } lru_cache_result_t;
176 |
177 |
178 | typedef lru_cache_result_t lru_cache_add_result_t;
179 |
180 | typedef lru_cache_result_t lru_cache_find_result_t;
181 |
182 |
183 | /**
184 | * @struct lru_cache_item_t
185 | * @brief LRU 缓存项
186 | */
187 | typedef struct lru_cache_item_s {
188 | u_char *key_ptr; /**< 用于哈希的关键字 */
189 | size_t key_byte_length; /**< 关键字占用的字节数 */
190 | void *data; /**< 缓存项的具体数据 */
191 | struct lru_cache_item_s *prev; /**< utlist 关键成员 */
192 | struct lru_cache_item_s *next; /**< utlist 关键成员 */
193 | UT_hash_handle hh; /**< uthash 关键成员 */
194 | } lru_cache_item_t;
195 |
196 |
197 | /**
198 | * @struct lru_cache_t
199 | * @brief LRU 缓存管理器
200 | */
201 | typedef struct lru_cache_s {
202 | time_t last_eliminate; /**< 最后一次批量淘汰缓存的时间 */
203 | mem_pool_t pool; /**< 内存池 */
204 | size_t capacity; /**< 最多嫩容纳多少个缓存项 */
205 | lru_cache_item_t *hash_head; /**< uthash 的表头 */
206 | lru_cache_item_t *chain_head; /**< utlist 的表头 */
207 | } lru_cache_t;
208 |
209 |
210 | /**
211 | * @struct token_bucket_t
212 | * @brief 令牌桶
213 | */
214 | typedef struct token_bucket_s{
215 | inx_addr_t inx_addr; /**< 作为哈希表中的 key */
216 | ngx_uint_t count; /**< 令牌剩余量 */
217 | ngx_int_t is_ban; /**< 令牌桶是否暂时被禁止 */
218 | time_t last_ban_time; /**< 最后一次开始禁止令牌桶的时间 */
219 | UT_hash_handle hh; /**< uthash 关键成员 */
220 | } token_bucket_t;
221 |
222 |
223 | /**
224 | * @struct token_bucket_set_t
225 | * @brief 令牌桶集合
226 | */
227 | typedef struct token_bucket_set_s{
228 | mem_pool_t pool; /**< 使用的内存池 */
229 | ngx_uint_t ban_duration; /**< 当令牌桶为空时自动禁止该桶一段时间(分钟)*/
230 | time_t last_put; /**< 上次集中添加令牌的时间 */
231 | time_t last_clear; /**< 上次清空令牌桶的时间 */
232 | ngx_uint_t init_count; /**< 令牌桶内初始的令牌数量 */
233 | ngx_uint_t bucket_count; /**< 已经有多少个令牌桶 */
234 | token_bucket_t *head; /**< 哈希表标头 */
235 | } token_bucket_set_t;
236 |
237 |
238 | /**
239 | * @struct ip_trie_node_t
240 | * @brief 前缀树节点。
241 | */
242 | typedef struct ip_trie_node_s {
243 | int is_ip; /**< 如果为 TRUE 则代表此节点也代表一个 IP,反之则为 FALSE */
244 | struct ip_trie_node_s *left; /**< 左子树代表当前位为零 */
245 | struct ip_trie_node_s *right; /**< 右子树代表当前位为一 */
246 | void *data;
247 | size_t data_byte_length;
248 | } ip_trie_node_t;
249 |
250 |
251 | /**
252 | * @struct ip_trie_t
253 | * @brief 前缀树。
254 | */
255 | typedef struct ip_trie_s {
256 | int ip_type; /**< 存储的 IP 地址的类型。 */
257 | ip_trie_node_t *root; /**< 前缀树树根。 */
258 | int match_all; /**< 当遇到前缀长度为零(0.0.0.0/0)的地址时为真,代表所有查询均返回真。 */
259 | size_t size; /**< 已经存储的 IP 数量。 */
260 | mem_pool_t pool; /**< 使用的内存池 */
261 | } ip_trie_t;
262 |
263 |
264 | /**
265 | * @struct ngx_http_waf_ctx_t
266 | * @brief 每个请求的上下文
267 | */
268 | typedef struct ngx_http_waf_ctx_s {
269 | ngx_int_t checked; /**< 是否启动了检测流程 */
270 | ngx_int_t blocked; /**< 是否拦截了本次请求 */
271 | double spend; /**< 本次检查花费的时间(毫秒) */
272 | u_char rule_type[128]; /**< 触发的规则类型 */
273 | u_char rule_deatils[NGX_HTTP_WAF_RULE_MAX_LEN]; /**< 触发的规则内容 */
274 | ngx_int_t read_body_done;
275 | ngx_int_t waiting_more_body; /**< 是否等待读取更多请求体 */
276 | ngx_int_t has_req_body; /**< 字段 req_body 是否以己经存储了请求体 */
277 | ngx_buf_t req_body; /**< 请求体 */
278 | } ngx_http_waf_ctx_t;
279 |
280 |
281 | /**
282 | * @struct ngx_http_waf_loc_conf_t
283 | */
284 | typedef struct ngx_http_waf_main_conf_s {
285 | ngx_array_t *local_caches; /**< 已经启用的所有的缓存管理器数组 */
286 | } ngx_http_waf_main_conf_t;
287 |
288 |
289 | /**
290 | * @struct ngx_http_waf_loc_conf_t
291 | * @brief 每个 server 块的配置块
292 | */
293 | typedef struct ngx_http_waf_loc_conf_s {
294 | struct ngx_http_waf_loc_conf_s *parent; /**< 上层配置,用来定位 CC 防护所使用的共享内存 */
295 | u_char random_str[129]; /**< 随机字符串 */
296 | ngx_str_t waf_under_attack_uri; /**< 五秒盾的 URI */
297 | ngx_int_t waf_under_attack; /**< 是否启用五秒盾 */
298 | ngx_int_t is_alloc; /**< 是否已经分配的存储规则的容器的内存 */
299 | ngx_int_t waf; /**< 是否启用本模块 */
300 | ngx_str_t waf_rule_path; /**< 配置文件所在目录 */
301 | uint_fast64_t waf_mode; /**< 检测模式 */
302 | ngx_int_t waf_cc_deny_limit; /**< CC 防御的限制频率 */
303 | ngx_int_t waf_cc_deny_duration; /**< CC 防御的拉黑时长(秒) */
304 | ngx_int_t waf_cc_deny_shm_zone_size; /**< CC 防御所使用的共享内存的大小(字节) */
305 | ngx_int_t waf_inspection_capacity; /**< 用于缓存检查结果的共享内存的大小(字节) */
306 | ngx_int_t waf_http_status; /**< 常规检测项目拦截后返回的状态码 */
307 | ngx_int_t waf_http_status_cc; /**< CC 防护出发后返回的状态码 */
308 | ip_trie_t *black_ipv4; /**< IPV4 黑名单 */
309 | #if (NGX_HAVE_INET6)
310 | ip_trie_t *black_ipv6; /**< IPV6 黑名单 */
311 | #endif
312 | ngx_array_t *black_url; /**< URL 黑名单 */
313 | ngx_array_t *black_args; /**< args 黑名单 */
314 | ngx_array_t *black_ua; /**< user-agent 黑名单 */
315 | ngx_array_t *black_referer; /**< Referer 黑名单 */
316 | ngx_array_t *black_cookie; /**< Cookie 黑名单 */
317 | ngx_array_t *black_post; /**< 请求体内容黑名单 */
318 | ip_trie_t *white_ipv4; /**< IPV4 白名单 */
319 | #if (NGX_HAVE_INET6)
320 | ip_trie_t *white_ipv6; /**< IPV6 白名单 */
321 | #endif
322 | ngx_array_t *white_url; /**< URL 白名单 */
323 | ngx_array_t *white_referer; /**< Referer 白名单 */
324 | UT_array *advanced_rule; /**< 高级规则表 */
325 | ngx_shm_zone_t *shm_zone_cc_deny; /**< 共享内存 */
326 | lru_cache_t *ip_access_statistics; /**< IP 访问频率统计表 */
327 | lru_cache_t *black_url_inspection_cache; /**< URL 黑名单检查缓存 */
328 | lru_cache_t *black_args_inspection_cache; /**< ARGS 黑名单检查缓存 */
329 | lru_cache_t *black_ua_inspection_cache; /**< User-Agent 黑名单检查缓存 */
330 | lru_cache_t *black_referer_inspection_cache; /**< Referer 黑名单检查缓存 */
331 | lru_cache_t *black_cookie_inspection_cache; /**< Cookie 黑名单检查缓存 */
332 | lru_cache_t *white_url_inspection_cache; /**< URL 白名单检查缓存 */
333 | lru_cache_t *white_referer_inspection_cache; /**< Referer 白名单检查缓存 */
334 | ngx_int_t is_custom_priority; /**< 用户是否自定义了优先级 */
335 | ngx_http_waf_check_pt check_proc[20]; /**< 各种检测流程的启动函数 */
336 | } ngx_http_waf_loc_conf_t;
337 |
338 |
339 | /**
340 | * @struct ipv4_t
341 | * @brief 格式化后的 IPV4
342 | * @note 注意,无论是 prefix 还是 suffix 都是网络字节序,即大端字节序。
343 | */
344 | typedef struct ipv4_s {
345 | u_char text[32]; /**< 点分十进制表示法 */
346 | uint32_t prefix; /**< 相当于 192.168.1.0/24 中的 192.168.1.0 的整数形式 */
347 | uint32_t suffix; /**< 相当于 192.168.1.0/24 中的 24 的位表示(网络字节序) */
348 | uint32_t suffix_num; /**< 相当于 192.168.1.0/24 中的 24 */
349 | } ipv4_t;
350 |
351 |
352 | /**
353 | * @struct ipv6_t
354 | * @brief 格式化后的 IPV6
355 | * @note 注意,无论是 prefix[16] 还是 suffix[16],他们中的每一项都是网络字节序。
356 | * 数组的下标同理,下标零代表最高位,下标十五代表最低位。
357 | */
358 | #if (NGX_HAVE_INET6)
359 | typedef struct ipv6_s {
360 | u_char text[64]; /**< 冒号十六进制表示法 */
361 | uint8_t prefix[16]; /**< 相当于 ffff::ffff/64 中的 ffff::ffff 的整数形式 */
362 | uint8_t suffix[16]; /**< 相当于 ffff::ffff/64 中的 64 的位表示(网络字节序) */
363 | uint32_t suffix_num; /**< 相当于 ffff::ffff/64 中的 64 */
364 | } ipv6_t;
365 | #endif
366 |
367 |
368 |
369 | /**
370 | * @struct vm_stack_arg_s
371 | * @brief 虚拟机指令参数
372 | */
373 | typedef struct vm_stack_arg_s {
374 | vm_data_type_e type[4]; /**< 每个参数的类型 */
375 | size_t argc; /**< 参数的数量 */
376 | union {
377 | int int_val;
378 | ngx_str_t str_val;
379 | uint8_t bool_val;
380 | ipv4_t ipv4_val;
381 | #if (NGX_HAVE_INET6)
382 | ipv6_t ipv6_val;
383 | #endif
384 | inx_addr_t inx_addr_val;
385 | } value[4]; /**< 每个参数的值 */
386 | struct vm_stack_arg_s *utstack_handle; /**< utstack 关键成员 */
387 | } vm_stack_arg_t;
388 |
389 |
390 |
391 | /**
392 | * @struct vm_code_t
393 | * @brief 虚拟机指令
394 | */
395 | typedef struct vm_code_s {
396 | vm_code_type_e type; /**< 指令类型 */
397 | struct vm_stack_arg_s argv; /**< 指令参数 */
398 | } vm_code_t;
399 |
400 | #endif // !NGX_HTTP_WAF_MODULE_TYPE_H
--------------------------------------------------------------------------------
/inc/ngx_http_waf_module_under_attack.h:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 |
5 | #ifndef __NGX_HTTP_WAF_MODULE_UNDER_ATTACK_H__
6 | #define __NGX_HTTP_WAF_MODULE_UNDER_ATTACK_H__
7 |
8 |
9 | extern ngx_module_t ngx_http_waf_module; /**< 模块详情 */
10 |
11 | /**
12 | * @brief 进行五秒盾检测
13 | */
14 | ngx_int_t ngx_http_waf_check_under_attack(ngx_http_request_t* r, ngx_int_t* out_http_status);
15 |
16 |
17 | /**
18 | * @brief 生成用于验证五秒盾的三个 Cookie
19 | */
20 | ngx_int_t ngx_http_waf_gen_cookie(ngx_http_request_t *r);
21 |
22 |
23 | /**
24 | * @brief 生成 Cookie 完整性校验码
25 | * @param[in] uid 对应 Cookie __waf_under_attack_uid
26 | * @param[in] uid_len 不包括结尾的 \0
27 | * @param[out] dst 对应 Cookie __waf_under_attack_verification,生成的校验码将保存到此处。
28 | * @param[in] dst_len 不包括结尾的 \0
29 | * @param[in] now 对应 Cookie __waf_under_attack_time
30 | * @param[in] now_len 不包括结尾的 \0
31 | */
32 | ngx_int_t ngx_http_waf_gen_verification(ngx_http_request_t *r,
33 | u_char* uid,
34 | size_t uid_len,
35 | u_char* dst,
36 | size_t dst_len,
37 | u_char* now,
38 | size_t now_len);
39 |
40 |
41 | void ngx_http_waf_gen_ctx_and_header_location(ngx_http_request_t *r);
42 |
43 |
44 | #endif
--------------------------------------------------------------------------------
/inc/ngx_http_waf_module_util.h:
--------------------------------------------------------------------------------
1 | /**
2 | * @file ngx_http_waf_module_util.h
3 | * @brief IPV4 字符串解析,nginx 风格转化为 C 风格字符串。
4 | */
5 |
6 |
7 | #include
8 | #include
9 | #include
10 | #include
11 |
12 | #ifndef NGX_HTTP_WAF_MODULE_UTIL_H
13 | #define NGX_HTTP_WAF_MODULE_UTIL_H
14 |
15 |
16 | /**
17 | * @defgroup util 工具代码
18 | * @addtogroup util 工具代码
19 | * @brief IPV4 字符串解析,nginx 风格转化为 C 风格字符串。
20 | * @{
21 | */
22 |
23 | /**
24 | * @brief 将一个字符串形式的 IPV4 地址转化为 ipv4_t。
25 | * @param[in] text 要转换的字符串
26 | * @param[out] ipv4 转换完成后的格式化的 ipv4
27 | * @return 成功返回 SUCCESS,失败返回 FAIL。
28 | * @retval SUCCESS 转换成功
29 | * @retval FAIL 转化错误
30 | */
31 | ngx_int_t ngx_http_waf_parse_ipv4(ngx_str_t text, ipv4_t* ipv4);
32 |
33 |
34 | /**
35 | * @brief 将一个字符串形式的 IPV6 地址转化为 ipv6_t。
36 | * @param[in] text 要转换的字符串
37 | * @param[out] ipv6 转换完成后的格式化的 ipv6
38 | * @return 成功返回 SUCCESS,失败返回 FAIL。
39 | * @retval SUCCESS 转换成功
40 | * @retval FAIL 转化错误
41 | */
42 | #if (NGX_HAVE_INET6)
43 | ngx_int_t ngx_http_waf_parse_ipv6(ngx_str_t text, ipv6_t* ipv6);
44 | #endif
45 |
46 |
47 | /**
48 | * @brief 将一个形如 10s 10m 10h 10d 这样的字符串转化为整数,单位是秒。
49 | * @param[in] str 要解析的字符串
50 | * @return 失败返回 NGX_ERROR,反之则不是。
51 | */
52 | ngx_int_t ngx_http_waf_parse_time(u_char* str);
53 |
54 |
55 | /**
56 | * @brief 将一个形如 10k 10m 10g 这样的字符串转化为整数,单位是字节。
57 | * @param[in] str 要解析的字符串
58 | * @return 失败返回 NGX_ERROR,反之则不是。
59 | */
60 | ngx_int_t ngx_http_waf_parse_size(u_char* str);
61 |
62 |
63 | /**
64 | * @brief 将一个 Cookie 字符串分割为一个一个的键值对。
65 | * @param[in] cookies 字符串形式的 Cookie
66 | * @param[out] array 保存解析结果的数组
67 | * @return 成功则返回 SUCCESS,反之则不是。
68 | * @note 数组内容格式为 [key, value, key, value, ......]
69 | * @warning 使用完毕后请自行释放数组所占用内存。
70 | */
71 | ngx_int_t ngx_http_waf_parse_cookie(ngx_str_t* native_cookie, UT_array** array);
72 |
73 |
74 | /**
75 | * @brief 将一个 Query String 字符串解析为哈希表
76 | * @param[in] native_query_string 字符串形式的 Cookie
77 | * @param[out] hash_head 保存解析结果的哈希表
78 | * @return 成功则返回 SUCCESS,反之则不是。
79 | * @warning 使用完毕后请自行释放数组所占用内存。
80 | */
81 | ngx_int_t ngx_http_waf_parse_query_string(ngx_str_t* native_query_string, key_value_t** hash_head);
82 |
83 |
84 | /**
85 | * @brief 将一个 Header 列表解析为哈希表
86 | * @param[in] native_header Header 列表
87 | * @param[out] hash_head 保存解析结果的哈希表
88 | * @return 成功则返回 SUCCESS,反之则不是。
89 | * @warning 使用完毕后请自行释放数组所占用内存。
90 | */
91 | ngx_int_t ngx_http_waf_parse_header(ngx_list_t* native_header, key_value_t** hash_head);
92 |
93 |
94 | /**
95 | * @brief 字符串分割
96 | * @param[in] str 要分割的字符串
97 | * @param[in] sep 分隔符
98 | * @param[out] max_len 分割后单个字符串的最大长度
99 | * @param[out] array 存放分割结果的数组
100 | * @return 成功则返回 SUCCESS,反之则不是。
101 | * @warning 使用完毕后请自行释放数组所占用内存。
102 | */
103 | ngx_int_t ngx_http_waf_str_split(ngx_str_t* str, u_char sep, size_t max_len, UT_array** array);
104 |
105 |
106 | /**
107 | * @brief IPV4 网段比较
108 | * @param[in] ip 某个 IP
109 | * @param[in] ipv4 某个 IP 或者某个网段
110 | * @return 网段匹配则返回 MATCHED,反之则为 NOT_MATCHED。
111 | * @note 所有参数均为网络字节序
112 | */
113 | ngx_int_t ngx_http_waf_ipv4_netcmp(uint32_t ip, const ipv4_t* ipv4);
114 |
115 |
116 | /**
117 | * @brief IPV4 网段比较
118 | * @param[in] ip 某个 IP
119 | * @param[in] ipv6 某个 IP 或者某个网段
120 | * @return 网段匹配则返回 MATCHED,反之则为 NOT_MATCHED。
121 | * @note 所有参数均为网络字节序
122 | */
123 | #if (NGX_HAVE_INET6)
124 | ngx_int_t ngx_http_waf_ipv6_netcmp(uint8_t ip[16], const ipv6_t* ipv6);
125 | #endif
126 |
127 |
128 | /**
129 | * @brief 字符串分割
130 | * @param[in] str 要分割的字符串
131 | * @param[in] sep 分隔符
132 | * @param[out] max_len 分割后单个字符串的最大长度
133 | * @param[out] array 存放分割结果的数组
134 | * @return 成功则返回 SUCCESS,反之则不是。
135 | * @warning 使用完毕后请自行释放数组所占用内存。
136 | */
137 | // ngx_int_t str_split(u_char* str, u_char sep, size_t max_len, UT_array** array);
138 |
139 |
140 | /**
141 | * @brief 将 ngx_str 转化为 C 风格的字符串
142 | * @param[out] destination 存储 C 风格字符串的字符数组
143 | * @param[in] ngx_str 要转换的 nginx 风格的字符串
144 | * @return 转换成功则返回 C 风格字符串的结尾的 '\0' 的地址,反之返回 NULL。
145 | * @retval !NULL C 风格字符串的结尾的 '\0' 的地址
146 | * @retval NULL 转换失败
147 | */
148 | char* ngx_http_waf_to_c_str(u_char* destination, ngx_str_t ngx_str);
149 |
150 |
151 | /**
152 | * @brief 生成一个 C 风格的随机字符串
153 | * @param[out] dest 存储 C 风格字符串的字符数组
154 | * @param[in] len 要生成的字符串的长度,不包含结尾的 \0 。
155 | * @return 成功返回 NGX_HTTP_WAF_SUCCESS,反之则不是。
156 | */
157 | ngx_int_t ngx_http_waf_rand_str(u_char* dest, size_t len);
158 |
159 |
160 | /**
161 | * @brief 计算 SHA256 并返回 16 进制字符串。
162 | * @param[out] dst 存储 SHA256 字符串的缓冲区
163 | * @param[in] dst_len 不包含结尾的 \0
164 | * @param[in] buf 用来计算数据所在的缓冲区
165 | * @param[in] buf_len 缓冲区长度
166 | * @return 成功返回 NGX_HTTP_WAF_SUCCESS,反之则不是。
167 | */
168 | ngx_int_t ngx_http_waf_sha256(u_char* dst, size_t dst_len, const u_char* buf, size_t buf_len);
169 |
170 |
171 | void ngx_http_waf_utarray_ngx_str_ctor(void *dst, const void *src);
172 |
173 |
174 | void ngx_http_waf_utarray_ngx_str_dtor(void* elt);
175 |
176 |
177 | void ngx_http_waf_utarray_vm_code_ctor(void *dst, const void *src);
178 |
179 |
180 | void ngx_http_waf_utarray_vm_code_dtor(void* elt);
181 |
182 |
183 | /**
184 | * @}
185 | */
186 |
187 |
188 | #endif
--------------------------------------------------------------------------------
/inc/ngx_http_waf_module_vm.h:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include
9 |
10 | #ifndef __NGX_HTTP_WAF_MODULE_VM_H__
11 | #define __NGX_HTTP_WAF_MODULE_VM_H__
12 |
13 | void ngx_http_waf_print_code(UT_array* array);
14 |
15 |
16 | /**
17 | * @brief 执行高级规则
18 | * @param[out] out_http_status 要返回的 HTTP 状态码
19 | * @return 如果命中规则则返回 NGX_HTTP_WAF_MATCHED,反之则为 NGX_HTTP_WAF_NOT_MATCHED。
20 | */
21 | ngx_int_t ngx_http_waf_vm_exec(ngx_http_request_t* r, ngx_int_t* out_http_status);
22 |
23 | #endif
--------------------------------------------------------------------------------
/src/ngx_http_waf_module_core.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include
5 | #include
6 | #include
7 |
8 | static ngx_command_t ngx_http_waf_commands[] = {
9 | {
10 | ngx_string("waf"),
11 | NGX_HTTP_MAIN_CONF | NGX_HTTP_SRV_CONF | NGX_HTTP_LOC_CONF | NGX_CONF_FLAG,
12 | ngx_http_waf_conf,
13 | NGX_HTTP_LOC_CONF_OFFSET,
14 | offsetof(ngx_http_waf_loc_conf_t, waf),
15 | NULL
16 | },
17 | {
18 | ngx_string("waf_rule_path"),
19 | NGX_HTTP_MAIN_CONF | NGX_HTTP_SRV_CONF | NGX_HTTP_LOC_CONF | NGX_CONF_TAKE1,
20 | ngx_http_waf_rule_path_conf,
21 | NGX_HTTP_LOC_CONF_OFFSET,
22 | offsetof(ngx_http_waf_loc_conf_t, waf_rule_path),
23 | NULL
24 | },
25 | {
26 | ngx_string("waf_mode"),
27 | NGX_HTTP_MAIN_CONF | NGX_HTTP_SRV_CONF | NGX_HTTP_LOC_CONF | NGX_CONF_1MORE,
28 | ngx_http_waf_mode_conf,
29 | NGX_HTTP_LOC_CONF_OFFSET,
30 | 0,
31 | NULL
32 | },
33 | {
34 | ngx_string("waf_cc_deny"),
35 | NGX_HTTP_MAIN_CONF | NGX_HTTP_SRV_CONF | NGX_HTTP_LOC_CONF | NGX_CONF_TAKE123,
36 | ngx_http_waf_cc_deny_conf,
37 | NGX_HTTP_LOC_CONF_OFFSET,
38 | 0,
39 | NULL
40 | },
41 | {
42 | ngx_string("waf_cache"),
43 | NGX_HTTP_MAIN_CONF | NGX_HTTP_SRV_CONF | NGX_HTTP_LOC_CONF | NGX_CONF_TAKE123,
44 | ngx_http_waf_cache_conf,
45 | NGX_HTTP_LOC_CONF_OFFSET,
46 | 0,
47 | NULL
48 | },
49 | {
50 | ngx_string("waf_under_attack"),
51 | NGX_HTTP_MAIN_CONF | NGX_HTTP_SRV_CONF | NGX_HTTP_LOC_CONF | NGX_CONF_TAKE2,
52 | ngx_http_waf_under_attack_conf,
53 | NGX_HTTP_LOC_CONF_OFFSET,
54 | 0,
55 | NULL
56 | },
57 | {
58 | ngx_string("waf_priority"),
59 | NGX_HTTP_MAIN_CONF | NGX_HTTP_SRV_CONF | NGX_HTTP_LOC_CONF | NGX_CONF_TAKE1,
60 | ngx_http_waf_priority_conf,
61 | NGX_HTTP_LOC_CONF_OFFSET,
62 | 0,
63 | NULL
64 | },
65 | {
66 | ngx_string("waf_http_status"),
67 | NGX_HTTP_MAIN_CONF | NGX_HTTP_SRV_CONF | NGX_HTTP_LOC_CONF | NGX_CONF_TAKE12,
68 | ngx_http_waf_http_status_conf,
69 | NGX_HTTP_LOC_CONF_OFFSET,
70 | 0,
71 | NULL
72 | },
73 | ngx_null_command
74 | };
75 |
76 |
77 | static ngx_http_module_t ngx_http_waf_module_ctx = {
78 | NULL,
79 | ngx_http_waf_init_after_load_config,
80 | ngx_http_waf_create_main_conf,
81 | NULL,
82 | NULL,
83 | NULL,
84 | ngx_http_waf_create_loc_conf,
85 | ngx_http_waf_merge_loc_conf
86 | };
87 |
88 |
89 | ngx_module_t ngx_http_waf_module = {
90 | NGX_MODULE_V1,
91 | &ngx_http_waf_module_ctx, /* module context */
92 | ngx_http_waf_commands, /* module directives */
93 | NGX_HTTP_MODULE, /* module type */
94 | NULL, /* init master */
95 | NULL, /* init module */
96 | ngx_http_waf_init_process, /* init process */
97 | NULL, /* init thread */
98 | NULL, /* exit thread */
99 | NULL, /* exit process */
100 | NULL, /* exit master */
101 | NGX_MODULE_V1_PADDING
102 | };
103 |
104 |
105 | static ngx_int_t _read_request_body(ngx_http_request_t* r);
106 |
107 |
108 | static void _handler_read_request_body(ngx_http_request_t* r);
109 |
110 |
111 | ngx_int_t ngx_http_waf_init_process(ngx_cycle_t *cycle) {
112 | randombytes_stir();
113 | return NGX_OK;
114 | }
115 |
116 |
117 | ngx_int_t ngx_http_waf_handler_access_phase(ngx_http_request_t* r) {
118 | return ngx_http_waf_check_all(r, NGX_HTTP_WAF_TRUE);
119 | }
120 |
121 |
122 | ngx_int_t ngx_http_waf_check_all(ngx_http_request_t* r, ngx_int_t is_check_cc) {
123 | ngx_log_debug(NGX_LOG_DEBUG_CORE, r->connection->log, 0,
124 | "ngx_waf_debug: The scheduler has been started.");
125 |
126 | ngx_http_waf_ctx_t* ctx = NULL;
127 | ngx_http_waf_loc_conf_t* loc_conf = NULL;
128 | ngx_http_waf_get_ctx_and_conf(r, &loc_conf, &ctx);
129 | ngx_int_t is_matched = NGX_HTTP_WAF_NOT_MATCHED;
130 | ngx_int_t http_status = NGX_DECLINED;
131 |
132 | if (ctx == NULL) {
133 | ngx_log_debug(NGX_LOG_DEBUG_CORE, r->connection->log, 0,
134 | "ngx_waf_debug: Start allocating memory for storage contexts.");
135 | ngx_http_cleanup_t* cln = ngx_palloc(r->pool, sizeof(ngx_http_cleanup_t));
136 | ctx = ngx_palloc(r->pool, sizeof(ngx_http_waf_ctx_t));
137 | if (ctx == NULL || cln == NULL) {
138 | http_status = NGX_HTTP_INTERNAL_SERVER_ERROR;
139 | ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
140 | "ngx_waf: The request context could not be created because the memory allocation failed.");
141 | ngx_log_debug(NGX_LOG_DEBUG_CORE, r->connection->log, 0,
142 | "ngx_waf_debug: The scheduler shutdown abnormally.");
143 | return http_status;
144 | }
145 | else {
146 | cln->handler = ngx_http_waf_handler_cleanup;
147 | cln->data = ctx;
148 | cln->next = NULL;
149 |
150 | ctx->read_body_done = NGX_HTTP_WAF_FALSE;
151 | ctx->has_req_body = NGX_HTTP_WAF_FALSE;
152 | ctx->waiting_more_body = NGX_HTTP_WAF_FALSE;
153 | ctx->checked = NGX_HTTP_WAF_FALSE;
154 | ctx->blocked = NGX_HTTP_WAF_FALSE;
155 | ctx->spend = (double)clock() / CLOCKS_PER_SEC * 1000;
156 | ctx->rule_type[0] = '\0';
157 | ctx->rule_deatils[0] = '\0';
158 |
159 | if (r->cleanup == NULL) {
160 | r->cleanup = cln;
161 | } else {
162 | for (ngx_http_cleanup_t* i = r->cleanup; i != NULL; i = i->next) {
163 | if (i->next == NULL) {
164 | i->next = cln;
165 | break;
166 | }
167 | }
168 | }
169 |
170 | ngx_http_set_ctx(r, ctx, ngx_http_waf_module);
171 |
172 | ngx_log_debug(NGX_LOG_DEBUG_CORE, r->connection->log, 0,
173 | "ngx_waf_debug: Context initialization is complete.");
174 | }
175 | } else if (ngx_http_get_module_ctx(r, ngx_http_waf_module) == NULL) {
176 | ngx_http_set_ctx(r, ctx, ngx_http_waf_module);
177 | }
178 |
179 | if (ctx->waiting_more_body == NGX_HTTP_WAF_TRUE) {
180 | return NGX_DONE;
181 | }
182 |
183 | if (ctx->read_body_done != NGX_HTTP_WAF_TRUE) {
184 | r->request_body_in_single_buf = 1;
185 | r->request_body_in_persistent_file = 1;
186 | r->request_body_in_clean_file = 1;
187 |
188 | ngx_int_t rc = ngx_http_read_client_request_body(r, _handler_read_request_body);
189 | if (rc == NGX_ERROR || rc >= NGX_HTTP_SPECIAL_RESPONSE) {
190 | return rc;
191 | }
192 | if (rc == NGX_AGAIN) {
193 | ctx->waiting_more_body = NGX_HTTP_WAF_TRUE;
194 | return NGX_DONE;
195 | }
196 | }
197 |
198 | if (loc_conf->waf == 0 || loc_conf->waf == NGX_CONF_UNSET) {
199 | http_status = NGX_DECLINED;
200 | ngx_log_debug(NGX_LOG_DEBUG_CORE, r->connection->log, 0,
201 | "ngx_waf_debug: Skip scheduling.");
202 |
203 | } else if (ngx_http_waf_check_flag(loc_conf->waf_mode, r->method) == NGX_HTTP_WAF_FALSE) {
204 | http_status = NGX_DECLINED;
205 |
206 | } else if (r->internal != 0 && ctx->checked == NGX_HTTP_WAF_TRUE) {
207 | http_status = NGX_DECLINED;
208 | ngx_log_debug(NGX_LOG_DEBUG_CORE, r->connection->log, 0,
209 | "ngx_waf_debug: Skip scheduling.");
210 |
211 | } else if (_read_request_body(r) == NGX_HTTP_WAF_BAD) {
212 | return NGX_HTTP_INTERNAL_SERVER_ERROR;
213 |
214 | } else {
215 | ctx->checked = NGX_HTTP_WAF_TRUE;
216 | ngx_http_waf_check_pt* funcs = loc_conf->check_proc;
217 | for (size_t i = 0; funcs[i] != NULL; i++) {
218 | is_matched = funcs[i](r, &http_status);
219 | if (is_matched == NGX_HTTP_WAF_MATCHED) {
220 | break;
221 | }
222 | }
223 | }
224 |
225 | if (http_status != NGX_DECLINED && http_status != NGX_DONE && http_status != NGX_ERROR) {
226 | ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, "ngx_waf: [%s][%s]", ctx->rule_type, ctx->rule_deatils);
227 | }
228 |
229 | if (http_status != NGX_DONE) {
230 | ctx->spend = ((double)clock() / CLOCKS_PER_SEC * 1000) - ctx->spend;
231 | }
232 |
233 | ngx_log_debug(NGX_LOG_DEBUG_CORE, r->connection->log, 0,
234 | "ngx_waf_debug: The scheduler shutdown normally.");
235 | return http_status;
236 | }
237 |
238 |
239 | static ngx_int_t _read_request_body(ngx_http_request_t* r) {
240 | ngx_http_waf_ctx_t* ctx = NULL;
241 | ngx_http_waf_loc_conf_t* loc_conf = NULL;
242 | ngx_http_waf_get_ctx_and_conf(r, &loc_conf, &ctx);
243 |
244 |
245 | if (r->request_body == NULL) {
246 | return NGX_HTTP_WAF_FAIL;
247 | }
248 |
249 | if (r->request_body->bufs == NULL) {
250 | return NGX_HTTP_WAF_FAIL;
251 | }
252 |
253 | if (r->request_body->temp_file) {
254 | return NGX_HTTP_WAF_FAIL;
255 | }
256 |
257 | if (ctx->has_req_body == NGX_HTTP_WAF_TRUE) {
258 | return NGX_HTTP_WAF_SUCCESS;
259 | }
260 |
261 | ngx_chain_t* bufs = r->request_body->bufs;
262 | size_t len = 0;
263 |
264 | while (bufs != NULL) {
265 | len += (bufs->buf->last - bufs->buf->pos) * (sizeof(u_char) / sizeof(uint8_t));
266 | bufs = bufs->next;
267 | }
268 |
269 | u_char* body = ngx_pnalloc(r->pool, len + sizeof(u_char));
270 | if (body == NULL) {
271 | return NGX_HTTP_WAF_BAD;
272 | }
273 |
274 | ctx->has_req_body = NGX_HTTP_WAF_TRUE;
275 | ctx->req_body.pos = body;
276 | ctx->req_body.last = (u_char*)((uint8_t*)body + len);
277 |
278 | bufs = r->request_body->bufs;
279 | size_t offset = 0;
280 | while (bufs != NULL) {
281 | size_t size = bufs->buf->last - bufs->buf->pos;
282 | ngx_memcpy((uint8_t*)body + offset, bufs->buf->pos, size);
283 | offset += size;
284 | bufs = bufs->next;
285 | }
286 | return NGX_HTTP_WAF_SUCCESS;
287 | }
288 |
289 |
290 | static void _handler_read_request_body(ngx_http_request_t* r) {
291 | ngx_http_waf_ctx_t* ctx = NULL;
292 | ngx_http_waf_loc_conf_t* loc_conf = NULL;
293 | ngx_http_waf_get_ctx_and_conf(r, &loc_conf, &ctx);
294 |
295 | ctx->read_body_done = NGX_HTTP_WAF_TRUE;
296 | ngx_http_finalize_request(r, NGX_DONE);
297 |
298 | if (ctx->waiting_more_body == NGX_HTTP_WAF_TRUE) {
299 | ctx->waiting_more_body = NGX_HTTP_WAF_FALSE;
300 | ngx_http_core_run_phases(r);
301 | }
302 | }
303 |
304 |
305 | void ngx_http_waf_handler_cleanup(void *data) {
306 | return;
307 | }
308 |
--------------------------------------------------------------------------------
/src/ngx_http_waf_module_ip_trie.c:
--------------------------------------------------------------------------------
1 | #include
2 |
3 |
4 | ngx_int_t ip_trie_init(ip_trie_t* trie, mem_pool_type_e pool_type, void* native_pool, int ip_type) {
5 | if (trie == NULL) {
6 | return NGX_HTTP_WAF_FAIL;
7 | }
8 |
9 |
10 | if (mem_pool_init(&trie->pool, pool_type, native_pool) != NGX_HTTP_WAF_SUCCESS) {
11 | return NGX_HTTP_WAF_FAIL;
12 | }
13 |
14 | trie->ip_type = ip_type;
15 | trie->root = (ip_trie_node_t*)mem_pool_calloc(&trie->pool, sizeof(ip_trie_node_t));
16 | trie->size = 0;
17 | trie->match_all = NGX_HTTP_WAF_FALSE;
18 |
19 | if (trie->root == NULL) {
20 | return NGX_HTTP_WAF_MALLOC_ERROR;
21 | }
22 |
23 | return NGX_HTTP_WAF_SUCCESS;
24 | }
25 |
26 |
27 | ngx_int_t ip_trie_add(ip_trie_t* trie, inx_addr_t* inx_addr, uint32_t suffix_num, void* data, size_t data_byte_length) {
28 | if (trie == NULL || inx_addr == NULL) {
29 | return NGX_HTTP_WAF_FAIL;
30 | }
31 |
32 | ip_trie_node_t* new_node = NULL;
33 |
34 | if (ip_trie_find(trie, inx_addr, &new_node) == NGX_HTTP_WAF_SUCCESS) {
35 | return NGX_HTTP_WAF_FAIL;
36 | }
37 |
38 | new_node = (ip_trie_node_t*)mem_pool_calloc(&trie->pool, sizeof(ip_trie_node_t));
39 | if (new_node == NULL) {
40 | return NGX_HTTP_WAF_MALLOC_ERROR;
41 | }
42 |
43 | new_node->data = mem_pool_calloc(&trie->pool, data_byte_length);
44 | if (new_node->data == NULL) {
45 | return NGX_HTTP_WAF_MALLOC_ERROR;
46 | }
47 |
48 | new_node->is_ip = NGX_HTTP_WAF_TRUE;
49 | ngx_memcpy(new_node->data, data, data_byte_length);
50 | new_node->data_byte_length = data_byte_length;
51 |
52 | if (suffix_num == 0) {
53 | trie->match_all = NGX_HTTP_WAF_TRUE;
54 | mem_pool_free(&trie->pool, trie->root);
55 | trie->root = new_node;
56 | return NGX_HTTP_WAF_SUCCESS;
57 | }
58 |
59 | ip_trie_node_t* prev_node = trie->root;
60 | ip_trie_node_t* cur_node = trie->root;
61 | uint32_t bit_index = 0, uint8_index;
62 | int prev_bit = 0;
63 |
64 | if (trie->ip_type == AF_INET) {
65 | uint8_t u8_addr[4];
66 | u8_addr[0] = (uint8_t)(inx_addr->ipv4.s_addr & 0x000000ff);
67 | u8_addr[1] = (uint8_t)((inx_addr->ipv4.s_addr & 0x0000ff00) >> 8);
68 | u8_addr[2] = (uint8_t)((inx_addr->ipv4.s_addr & 0x00ff0000) >> 16);
69 | u8_addr[3] = (uint8_t)((inx_addr->ipv4.s_addr & 0xff000000) >> 24);
70 |
71 | while (bit_index < suffix_num - 1) {
72 | uint8_index = bit_index / 8;
73 | if (cur_node == NULL) {
74 | cur_node = (ip_trie_node_t*)mem_pool_calloc(&trie->pool, sizeof(ip_trie_node_t));
75 | if (cur_node == NULL) {
76 | return NGX_HTTP_WAF_MALLOC_ERROR;
77 | }
78 | if (prev_bit == 0) {
79 | prev_node->left = cur_node;
80 | } else {
81 | prev_node->right = cur_node;
82 | }
83 | }
84 | prev_node = cur_node;
85 | if (ngx_http_waf_check_bit(u8_addr[uint8_index], 7 - (bit_index % 8)) != NGX_HTTP_WAF_TRUE) {
86 | prev_bit = 0;
87 | cur_node = cur_node->left;
88 | } else {
89 | prev_bit = 1;
90 | cur_node = cur_node->right;
91 | }
92 | ++bit_index;
93 | }
94 | if (cur_node == NULL) {
95 | cur_node = (ip_trie_node_t*)mem_pool_calloc(&trie->pool, sizeof(ip_trie_node_t));
96 | if (cur_node == NULL) {
97 | return NGX_HTTP_WAF_MALLOC_ERROR;
98 | }
99 | if (prev_bit == 0) {
100 | prev_node->left = cur_node;
101 | } else {
102 | prev_node->right = cur_node;
103 | }
104 | }
105 | uint8_index = bit_index / 8;
106 | if (ngx_http_waf_check_bit(u8_addr[uint8_index], 7 - (bit_index % 8)) != NGX_HTTP_WAF_TRUE) {
107 | cur_node->left = new_node;
108 | } else {
109 | cur_node->right = new_node;
110 | }
111 |
112 | }
113 | #if (NGX_HAVE_INET6)
114 | else if (trie->ip_type == AF_INET6) {
115 | while (bit_index < suffix_num - 1) {
116 | uint8_index = bit_index / 8;
117 | if (cur_node == NULL) {
118 | cur_node = (ip_trie_node_t*)mem_pool_calloc(&trie->pool, sizeof(ip_trie_node_t));
119 | if (cur_node == NULL) {
120 | return NGX_HTTP_WAF_MALLOC_ERROR;
121 | }
122 | if (prev_bit == 0) {
123 | prev_node->left = cur_node;
124 | } else {
125 | prev_node->right = cur_node;
126 | }
127 | }
128 | prev_node = cur_node;
129 | if (ngx_http_waf_check_bit(inx_addr->ipv6.s6_addr[uint8_index], 7 - (bit_index % 8)) != NGX_HTTP_WAF_TRUE) {
130 | cur_node = cur_node->left;
131 | prev_bit = 0;
132 | } else {
133 | cur_node = cur_node->right;
134 | prev_bit = 1;
135 | }
136 | ++bit_index;
137 | }
138 | if (cur_node == NULL) {
139 | cur_node = (ip_trie_node_t*)mem_pool_calloc(&trie->pool, sizeof(ip_trie_node_t));
140 | if (cur_node == NULL) {
141 | return NGX_HTTP_WAF_MALLOC_ERROR;
142 | }
143 | if (prev_bit == 0) {
144 | prev_node->left = cur_node;
145 | } else {
146 | prev_node->right = cur_node;
147 | }
148 | }
149 | uint8_index = bit_index / 8;
150 | if (ngx_http_waf_check_bit(inx_addr->ipv6.s6_addr[uint8_index], 7 - (bit_index % 8)) != NGX_HTTP_WAF_TRUE) {
151 | cur_node->left = new_node;
152 | } else {
153 | cur_node->right = new_node;
154 | }
155 | }
156 | #endif
157 |
158 | return NGX_HTTP_WAF_SUCCESS;
159 | }
160 |
161 |
162 | ngx_int_t ip_trie_find(ip_trie_t* trie, inx_addr_t* inx_addr, ip_trie_node_t** ip_trie_node) {
163 | if (trie == NULL || inx_addr == NULL || ip_trie_node ==NULL) {
164 | return NGX_HTTP_WAF_FAIL;
165 | }
166 |
167 | *ip_trie_node = NULL;
168 |
169 | if (trie->match_all == NGX_HTTP_WAF_TRUE) {
170 | *ip_trie_node = trie->root;
171 | return NGX_HTTP_WAF_SUCCESS;
172 | }
173 |
174 | ip_trie_node_t* cur_node = trie->root;
175 | ngx_int_t is_found = NGX_HTTP_WAF_FAIL;
176 | uint32_t bit_index = 0;
177 |
178 | if (trie->ip_type == AF_INET) {
179 | uint8_t u8_addr[4];
180 | u8_addr[0] = (uint8_t)(inx_addr->ipv4.s_addr & 0x000000ff);
181 | u8_addr[1] = (uint8_t)((inx_addr->ipv4.s_addr & 0x0000ff00) >> 8);
182 | u8_addr[2] = (uint8_t)((inx_addr->ipv4.s_addr & 0x00ff0000) >> 16);
183 | u8_addr[3] = (uint8_t)((inx_addr->ipv4.s_addr & 0xff000000) >> 24);
184 |
185 | while (bit_index < 32 && cur_node != NULL && cur_node->is_ip != NGX_HTTP_WAF_TRUE) {
186 | int uint8_index = bit_index / 8;
187 | if (ngx_http_waf_check_bit(u8_addr[uint8_index], 7 - (bit_index % 8)) != NGX_HTTP_WAF_TRUE) {
188 | cur_node = cur_node->left;
189 | } else {
190 | cur_node = cur_node->right;
191 | }
192 | ++bit_index;
193 | }
194 |
195 | }
196 | #if (NGX_HAVE_INET6)
197 | else if (trie->ip_type == AF_INET6) {
198 | while (bit_index < 128 && cur_node != NULL && cur_node->is_ip != NGX_HTTP_WAF_TRUE) {
199 | int uint8_index = bit_index / 8;
200 | if (ngx_http_waf_check_bit(inx_addr->ipv6.s6_addr[uint8_index], 7 - (bit_index % 8)) != NGX_HTTP_WAF_TRUE) {
201 | cur_node = cur_node->left;
202 | } else {
203 | cur_node = cur_node->right;
204 | }
205 | ++bit_index;
206 | }
207 | }
208 | #endif
209 |
210 | if (cur_node != NULL && cur_node->is_ip == NGX_HTTP_WAF_TRUE) {
211 | is_found = NGX_HTTP_WAF_SUCCESS;
212 | *ip_trie_node = cur_node;
213 | }
214 |
215 | return is_found;
216 | }
217 |
218 |
219 | // ngx_int_t ip_trie_delete(ip_trie_t* trie, inx_addr_t* inx_addr) {
220 | // if (trie == NULL || inx_addr == NULL) {
221 | // return NGX_HTTP_WAF_FAIL;
222 | // }
223 |
224 | // ip_trie_node_t* node = NULL;
225 | // ngx_int_t ret = ip_trie_find(trie, inx_addr, &node);
226 | // if (ret != NGX_HTTP_WAF_TRUE) {
227 | // return ret;
228 | // }
229 |
230 | // node->data_byte_length = 0;
231 | // node->is_ip = NGX_HTTP_WAF_FALSE;
232 |
233 | // ret = mem_pool_free(&trie->pool, node->data);
234 | // if (ret != NGX_HTTP_WAF_SUCCESS) {
235 | // return ret;
236 | // }
237 |
238 | // node->data = NULL;
239 |
240 | // return NGX_HTTP_WAF_SUCCESS;
241 | // }
242 |
243 |
244 | // ngx_int_t ip_trie_clear(ip_trie_t* trie) {
245 | // circular_doublly_linked_list_t* head = NULL;
246 |
247 | // _ip_trie_traversal(trie->root, &head);
248 | // if (head == NULL) {
249 | // return NGX_HTTP_WAF_SUCCESS;
250 | // }
251 |
252 | // circular_doublly_linked_list_t* item = NULL;
253 | // ip_trie_node_t* node = NULL;
254 |
255 | // while ((item = head->next), (item != NULL && item != head)) {
256 | // node = item->data;
257 | // if (node->data != NULL) {
258 | // mem_pool_free(&trie->pool, node->data);
259 | // }
260 | // mem_pool_free(&trie->pool, item->data);
261 | // CDL_DELETE(head, item);
262 | // free(item);
263 | // }
264 |
265 | // if (item != NULL) {
266 | // node = item->data;
267 | // if (node->data != NULL) {
268 | // mem_pool_free(&trie->pool, node->data);
269 | // }
270 | // mem_pool_free(&trie->pool, head->data);
271 | // item = head;
272 | // CDL_DELETE(head, head);
273 | // free(item);
274 | // }
275 |
276 | // trie->root->left = NULL;
277 | // trie->root->right = NULL;
278 |
279 | // return NGX_HTTP_WAF_SUCCESS;
280 | // }
281 |
282 |
283 | // void _ip_trie_traversal(ip_trie_node_t* node, circular_doublly_linked_list_t** head) {
284 | // if (node == NULL) {
285 | // return;
286 | // }
287 |
288 | // circular_doublly_linked_list_t* item = NULL;
289 |
290 | // if (node->left != NULL) {
291 | // item = malloc(sizeof(circular_doublly_linked_list_t));
292 | // if (item != NULL) {
293 | // ngx_memzero(item, sizeof(circular_doublly_linked_list_t));
294 | // item->data = node->left;
295 | // item->data_byte_length = sizeof(ip_trie_node_t);
296 | // CDL_APPEND(*head, item);
297 | // _ip_trie_traversal(node->left, head);
298 | // }
299 | // }
300 |
301 | // if (node->right != NULL) {
302 | // item = malloc(sizeof(circular_doublly_linked_list_t));
303 | // if (item != NULL) {
304 | // ngx_memzero(item, sizeof(circular_doublly_linked_list_t));
305 | // item->data = node->right;
306 | // item->data_byte_length = sizeof(ip_trie_node_t);
307 | // CDL_APPEND(*head, item);
308 | // _ip_trie_traversal(node->right, head);
309 | // }
310 | // }
311 |
312 | // }
313 |
--------------------------------------------------------------------------------
/src/ngx_http_waf_module_lru_cache.c:
--------------------------------------------------------------------------------
1 | #include
2 |
3 |
4 | lru_cache_item_t* _lru_cache_hash_find(lru_cache_t* lru, void* key, size_t key_len);
5 |
6 |
7 | void _lru_cache_hash_add(lru_cache_t* lru, lru_cache_item_t* item);
8 |
9 |
10 | void _lru_cache_hash_delete(lru_cache_t* lru, lru_cache_item_t* item);
11 |
12 |
13 | void* _lru_cache_hash_calloc(lru_cache_t* lru, size_t n);
14 |
15 |
16 | void _lru_cache_hash_free(lru_cache_t* lru, void* addr);
17 |
18 |
19 | void lru_cache_init(lru_cache_t** lru, size_t capacity, mem_pool_type_e pool_type, void* native_pool) {
20 | assert(lru != NULL);
21 |
22 | lru_cache_t* _lru;
23 |
24 | if (pool_type != std) {
25 | assert(native_pool != NULL);
26 | }
27 |
28 | mem_pool_t pool;
29 | assert(mem_pool_init(&pool, pool_type, native_pool) == NGX_HTTP_WAF_SUCCESS);
30 |
31 | _lru = mem_pool_calloc(&pool, sizeof(lru_cache_t));
32 | assert(_lru != NULL);
33 |
34 | ngx_memzero(_lru, sizeof(lru_cache_t));
35 |
36 | ngx_memcpy(&_lru->pool, &pool, sizeof(mem_pool_t));
37 | _lru->last_eliminate = time(NULL);
38 | _lru->capacity = capacity;
39 | _lru->hash_head = NULL;
40 | _lru->chain_head = NULL;
41 |
42 | *lru = _lru;
43 | }
44 |
45 |
46 | lru_cache_add_result_t lru_cache_add(lru_cache_t* lru, void* key, size_t key_len) {
47 | assert(lru != NULL);
48 | assert(key != NULL);
49 | assert(key_len != 0);
50 |
51 | lru_cache_add_result_t ret;
52 |
53 | lru_cache_item_t* item = _lru_cache_hash_find(lru, key, key_len);
54 | if (item != NULL) {
55 | CDL_DELETE(lru->chain_head, item);
56 | CDL_PREPEND(lru->chain_head, item);
57 | ret.status = NGX_HTTP_WAF_KEY_EXISTS;
58 | ret.data = &item->data;
59 | return ret;
60 | }
61 |
62 | if (HASH_COUNT(lru->hash_head) >= lru->capacity) {
63 | lru_cache_eliminate(lru, 1);
64 | }
65 |
66 |
67 | item = mem_pool_calloc(&lru->pool, sizeof(lru_cache_item_t));
68 | while (item == NULL && HASH_COUNT(lru->hash_head) != 0) {
69 | lru_cache_eliminate(lru, 1);
70 | item = mem_pool_calloc(&lru->pool, sizeof(lru_cache_item_t));
71 | }
72 |
73 | if (item == NULL) {
74 | ret.status = NGX_HTTP_WAF_MALLOC_ERROR;
75 | ret.data = NULL;
76 | return ret;
77 | }
78 |
79 | item->key_ptr = mem_pool_calloc(&lru->pool, key_len);
80 | while (item->key_ptr == NULL && HASH_COUNT(lru->hash_head) != 0) {
81 | lru_cache_eliminate(lru, 1);
82 | item->key_ptr = mem_pool_calloc(&lru->pool, key_len);
83 | }
84 |
85 | if (item->key_ptr == NULL) {
86 | mem_pool_free(&lru->pool, item);
87 | ret.status = NGX_HTTP_WAF_MALLOC_ERROR;
88 | ret.data = NULL;
89 | return ret;
90 | }
91 |
92 | ngx_memcpy(item->key_ptr, key, key_len);
93 | item->key_byte_length = key_len;
94 | CDL_PREPEND(lru->chain_head, item);
95 | _lru_cache_hash_add(lru, item);
96 |
97 | ret.status = NGX_HTTP_WAF_SUCCESS;
98 | ret.data = &item->data;
99 |
100 | return ret;
101 | }
102 |
103 |
104 | lru_cache_find_result_t lru_cache_find(lru_cache_t* lru, void* key, size_t key_len) {
105 | assert(lru != NULL);
106 | assert(key != NULL);
107 | assert(key_len != 0);
108 |
109 | lru_cache_find_result_t ret;
110 |
111 | lru_cache_item_t* item = _lru_cache_hash_find(lru, key, key_len);
112 | if (item != NULL) {
113 | CDL_DELETE(lru->chain_head, item);
114 | CDL_PREPEND(lru->chain_head, item);
115 | ret.status = NGX_HTTP_WAF_KEY_EXISTS;
116 | ret.data = &item->data;
117 | } else {
118 | ret.status = NGX_HTTP_WAF_KEY_NOT_EXISTS;
119 | ret.data = NULL;
120 | }
121 |
122 | return ret;
123 | }
124 |
125 |
126 | void* lru_cache_calloc(lru_cache_t* lru, size_t size) {
127 | assert(lru != NULL);
128 | assert(size != 0);
129 | return mem_pool_calloc(&lru->pool, size);
130 | }
131 |
132 |
133 | void lru_cache_free(lru_cache_t* lru, void* addr) {
134 | assert(lru != NULL);
135 | assert(addr != NULL);
136 | assert(addr != NGX_CONF_UNSET_PTR);
137 | mem_pool_free(&lru->pool, addr);
138 | }
139 |
140 |
141 | void lru_cache_delete(lru_cache_t* lru, void* key, size_t key_len) {
142 | assert(lru != NULL);
143 | assert(key != NULL);
144 | assert(key_len != 0);
145 |
146 | lru_cache_item_t* item = _lru_cache_hash_find(lru, key, key_len);
147 | if (item != NULL) {
148 | _lru_cache_hash_delete(lru, item);
149 | CDL_DELETE(lru->chain_head, item);
150 |
151 | if (item->data != NULL) {
152 | lru_cache_free(lru, item->data);
153 | }
154 |
155 | mem_pool_free(&lru->pool, item->key_ptr);
156 | mem_pool_free(&lru->pool, item);
157 | }
158 | }
159 |
160 |
161 | void lru_cache_eliminate(lru_cache_t* lru, size_t count) {
162 | assert(lru != NULL);
163 | assert(count != 0);
164 |
165 | for (size_t i = 0; i < count; i++) {
166 | if (lru->chain_head != NULL) {
167 | lru_cache_item_t* tail = lru->chain_head->prev;
168 | lru_cache_delete(lru, tail->key_ptr, tail->key_byte_length);
169 | }
170 | }
171 | }
172 |
173 |
174 | void lru_cache_destory(lru_cache_t* lru) {
175 | mem_pool_free(&lru->pool, lru);
176 | }
177 |
178 |
179 | lru_cache_item_t* _lru_cache_hash_find(lru_cache_t* lru, void* key, size_t key_len) {
180 | lru_cache_item_t* ret;
181 | HASH_FIND(hh, lru->hash_head, key, key_len, ret);
182 | return ret;
183 | }
184 |
185 | void _lru_cache_hash_add(lru_cache_t* lru, lru_cache_item_t* item) {
186 | #undef uthash_malloc
187 | #undef uthash_free
188 | #define uthash_malloc(n) _lru_cache_hash_calloc(lru, n)
189 | #define uthash_free(ptr,sz) _lru_cache_hash_free(lru, ptr)
190 | HASH_ADD_KEYPTR(hh, lru->hash_head, item->key_ptr, item->key_byte_length, item);
191 | #undef uthash_malloc
192 | #undef uthash_free
193 | #define uthash_malloc(n) malloc(n)
194 | #define uthash_free(ptr, sz) free(ptr)
195 | }
196 |
197 |
198 | void _lru_cache_hash_delete(lru_cache_t* lru, lru_cache_item_t* item) {
199 | #undef uthash_malloc
200 | #undef uthash_free
201 | #define uthash_malloc(n) _lru_cache_hash_calloc(lru, n)
202 | #define uthash_free(ptr,sz) _lru_cache_hash_free(lru, ptr)
203 | HASH_DELETE(hh, lru->hash_head, item);
204 | #undef uthash_malloc
205 | #undef uthash_free
206 | #define uthash_malloc(n) malloc(n)
207 | #define uthash_free(ptr, sz) free(ptr)
208 | }
209 |
210 |
211 | void* _lru_cache_hash_calloc(lru_cache_t* lru, size_t n) {
212 | void* ret = mem_pool_calloc(&lru->pool, n);
213 | while (ret == NULL && HASH_COUNT(lru->hash_head) != 0) {
214 | lru_cache_eliminate(lru, 1);
215 | ret = mem_pool_calloc(&lru->pool, n);
216 | }
217 | assert(ret != NULL);
218 | return ret;
219 | }
220 |
221 |
222 | void _lru_cache_hash_free(lru_cache_t* lru, void* addr) {
223 | mem_pool_free(&lru->pool, addr);
224 | }
--------------------------------------------------------------------------------
/src/ngx_http_waf_module_mem_pool.c:
--------------------------------------------------------------------------------
1 | #include
2 |
3 | ngx_int_t mem_pool_init(mem_pool_t* pool, mem_pool_type_e type, void* native_pool) {
4 | if (pool == NULL || (type != std && native_pool == NULL)) {
5 | return NGX_HTTP_WAF_FAIL;
6 | }
7 |
8 | pool->type = type;
9 |
10 | switch (type) {
11 | case std: break;
12 | case gernal_pool: pool->native_pool.gernal_pool = (ngx_pool_t*)native_pool; break;
13 | case slab_pool: pool->native_pool.slab_pool = (ngx_slab_pool_t*)native_pool; break;
14 | }
15 |
16 | return NGX_HTTP_WAF_SUCCESS;
17 | }
18 |
19 | void* mem_pool_calloc(mem_pool_t* pool, ngx_uint_t byte_size) {
20 | void* addr;
21 | switch (pool->type) {
22 | case std: addr = malloc(byte_size); ngx_memzero(addr, byte_size); break;
23 | case gernal_pool: addr = ngx_pcalloc(pool->native_pool.gernal_pool, byte_size); break;
24 | case slab_pool: addr = ngx_slab_calloc_locked(pool->native_pool.slab_pool, byte_size); break;
25 | default: addr = NULL; break;
26 | }
27 | return addr;
28 | }
29 |
30 | ngx_int_t mem_pool_free(mem_pool_t* pool, void* buffer) {
31 | switch (pool->type) {
32 | case std: free(buffer); break;
33 | case gernal_pool: ngx_pfree(pool->native_pool.gernal_pool, buffer); break;
34 | case slab_pool: ngx_slab_free_locked(pool->native_pool.slab_pool, buffer); break;
35 | default: return NGX_HTTP_WAF_FAIL;
36 | }
37 | return NGX_HTTP_WAF_SUCCESS;
38 | }
--------------------------------------------------------------------------------
/src/ngx_http_waf_module_under_attack.c:
--------------------------------------------------------------------------------
1 | #include
2 |
3 |
4 | ngx_int_t ngx_http_waf_check_under_attack(ngx_http_request_t* r, ngx_int_t* out_http_status) {
5 | ngx_log_debug(NGX_LOG_DEBUG_CORE, r->connection->log, 0,
6 | "ngx_waf_debug: Enter the Under-Attack processing flow.");
7 |
8 | ngx_http_waf_ctx_t* ctx = NULL;
9 | ngx_http_waf_loc_conf_t* loc_conf = NULL;
10 | ngx_http_waf_get_ctx_and_conf(r, &loc_conf, &ctx);
11 |
12 | if (loc_conf->waf_under_attack == 0 || loc_conf->waf_under_attack == NGX_CONF_UNSET) {
13 | ngx_log_debug(NGX_LOG_DEBUG_CORE, r->connection->log, 0,
14 | "ngx_waf_debug: Because this Inspection is disabled in the configuration, no Inspection is performed.");
15 | ngx_log_debug(NGX_LOG_DEBUG_CORE, r->connection->log, 0,
16 | "ngx_waf_debug: Processing is complete.");
17 | return NGX_HTTP_WAF_NOT_MATCHED;
18 | }
19 |
20 | if (ngx_strncmp(r->uri.data,
21 | loc_conf->waf_under_attack_uri.data,
22 | ngx_max(r->uri.len, loc_conf->waf_under_attack_uri.len)) == 0) {
23 | ngx_log_debug(NGX_LOG_DEBUG_CORE, r->connection->log, 0,
24 | "ngx_waf_debug: Because this Inspection is disabled in the configuration, no Inspection is performed.");
25 | ngx_log_debug(NGX_LOG_DEBUG_CORE, r->connection->log, 0,
26 | "ngx_waf_debug: Processing is complete.");
27 | return NGX_HTTP_WAF_NOT_MATCHED;
28 | }
29 |
30 | ngx_log_debug(NGX_LOG_DEBUG_CORE, r->connection->log, 0,
31 | "ngx_waf_debug: Begin the processing flow.");
32 |
33 | ngx_str_t __waf_under_attack_time = { 0, NULL };
34 | ngx_str_t __waf_under_attack_uid = { 0, NULL };
35 | ngx_str_t __waf_under_attack_verification = { 0, NULL };
36 |
37 | ngx_log_debug(NGX_LOG_DEBUG_CORE, r->connection->log, 0,
38 | "ngx_waf_debug: Start parsing cookies.");
39 |
40 | #if (nginx_version >= 1023000)
41 | if (r->headers_in.cookie != NULL) {
42 | ngx_table_elt_t* cookies = r->headers_in.cookie;
43 | #else
44 | if (r->headers_in.cookies.nelts > 0) {
45 | ngx_array_t* cookies = &(r->headers_in.cookies);
46 | #endif
47 | ngx_str_t key, value;
48 |
49 |
50 | ngx_str_set(&key, "__waf_under_attack_time");
51 | ngx_str_null(&value);
52 |
53 | #if (nginx_version >= 1023000)
54 | if (ngx_http_parse_multi_header_lines(r, cookies, &key, &value) != NULL) {
55 | #else
56 | if (ngx_http_parse_multi_header_lines(cookies, &key, &value) != NGX_DECLINED) {
57 | #endif
58 | __waf_under_attack_time.data = ngx_pcalloc(r->pool, value.len + 1);
59 | __waf_under_attack_time.len = value.len;
60 | ngx_memcpy(__waf_under_attack_time.data, value.data, value.len);
61 |
62 | }
63 |
64 | ngx_str_set(&key, "__waf_under_attack_uid");
65 | ngx_str_null(&value);
66 |
67 | #if (nginx_version >= 1023000)
68 | if (ngx_http_parse_multi_header_lines(r, cookies, &key, &value) != NULL) {
69 | #else
70 | if (ngx_http_parse_multi_header_lines(cookies, &key, &value) != NGX_DECLINED) {
71 | #endif
72 | __waf_under_attack_uid.data = ngx_pcalloc(r->pool, value.len + 1);
73 | __waf_under_attack_uid.len = value.len;
74 | ngx_memcpy(__waf_under_attack_uid.data, value.data, value.len);
75 |
76 | }
77 |
78 | ngx_str_set(&key, "__waf_under_attack_verification");
79 | ngx_str_null(&value);
80 |
81 | #if (nginx_version >= 1023000)
82 | if (ngx_http_parse_multi_header_lines(r, cookies, &key, &value) != NULL) {
83 | #else
84 | if (ngx_http_parse_multi_header_lines(cookies, &key, &value) != NGX_DECLINED) {
85 | #endif
86 | __waf_under_attack_verification.data = ngx_pcalloc(r->pool, value.len + 1);
87 | __waf_under_attack_verification.len = value.len;
88 | ngx_memcpy(__waf_under_attack_verification.data, value.data, value.len);
89 |
90 | }
91 | }
92 |
93 |
94 | /* 如果 cookie 不完整 */
95 | if (__waf_under_attack_time.data == NULL || __waf_under_attack_uid.data == NULL || __waf_under_attack_verification.data == NULL) {
96 | ngx_http_waf_gen_cookie(r);
97 | *out_http_status = 303;
98 | ngx_http_waf_gen_ctx_and_header_location(r);
99 | ngx_log_debug(NGX_LOG_DEBUG_CORE, r->connection->log, 0,
100 | "ngx_waf_debug: Failed to parse cookies");
101 | ngx_log_debug(NGX_LOG_DEBUG_CORE, r->connection->log, 0,
102 | "ngx_waf_debug: Processing is complete.");
103 | return NGX_HTTP_WAF_MATCHED;
104 | }
105 |
106 |
107 | ngx_log_debug(NGX_LOG_DEBUG_CORE, r->connection->log, 0,
108 | "ngx_waf_debug: Successfully parsed all cookies.");
109 |
110 |
111 | /* 验证 token 是否正确 */
112 | u_char cur_verification[NGX_HTTP_WAF_SHA256_HEX_LEN + 1];
113 | ngx_memzero(cur_verification, sizeof(u_char) * (NGX_HTTP_WAF_SHA256_HEX_LEN + 1));
114 | ngx_http_waf_gen_verification(r,
115 | __waf_under_attack_uid.data,
116 | NGX_HTTP_WAF_UNDER_ATTACH_UID_LEN,
117 | cur_verification,
118 | NGX_HTTP_WAF_SHA256_HEX_LEN,
119 | __waf_under_attack_time.data,
120 | NGX_HTTP_WAF_UNDER_ATTACH_TIME_LEN);
121 | if (ngx_memcmp(__waf_under_attack_verification.data, cur_verification, sizeof(u_char) * NGX_HTTP_WAF_SHA256_HEX_LEN) != 0) {
122 | ngx_http_waf_gen_cookie(r);
123 | *out_http_status = 303;
124 | ngx_http_waf_gen_ctx_and_header_location(r);
125 | ngx_log_debug(NGX_LOG_DEBUG_CORE, r->connection->log, 0,
126 | "ngx_waf_debug: Wrong __waf_under_attack_verification.");
127 | ngx_log_debug(NGX_LOG_DEBUG_CORE, r->connection->log, 0,
128 | "ngx_waf_debug: Processing is complete.");
129 | return NGX_HTTP_WAF_MATCHED;
130 | }
131 |
132 |
133 | /* 验证时间是否超过 5 秒 */
134 | time_t client_time = ngx_atoi(__waf_under_attack_time.data, __waf_under_attack_time.len);
135 | /* 如果 Cookie 不合法 或 已经超过 30 分钟 */
136 | if (client_time == NGX_ERROR || difftime(time(NULL), client_time) > 60 * 30) {
137 | ngx_http_waf_gen_cookie(r);
138 | *out_http_status = 303;
139 | ngx_http_waf_gen_ctx_and_header_location(r);
140 | ngx_log_debug(NGX_LOG_DEBUG_CORE, r->connection->log, 0,
141 | "ngx_waf_debug: Wrong __waf_under_attack_verification.");
142 | ngx_log_debug(NGX_LOG_DEBUG_CORE, r->connection->log, 0,
143 | "ngx_waf_debug: Processing is complete.");
144 | return NGX_HTTP_WAF_MATCHED;
145 | } else if (difftime(time(NULL), client_time) < 5) {
146 | *out_http_status = 303;
147 | ngx_http_waf_gen_ctx_and_header_location(r);
148 | ngx_log_debug(NGX_LOG_DEBUG_CORE, r->connection->log, 0,
149 | "ngx_waf_debug: Not five seconds have passed.");
150 | ngx_log_debug(NGX_LOG_DEBUG_CORE, r->connection->log, 0,
151 | "ngx_waf_debug: Processing is complete.");
152 | return NGX_HTTP_WAF_MATCHED;
153 | }
154 |
155 | return NGX_HTTP_WAF_NOT_MATCHED;
156 | }
157 |
158 |
159 | ngx_int_t ngx_http_waf_gen_cookie(ngx_http_request_t *r) {
160 | static size_t s_header_key_len = sizeof("Set-Cookie");
161 |
162 | ngx_table_elt_t *__waf_under_attack_time = NULL;
163 | ngx_table_elt_t *__waf_under_attack_uid = NULL;
164 | ngx_table_elt_t *__waf_under_attack_verification = NULL;
165 | int write_len = 0;
166 | long long int now = (long long int)time(NULL);
167 | u_char now_str[NGX_HTTP_WAF_UNDER_ATTACH_TIME_LEN + 1];
168 | ngx_memzero(now_str, sizeof(u_char) * (NGX_HTTP_WAF_UNDER_ATTACH_TIME_LEN + 1));
169 | sprintf((char*)now_str, "%lld", now);
170 |
171 | __waf_under_attack_time = (ngx_table_elt_t *)ngx_list_push(&(r->headers_out.headers));
172 | __waf_under_attack_time->hash = 1;
173 | __waf_under_attack_time->key.data = (u_char *)ngx_pnalloc(r->pool, sizeof(u_char) * (s_header_key_len + 1));
174 | ngx_memzero(__waf_under_attack_time->key.data, sizeof(u_char) * (s_header_key_len + 1));
175 | __waf_under_attack_time->key.len = s_header_key_len - 1;
176 | strcpy((char *)(__waf_under_attack_time->key.data), "Set-Cookie");
177 |
178 | __waf_under_attack_time->value.data = (u_char *)ngx_pnalloc(r->pool, sizeof(u_char) * (NGX_HTTP_WAF_UNDER_ATTACH_TIME_LEN * 4));
179 | ngx_memzero(__waf_under_attack_time->value.data, sizeof(u_char) * (NGX_HTTP_WAF_UNDER_ATTACH_TIME_LEN + 1));
180 | write_len = sprintf((char *)(__waf_under_attack_time->value.data), "__waf_under_attack_time=%s; Path=/", (char*)now_str);
181 | __waf_under_attack_time->value.len = (size_t)write_len;
182 |
183 |
184 | __waf_under_attack_uid = (ngx_table_elt_t *)ngx_list_push(&(r->headers_out.headers));
185 | __waf_under_attack_uid->hash = 1;
186 | __waf_under_attack_uid->key.data = (u_char *)ngx_pnalloc(r->pool, sizeof(u_char) * s_header_key_len);
187 | ngx_memzero(__waf_under_attack_uid->key.data, sizeof(u_char) * (s_header_key_len + 1));
188 | __waf_under_attack_uid->key.len = s_header_key_len - 1;
189 | strcpy((char *)(__waf_under_attack_uid->key.data), "Set-Cookie");
190 |
191 | __waf_under_attack_uid->value.data = (u_char *)ngx_pnalloc(r->pool, sizeof(u_char) * NGX_HTTP_WAF_UNDER_ATTACH_UID_LEN * 2);
192 | ngx_memzero(__waf_under_attack_uid->value.data, sizeof(u_char) * NGX_HTTP_WAF_UNDER_ATTACH_UID_LEN * 2);
193 | u_char* uid = ngx_pnalloc(r->pool, sizeof(u_char) * (NGX_HTTP_WAF_UNDER_ATTACH_UID_LEN + 1));
194 | ngx_http_waf_rand_str(uid, NGX_HTTP_WAF_UNDER_ATTACH_UID_LEN);
195 | write_len = sprintf((char *)(__waf_under_attack_uid->value.data)
196 | , "__waf_under_attack_uid=%s; Path=/",
197 | (char *)uid);
198 |
199 | __waf_under_attack_uid->value.len = (size_t)write_len;
200 |
201 |
202 | __waf_under_attack_verification = (ngx_table_elt_t *)ngx_list_push(&(r->headers_out.headers));
203 | __waf_under_attack_verification->hash = 1;
204 | __waf_under_attack_verification->key.data = (u_char *)ngx_pnalloc(r->pool, sizeof(u_char) * s_header_key_len);
205 | ngx_memzero(__waf_under_attack_verification->key.data, sizeof(u_char) * (s_header_key_len + 1));
206 | __waf_under_attack_verification->key.len = s_header_key_len - 1;
207 | strcpy((char *)(__waf_under_attack_verification->key.data), "Set-Cookie");
208 |
209 |
210 | __waf_under_attack_verification->value.data = (u_char *)ngx_pnalloc(r->pool, sizeof(u_char) * NGX_HTTP_WAF_SHA256_HEX_LEN * 2);
211 | u_char* verification = ngx_pnalloc(r->pool, sizeof(u_char) * (NGX_HTTP_WAF_SHA256_HEX_LEN + 1));
212 | ngx_memzero(__waf_under_attack_verification->value.data, sizeof(u_char) * NGX_HTTP_WAF_SHA256_HEX_LEN * 2);
213 | ngx_memzero(verification, sizeof(u_char) * (NGX_HTTP_WAF_SHA256_HEX_LEN + 1));
214 | ngx_http_waf_gen_verification(r,
215 | uid,
216 | NGX_HTTP_WAF_UNDER_ATTACH_UID_LEN,
217 | verification,
218 | NGX_HTTP_WAF_SHA256_HEX_LEN,
219 | now_str,
220 | NGX_HTTP_WAF_UNDER_ATTACH_TIME_LEN);
221 | write_len = sprintf((char *)(__waf_under_attack_verification->value.data),
222 | "__waf_under_attack_verification=%s; Path=/", (char*)verification);
223 |
224 | ngx_pfree(r->pool, verification);
225 | ngx_pfree(r->pool, uid);
226 | __waf_under_attack_verification->value.len = (size_t)write_len;
227 |
228 | return NGX_HTTP_WAF_TRUE;
229 | }
230 |
231 |
232 | ngx_int_t ngx_http_waf_gen_verification(ngx_http_request_t *r,
233 | u_char* uid,
234 | size_t uid_len,
235 | u_char* dst,
236 | size_t dst_len,
237 | u_char* now,
238 | size_t now_len) {
239 | ngx_http_waf_loc_conf_t* loc_conf = NULL;
240 | ngx_http_waf_get_ctx_and_conf(r, &loc_conf, NULL);
241 | size_t buf_len = sizeof(loc_conf->random_str) + sizeof(inx_addr_t) + uid_len + now_len;
242 | u_char *buf = (u_char *)ngx_pnalloc(r->pool, buf_len);
243 | ngx_memzero(buf, sizeof(u_char) * buf_len);
244 | inx_addr_t inx_addr;
245 | ngx_memzero(&inx_addr, sizeof(inx_addr));
246 |
247 | if (r->connection->sockaddr->sa_family == AF_INET) {
248 | struct sockaddr_in *sin = (struct sockaddr_in *)r->connection->sockaddr;
249 | ngx_memcpy(&(inx_addr.ipv4), &(sin->sin_addr), sizeof(struct in_addr));
250 |
251 | }
252 | #if (NGX_HAVE_INET6)
253 | else if (r->connection->sockaddr->sa_family == AF_INET6) {
254 | struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)r->connection->sockaddr;
255 | ngx_memcpy(&(inx_addr.ipv6), &(sin6->sin6_addr), sizeof(struct in6_addr));
256 | }
257 | #endif
258 |
259 |
260 | size_t offset = 0;
261 |
262 | /* 写入随机字符串 */
263 | ngx_memcpy(buf+ offset, loc_conf->random_str, sizeof(loc_conf->random_str));
264 | offset += sizeof(loc_conf->random_str);
265 |
266 | /* 写入时间戳 */
267 | ngx_memcpy(buf + offset, now, sizeof(u_char) * now_len);
268 | offset += now_len;
269 |
270 | /* 写入 uid */
271 | ngx_memcpy(buf + offset, uid, sizeof(u_char) * uid_len);
272 | offset += uid_len;
273 |
274 | /* 写入 IP 地址 */
275 | ngx_memcpy(buf + offset, &inx_addr, sizeof(inx_addr_t));
276 | offset += sizeof(inx_addr_t);
277 |
278 | ngx_int_t ret = ngx_http_waf_sha256(dst, dst_len + 1, buf, buf_len);
279 | ngx_pfree(r->pool, buf);
280 |
281 | return ret;
282 | }
283 |
284 |
285 | void ngx_http_waf_gen_ctx_and_header_location(ngx_http_request_t *r) {
286 | size_t s_header_location_key_len = sizeof("Location");
287 | ngx_http_waf_loc_conf_t* loc_conf = NULL;
288 | ngx_http_waf_get_ctx_and_conf(r, &loc_conf, NULL);
289 |
290 |
291 | ngx_table_elt_t* header = (ngx_table_elt_t *)ngx_list_push(&(r->headers_out.headers));
292 | header->hash = 1;
293 | header->key.data = ngx_pnalloc(r->pool, sizeof(u_char) * (s_header_location_key_len + 1));
294 | ngx_memzero(header->key.data, sizeof(u_char) * (s_header_location_key_len + 1));
295 | ngx_memcpy(header->key.data, "Location", s_header_location_key_len - 1);
296 | header->key.len = s_header_location_key_len - 1;
297 |
298 | header->value.data = ngx_pnalloc(r->pool, sizeof(u_char) * (loc_conf->waf_under_attack_uri.len + r->uri.len + 32));
299 | ngx_memzero(header->value.data, sizeof(u_char) * (loc_conf->waf_under_attack_uri.len + r->uri.len + 1));
300 | u_char* uri = ngx_pnalloc(r->pool, sizeof(u_char) * (r->uri.len + 1));
301 | ngx_memzero(uri, sizeof(u_char) * (r->uri.len + 1));
302 | ngx_memcpy(uri, r->uri.data, sizeof(u_char) * r->uri.len);
303 | header->value.len = sprintf((char*)header->value.data, "%s?target=%s",
304 | (char*)loc_conf->waf_under_attack_uri.data,
305 | (char*)uri);
306 | ngx_pfree(r->pool, uri);
307 |
308 | ngx_http_waf_ctx_t* ctx = NULL;
309 | ngx_http_waf_get_ctx_and_conf(r, NULL, &ctx);
310 | ctx->blocked = NGX_HTTP_WAF_TRUE;
311 | strcpy((char*)ctx->rule_type, "UNDER-ATTACK");
312 | ctx->rule_deatils[0] = '\0';
313 | }
--------------------------------------------------------------------------------
/src/ngx_http_waf_module_util.c:
--------------------------------------------------------------------------------
1 | #include
2 |
3 | ngx_int_t ngx_http_waf_parse_ipv4(ngx_str_t text, ipv4_t* ipv4) {
4 | uint32_t prefix = 0;
5 | uint32_t suffix = 0;
6 | uint32_t suffix_num = 0;
7 |
8 | if (ipv4 == NULL) {
9 | return NGX_HTTP_WAF_FAIL;
10 | }
11 |
12 | ngx_memcpy(ipv4->text, text.data, text.len);
13 | ipv4->text[text.len] = '\0';
14 |
15 | u_char* c = ipv4->text;
16 | ngx_uint_t prefix_len = 0;
17 | while (*c !='\0' && *c != '/') {
18 | ++prefix_len;
19 | ++c;
20 | }
21 |
22 | char prefix_text[32];
23 | struct in_addr addr4;
24 | if (*c =='\0' && prefix_len == text.len) {
25 | ngx_memcpy(prefix_text, ipv4->text, prefix_len);
26 | prefix_text[prefix_len] = '\0';
27 | }
28 | else if (*c == '/' && prefix_len >= 7) {
29 | /* 0.0.0.0 的长度刚好是 7 */
30 | ngx_memcpy(prefix_text, ipv4->text, prefix_len);
31 | prefix_text[prefix_len] = '\0';
32 | }
33 | else {
34 | return NGX_HTTP_WAF_FAIL;
35 | }
36 |
37 | if (inet_pton(AF_INET, prefix_text, &addr4) != 1) {
38 | return NGX_HTTP_WAF_FAIL;
39 | }
40 | prefix = addr4.s_addr;
41 |
42 | if (*c == '/') {
43 | ++c;
44 | }
45 | while (*c != '\0') {
46 | suffix = suffix * 10 + (*c - '0');
47 | ++c;
48 | }
49 | if (suffix == 0) {
50 | suffix = 32;
51 | }
52 |
53 | suffix_num = suffix;
54 |
55 | uint8_t temp_suffix[4] = { 0 };
56 | for (int i = 0; i < 4; i++) {
57 | uint8_t temp = 0;
58 | if (suffix >= 8) {
59 | suffix -=8;
60 | temp = ~0;
61 | }
62 | else {
63 | for (uint32_t j = 0; j < suffix; j++) {
64 | temp |= 0x80 >> j;
65 | }
66 | suffix = 0;
67 | }
68 | temp_suffix[i] = temp;
69 | }
70 |
71 | suffix = 0;
72 | for (int i = 0; i < 4; i++) {
73 | suffix |= ((uint32_t)temp_suffix[i]) << (i * 8);
74 | }
75 |
76 | ipv4->prefix = prefix & suffix;
77 | ipv4->suffix = suffix;
78 | ipv4->suffix_num = suffix_num;
79 |
80 | return NGX_HTTP_WAF_SUCCESS;
81 | }
82 |
83 |
84 | #if (NGX_HAVE_INET6)
85 | ngx_int_t ngx_http_waf_parse_ipv6(ngx_str_t text, ipv6_t* ipv6) {
86 | uint8_t prefix[16] = { 0 };
87 | uint8_t suffix[16] = { 0 };
88 | uint32_t suffix_num = 0;
89 |
90 | if (ipv6 == NULL) {
91 | return NGX_HTTP_WAF_FAIL;
92 | }
93 |
94 | ngx_memcpy(ipv6->text, text.data, text.len);
95 |
96 | ipv6->text[text.len] = '\0';
97 |
98 | u_char* c = ipv6->text;
99 | ngx_uint_t prefix_len = 0;
100 | while (*c !='\0' && *c != '/') {
101 | ++prefix_len;
102 | ++c;
103 | }
104 |
105 | char prefix_text[64];
106 | struct in6_addr addr6;
107 | if (*c =='\0' && prefix_len == text.len) {
108 | ngx_memcpy(prefix_text, ipv6->text, prefix_len);
109 | prefix_text[prefix_len] = '\0';
110 | }
111 | else if (*c == '/' && prefix_len >= 2) {
112 | /* :: 的长度刚好是 2,此 IPV6 地址代表全零 */
113 | ngx_memcpy(prefix_text, ipv6->text, prefix_len);
114 | prefix_text[prefix_len] = '\0';
115 | }
116 | else {
117 | return NGX_HTTP_WAF_FAIL;
118 | }
119 |
120 | if (inet_pton(AF_INET6, prefix_text, &addr6) != 1) {
121 | return NGX_HTTP_WAF_FAIL;
122 | }
123 | ngx_memcpy(prefix, &addr6.s6_addr, 16);
124 |
125 | uint32_t temp_suffix = 0;
126 | if (*c == '/') {
127 | ++c;
128 | }
129 | while (*c != '\0') {
130 | temp_suffix = temp_suffix * 10 + (*c - '0');
131 | ++c;
132 | }
133 | if (temp_suffix == 0) {
134 | temp_suffix = 128;
135 | }
136 |
137 | suffix_num = temp_suffix;
138 | for (int i = 0; i < 16; i++) {
139 | uint8_t temp = 0;
140 | if (temp_suffix >= 8) {
141 | temp_suffix -=8;
142 | temp = ~0;
143 | }
144 | else {
145 | for (uint32_t j = 0; j < temp_suffix; j++) {
146 | temp |= 0x80 >> j;
147 | }
148 | temp_suffix = 0;
149 | }
150 | suffix[i] = temp;
151 | }
152 |
153 | for (int i = 0; i < 16; i++) {
154 | prefix[i] &= suffix[i];
155 | }
156 |
157 | ngx_memcpy(ipv6->prefix, prefix, 16);
158 | ngx_memcpy(ipv6->suffix, suffix, 16);
159 | ipv6->suffix_num = suffix_num;
160 |
161 | return NGX_HTTP_WAF_SUCCESS;
162 | }
163 | #endif
164 |
165 |
166 | ngx_int_t ngx_http_waf_parse_time(u_char* str) {
167 | ngx_int_t ret = 0;
168 | size_t len = ngx_strlen(str);
169 | if (len < 2) {
170 | return NGX_ERROR;
171 | }
172 |
173 | ret = ngx_atoi(str, len - 1);
174 | if (ret == NGX_ERROR || ret <= 0) {
175 | return NGX_ERROR;
176 | }
177 |
178 | switch (str[len - 1]) {
179 | case 's': ret *= 1; break;
180 | case 'm': ret *= 1 * 60; break;
181 | case 'h': ret *= 1 * 60 * 60; break;
182 | case 'd': ret *= 1 * 60 * 60 * 24; break;
183 | default: return NGX_ERROR; break;
184 | }
185 |
186 | return ret;
187 | }
188 |
189 |
190 | ngx_int_t ngx_http_waf_parse_size(u_char* str) {
191 | ngx_int_t ret = 0;
192 | size_t len = ngx_strlen(str);
193 | if (len < 2) {
194 | return NGX_ERROR;
195 | }
196 |
197 | ret = ngx_atoi(str, len - 1);
198 | if (ret == NGX_ERROR || ret <= 0) {
199 | return NGX_ERROR;
200 | }
201 |
202 | switch (str[len - 1]) {
203 | case 'k': ret *= 1 * 1024; break;
204 | case 'm': ret *= 1 * 1024 * 1024; break;
205 | case 'g': ret *= 1 * 1024 * 1024 * 1024; break;
206 | default: return NGX_ERROR; break;
207 | }
208 |
209 | return ret;
210 | }
211 |
212 |
213 | ngx_int_t ngx_http_waf_parse_cookie(ngx_str_t* native_cookie, UT_array** array) {
214 | if (array == NULL) {
215 | return NGX_HTTP_WAF_FAIL;
216 | }
217 |
218 | UT_icd icd = ngx_http_waf_make_utarray_ngx_str_icd();
219 | utarray_new(*array, &icd);
220 |
221 | if (native_cookie == NULL) {
222 | return NGX_HTTP_WAF_FAIL;
223 | }
224 |
225 |
226 | UT_array* cookies = NULL;
227 | utarray_new(cookies, &icd);
228 |
229 | ngx_http_waf_str_split(native_cookie, ';', native_cookie->len, &cookies);
230 | ngx_str_t* p = NULL;
231 |
232 | while (p = (ngx_str_t*)utarray_next(cookies, p), p != NULL) {
233 | UT_array* key_and_value = NULL;
234 | ngx_str_t temp;
235 | temp.data = p->data;
236 | temp.len = p->len;
237 | if (p->data[0] == ' ') {
238 | temp.data += 1;
239 | temp.len -= 1;
240 | }
241 |
242 | ngx_http_waf_str_split(&temp, '=', native_cookie->len, &key_and_value);
243 |
244 | if (utarray_len(key_and_value) != 2) {
245 | return NGX_HTTP_WAF_FAIL;
246 | }
247 |
248 | ngx_str_t* key = NULL;
249 | ngx_str_t* value = NULL;
250 |
251 |
252 | key = (ngx_str_t*)utarray_next(key_and_value, NULL);
253 | value = (ngx_str_t*)utarray_next(key_and_value, key);
254 |
255 |
256 | utarray_push_back(*array, key);
257 | utarray_push_back(*array, value);
258 | utarray_free(key_and_value);
259 | }
260 |
261 | utarray_free(cookies);
262 |
263 | return NGX_HTTP_WAF_SUCCESS;
264 | }
265 |
266 |
267 | ngx_int_t ngx_http_waf_parse_query_string(ngx_str_t* native_query_string, key_value_t** hash_head) {
268 | if (hash_head == NULL) {
269 | return NGX_HTTP_WAF_FAIL;
270 | }
271 |
272 | if (native_query_string == NULL) {
273 | return NGX_HTTP_WAF_FAIL;
274 | }
275 |
276 |
277 | UT_array* kvs = NULL;
278 |
279 | ngx_http_waf_str_split(native_query_string, '&', native_query_string->len, &kvs);
280 | ngx_str_t* p = NULL;
281 |
282 | while (p = (ngx_str_t*)utarray_next(kvs, p), p != NULL) {
283 | UT_array* key_and_value = NULL;
284 | ngx_str_t temp;
285 | temp.data = p->data;
286 | temp.len = p->len;
287 |
288 | ngx_http_waf_str_split(&temp, '=', native_query_string->len, &key_and_value);
289 |
290 | if (utarray_len(key_and_value) != 2) {
291 | return NGX_HTTP_WAF_FAIL;
292 | }
293 |
294 | ngx_str_t* key = NULL;
295 | ngx_str_t* value = NULL;
296 |
297 |
298 | key = (ngx_str_t*)utarray_next(key_and_value, NULL);
299 | value = (ngx_str_t*)utarray_next(key_and_value, key);
300 |
301 | key_value_t* qs = malloc(sizeof(key_value_t));
302 | ngx_memzero(qs, sizeof(key_value_t));
303 | qs->key.data = ngx_strdup(key->data);
304 | qs->key.len = key->len;
305 | qs->value.data = ngx_strdup(value->data);
306 | qs->value.len = value->len;
307 |
308 | HASH_ADD_KEYPTR(hh, *hash_head, qs->key.data, qs->key.len * sizeof(u_char), qs);
309 |
310 | utarray_free(key_and_value);
311 | }
312 |
313 | utarray_free(kvs);
314 | return NGX_HTTP_WAF_SUCCESS;
315 | }
316 |
317 |
318 | ngx_int_t ngx_http_waf_parse_header(ngx_list_t* native_header, key_value_t** hash_head) {
319 | if (native_header == NULL || hash_head == NULL) {
320 | return NGX_HTTP_WAF_FALSE;
321 | }
322 |
323 | ngx_list_part_t* part = &(native_header->part);
324 | ngx_table_elt_t* value = part->elts;
325 |
326 | for (size_t i = 0; ; i++) {
327 | if (i >= part->nelts) {
328 | if (part->next == NULL) {
329 | break;
330 | }
331 |
332 | part = part->next;
333 | value = part->elts;
334 | i = 0;
335 | }
336 |
337 | key_value_t* temp = malloc(sizeof(key_value_t));
338 | ngx_memzero(temp, sizeof(key_value_t));
339 | temp->key.data = ngx_strdup(value[i].key.data);
340 | temp->key.len = value[i].key.len;
341 | ngx_strlow(temp->key.data, temp->key.data, temp->key.len);
342 | temp->value.data = ngx_strdup(value[i].value.data);
343 | temp->value.len = value[i].value.len;
344 | HASH_ADD_KEYPTR(hh, *hash_head, temp->key.data, temp->key.len * sizeof(u_char), temp);
345 |
346 | }
347 |
348 | return NGX_HTTP_WAF_TRUE;
349 | }
350 |
351 |
352 | ngx_int_t ngx_http_waf_ipv4_netcmp(uint32_t ip, const ipv4_t* ipv4) {
353 | size_t prefix = ip & ipv4->suffix;
354 |
355 | if (prefix == ipv4->prefix) {
356 | return NGX_HTTP_WAF_MATCHED;
357 | }
358 |
359 | return NGX_HTTP_WAF_NOT_MATCHED;
360 | }
361 |
362 |
363 | #if (NGX_HAVE_INET6)
364 | ngx_int_t ngx_http_waf_ipv6_netcmp(uint8_t ip[16], const ipv6_t* ipv6) {
365 | uint8_t temp_ip[16];
366 |
367 | memcpy(temp_ip, ip, 16);
368 |
369 | for (int i = 0; i < 16; i++) {
370 | temp_ip[i] &= ipv6->suffix[i];
371 | }
372 |
373 | if (memcmp(temp_ip, ipv6->prefix, sizeof(uint8_t) * 16) != 0) {
374 | return NGX_HTTP_WAF_NOT_MATCHED;
375 | }
376 |
377 | return NGX_HTTP_WAF_MATCHED;
378 | }
379 | #endif
380 |
381 |
382 | ngx_int_t ngx_http_waf_str_split(ngx_str_t* str, u_char sep, size_t max_len, UT_array** array) {
383 | if (array == NULL) {
384 | return NGX_HTTP_WAF_FAIL;
385 | }
386 |
387 | UT_icd icd = ngx_http_waf_make_utarray_ngx_str_icd();
388 | utarray_new(*array,&icd);
389 |
390 | if (str == NULL) {
391 | return NGX_HTTP_WAF_FAIL;
392 | }
393 |
394 | ngx_str_t temp_str;
395 | temp_str.data = malloc(sizeof(u_char) * (max_len + 1));
396 | ngx_memzero(temp_str.data, sizeof(u_char) * (max_len + 1));
397 | size_t str_index = 0;
398 |
399 | for (size_t i = 0; i < str->len; i++) {
400 | u_char c = str->data[i];
401 | if (c != sep) {
402 | if (str_index >= max_len) {
403 | free(temp_str.data);
404 | return NGX_HTTP_WAF_FAIL;
405 | }
406 | temp_str.data[str_index++] = c;
407 | } else {
408 | temp_str.data[str_index] = '\0';
409 | temp_str.len = str_index;
410 | utarray_push_back(*array, &temp_str);
411 | str_index = 0;
412 | }
413 | }
414 |
415 | if (str_index != 0) {
416 | temp_str.data[str_index] = '\0';
417 | temp_str.len = str_index;
418 | utarray_push_back(*array, &temp_str);
419 | str_index = 0;
420 | }
421 |
422 | free(temp_str.data);
423 |
424 | return NGX_HTTP_WAF_SUCCESS;
425 | }
426 |
427 |
428 | // ngx_int_t str_split(u_char* str, u_char sep, size_t max_len, UT_array** array) {
429 | // if (str == NULL || array == NULL) {
430 | // return NGX_HTTP_WAF_FAIL;
431 | // }
432 |
433 | // UT_icd icd = ngx_http_waf_make_utarray_ngx_str_icd();
434 |
435 | // utarray_new(*array,&icd);
436 | // ngx_str_t temp_str;
437 | // temp_str.data = malloc(sizeof(u_char) * max_len);
438 | // ngx_memzero(temp_str.data, sizeof(u_char) * max_len);
439 | // size_t str_index = 0;
440 |
441 | // for (size_t i = 0; str[i] != '\0'; i++) {
442 | // u_char c = str[i];
443 | // if (c != sep) {
444 | // if (str_index + 1 >= max_len) {
445 | // return NGX_HTTP_WAF_FAIL;
446 | // }
447 | // temp_str.data[str_index++] = c;
448 | // } else {
449 | // temp_str.data[str_index] = '\0';
450 | // temp_str.len = str_index;
451 | // utarray_push_back(*array, &temp_str);
452 | // str_index = 0;
453 | // }
454 | // }
455 |
456 | // if (str_index != 0) {
457 | // temp_str.data[str_index] = '\0';
458 | // temp_str.len = str_index;
459 | // utarray_push_back(*array, &temp_str);
460 | // str_index = 0;
461 | // }
462 |
463 | // free(temp_str.data);
464 |
465 | // return NGX_HTTP_WAF_SUCCESS;
466 | // }
467 |
468 |
469 | char* ngx_http_waf_to_c_str(u_char* destination, ngx_str_t ngx_str) {
470 | if (ngx_str.len > NGX_HTTP_WAF_RULE_MAX_LEN) {
471 | return NULL;
472 | }
473 | ngx_memcpy(destination, ngx_str.data, ngx_str.len);
474 | destination[ngx_str.len] = '\0';
475 | return (char*)destination + ngx_str.len;
476 | }
477 |
478 |
479 | ngx_int_t ngx_http_waf_rand_str(u_char* dest, size_t len) {
480 | if (dest == NULL || len == 0) {
481 | return NGX_HTTP_WAF_FAIL;
482 | }
483 |
484 | for (size_t i = 0; i < len; i++) {
485 | uint32_t num = randombytes_uniform(52);
486 | if (num < 26) {
487 | dest[i] = (unsigned char)'A' + (unsigned char)num;
488 | } else {
489 | dest[i] = (unsigned char)'a' + (unsigned char)(num - 26);
490 | }
491 | }
492 |
493 | dest[len] = '\0';
494 |
495 | return NGX_HTTP_WAF_SUCCESS;
496 | }
497 |
498 |
499 | ngx_int_t ngx_http_waf_sha256(u_char* dst, size_t dst_len, const u_char* buf, size_t buf_len) {
500 | if (dst == NULL || dst_len < crypto_hash_sha256_BYTES * 2 + 1 || buf == NULL || buf_len == 0) {
501 | return NGX_HTTP_WAF_FAIL;
502 | }
503 |
504 | unsigned char* out = malloc(sizeof(u_char) * crypto_hash_sha256_BYTES);
505 | ngx_memzero(out, sizeof(u_char) * crypto_hash_sha256_BYTES);
506 |
507 | crypto_hash_sha256(out, buf, buf_len);
508 | sodium_bin2hex((char*)dst, dst_len, out, crypto_hash_sha256_BYTES);
509 |
510 | free(out);
511 |
512 | return NGX_HTTP_WAF_SUCCESS;
513 | }
514 |
515 |
516 | void ngx_http_waf_utarray_ngx_str_ctor(void *dst, const void *src) {
517 | ngx_str_t* _dst = (ngx_str_t*)dst;
518 | const ngx_str_t* _src = (const ngx_str_t*)src;
519 |
520 | _dst->data = malloc(sizeof(u_char) * (_src->len + 1));
521 | ngx_memcpy(_dst->data, _src->data, sizeof(u_char) * _src->len);
522 | _dst->data[_src->len] = '\0';
523 | _dst->len = _src->len;
524 | }
525 |
526 |
527 | void ngx_http_waf_utarray_ngx_str_dtor(void* elt) {
528 | ngx_str_t* _elt = (ngx_str_t*)elt;
529 | free(_elt->data);
530 | }
531 |
532 |
533 | void ngx_http_waf_utarray_vm_code_ctor(void *dst, const void *src) {
534 | vm_code_t* _dst = (vm_code_t*)dst;
535 | const vm_code_t* _src = (const vm_code_t*)src;
536 | _dst->type = _src->type;
537 | _dst->argv.argc = _src->argv.argc;
538 |
539 |
540 | for (size_t i = 0; i < _src->argv.argc; i++) {
541 | _dst->argv.type[i] = _src->argv.type[i];
542 |
543 | if (_src->argv.type[i] == VM_DATA_STR) {
544 | size_t len = _src->argv.value[i].str_val.len;
545 | _dst->argv.value[i].str_val.len = len;
546 | _dst->argv.value[i].str_val.data = (u_char*)malloc(sizeof(u_char) * (len + 1));
547 | ngx_memcpy(_dst->argv.value[i].str_val.data, _src->argv.value[i].str_val.data, sizeof(u_char) * len);
548 | _dst->argv.value[i].str_val.data[len] = '\0';
549 | _dst->argv.value[i].str_val.len = len;
550 | } else {
551 | ngx_memcpy(&(_dst->argv.value[i]), &(_src->argv.value[i]), sizeof(_src->argv.value[i]));
552 | }
553 | }
554 | }
555 |
556 |
557 | void ngx_http_waf_utarray_vm_code_dtor(void* elt) {
558 | vm_code_t* _elt = (vm_code_t*)elt;
559 |
560 | for (size_t i = 0; i < _elt->argv.argc; i++) {
561 | switch (_elt->argv.type[i]) {
562 | case VM_DATA_STR:
563 | free(_elt->argv.value[i].str_val.data);
564 | break;
565 | default:
566 | break;
567 | }
568 | }
569 | }
--------------------------------------------------------------------------------
/test/test-nginx/init.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | set -xe
4 |
5 | if [ -z "$MODULE_TEST_PATH" ] ; then
6 | echo "Environment variable MODULE_TEST_PATH is not set."
7 | exit 1
8 | fi
9 |
10 | base_dir="$MODULE_TEST_PATH"
11 | origin_dir=$(pwd)
12 |
13 | rm -rf "$base_dir"
14 | mkdir -p "$base_dir"
15 | cp -r ../../assets "$base_dir/waf"
16 |
17 | templates=$(ls template)
18 |
19 | for file in $templates
20 | do
21 | eval "cat < "t/$file"
25 | done
26 |
27 | cd "$base_dir/waf"
28 | git clone https://github.com/SpiderLabs/ModSecurity.git
29 | git clone https://github.com/coreruleset/coreruleset.git
30 |
31 | mkdir -p modsec
32 | cp coreruleset/crs-setup.conf.example ./modsec/crs-setup.conf
33 | cp ModSecurity/modsecurity.conf-recommended ./modsec/modsecurity.conf
34 | cp ModSecurity/unicode.mapping ./modsec/unicode.mapping
35 |
36 | sed -i 's/SecRuleEngine DetectionOnly/SecRuleEngine On/' ./modsec/modsecurity.conf
37 | echo "Include /usr/local/nginx/conf/waf/modsec/crs-setup.conf" >> ./modsec/modsecurity.conf
38 | echo "Include /usr/local/nginx/conf/waf/coreruleset/rules/*.conf" >> ./modsec/modsecurity.conf
39 | echo "SecRule ARGS:test \"@streq deny\" \"id:1234567,phase:2,log,auditlog,deny,status:403\"" >> ./modsec/modsecurity.conf
40 | echo "SecRule ARGS:test \"@streq redirect\" \"id:123456,phase:2,log,auditlog,redirect:/,status:302\"" >> ./modsec/modsecurity.conf
41 |
42 |
43 | echo "1.1.1.1" >> ./rules/ipv4
44 | echo "2.0.0.0/8" >> ./rules/ipv4
45 |
46 | echo "3.3.3.3" >> ./rules/white-ipv4
47 | echo "4.0.0.0/8" >> ./rules/white-ipv4
48 |
49 | echo "AAAA::" >> ./rules/ipv6
50 | echo "BBBB::/16" >> ./rules/ipv6
51 |
52 | echo "CCCC::" >> ./rules/white-ipv6
53 | echo "DDDD::/16" >> ./rules/white-ipv6
54 |
55 | echo "/white/" >> ./rules/white-url
56 | echo "/white/" >> ./rules/white-referer
57 |
58 | cd "$origin_dir"
--------------------------------------------------------------------------------
/test/test-nginx/start.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | export PATH="/usr/local/openresty/nginx/sbin:/usr/local/nginx/sbin:$PATH"
4 |
5 | if [ -e "/usr/local/nginx/modules/ngx_http_waf_module.so" ] ; then
6 | export TEST_NGINX_LOAD_MODULES=/usr/local/nginx/modules/ngx_http_waf_module.so
7 | fi
8 |
9 | if [ -n "$MODULE_PATH" ] ; then
10 | export TEST_NGINX_LOAD_MODULES="$MODULE_PATH"
11 | fi
12 |
13 | # export TEST_NGINX_LOG_LEVEL=emerg
14 | # export TEST_NGINX_SLEEP=1
15 | # export TEST_NGINX_NO_CLEAN=1
16 | # export TEST_NGINX_MASTER_PROCESS=on
17 |
18 | exec prove "$@"
--------------------------------------------------------------------------------
/test/test-nginx/t/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ADD-SP/ngx_waf/458b6a62e26c201bc72e1366e87e775fab7d9efa/test/test-nginx/t/.gitkeep
--------------------------------------------------------------------------------
/test/test-nginx/template/args.t:
--------------------------------------------------------------------------------
1 | use Test::Nginx::Socket 'no_plan';
2 |
3 | run_tests();
4 |
5 |
6 | __DATA__
7 |
8 | === TEST: General
9 |
10 | --- config
11 | waf on;
12 | waf_mode GET ARGS;
13 | waf_rule_path ${base_dir}/waf/rules/;
14 |
15 | --- request
16 | GET /?s=test0
17 |
18 | --- error_code chomp
19 | 200
20 |
21 | === TEST: Black query string
22 |
23 | --- config
24 | waf on;
25 | waf_mode GET ARGS;
26 | waf_rule_path ${base_dir}/waf/rules/;
27 |
28 | --- pipelined_requests eval
29 | [
30 | "GET /?s=../",
31 | "GET /?s=onload="
32 | ]
33 |
34 | --- error_code eval
35 | [
36 | 403,
37 | 403
38 | ]
39 |
--------------------------------------------------------------------------------
/test/test-nginx/template/bad-config.t:
--------------------------------------------------------------------------------
1 | use Test::Nginx::Socket 'no_plan';
2 |
3 | run_tests();
4 |
5 |
6 | __DATA__
7 |
8 | === TEST: Bad directive waf
9 |
10 | --- config
11 | waf bad;
12 |
13 | --- must_die
14 |
15 |
16 | === TEST: Bad directive waf_rule_path
17 |
18 | --- config
19 | waf_rule_path ${base_dir}/waf/rules;
20 |
21 | --- must_die
22 |
23 |
24 | === TEST: Bad directive waf_mode
25 |
26 | --- config
27 | waf_mode BAD;
28 |
29 | --- must_die
30 |
31 |
32 | === TEST: Bad directive waf_cc_deny (1)
33 |
34 | --- config
35 | waf_cc_deny bad rate=100/m;
36 |
37 | --- must_die
38 |
39 |
40 | === TEST: Bad directive waf_cc_deny (2)
41 |
42 | --- config
43 | waf_cc_deny rate=r/m;
44 |
45 | --- must_die
46 |
47 | === TEST: Bad directive waf_cc_deny (3)
48 |
49 | --- config
50 | waf_cc_deny rate=-1r/m;
51 |
52 | --- must_die
53 |
54 |
55 | === TEST: Bad directive waf_cc_deny (4)
56 |
57 | --- config
58 | waf_cc_deny rate=100r;
59 |
60 | --- must_die
61 |
62 |
63 | === TEST: Bad directive waf_cc_deny (5)
64 |
65 | --- config
66 | waf_cc_deny rate=100r/b;
67 |
68 | --- must_die
69 |
70 |
71 | === TEST: Bad directive waf_cc_deny (6)
72 |
73 | --- config
74 | waf_cc_deny on;
75 |
76 | --- must_die
77 |
78 |
79 | === TEST: Bad directive waf_cc_deny (7)
80 |
81 | --- config
82 | waf_cc_deny rate=100r/m duration=1;
83 |
84 | --- must_die
85 |
86 |
87 | === TEST: Bad directive waf_cc_deny (8)
88 |
89 | --- config
90 | waf_cc_deny rate=100r/m duration=1b;
91 |
92 | --- must_die
93 |
94 |
95 | === TEST: Bad directive waf_cc_deny (9)
96 |
97 | --- config
98 | waf_cc_deny rate=100r/m duration=1h size=10z;
99 |
100 | --- must_die
101 |
102 |
103 | === TEST: Bad directive waf_cc_deny (10)
104 |
105 | --- config
106 | waf_cc_deny rate=100r/m duration=1h bad=bad;
107 |
108 | --- must_die
109 |
110 |
111 | === TEST: Bad directive waf_cache (1)
112 |
113 | --- config
114 | waf_cache on capacity=-1;
115 |
116 | --- must_die
117 |
118 |
119 | === TEST: Bad directive waf_cache (2)
120 |
121 | --- config
122 | waf_cache bad=bad;
123 |
124 | --- must_die
125 |
126 |
127 | === TEST: Bad directive waf_under_attack (1)
128 |
129 | --- config
130 | waf_under_attack bad;
131 |
132 | --- must_die
133 |
134 |
135 | === TEST: Bad directive waf_under_attack (2)
136 |
137 | --- config
138 | waf_under_attack on bad;
139 |
140 | --- must_die
141 |
142 |
143 | === TEST: Bad directive waf_under_attack (3)
144 |
145 | --- config
146 | waf_under_attack on bad;
147 |
148 | --- must_die
149 |
150 |
151 | === TEST: Bad directive waf_http_status
152 |
153 | --- config
154 | waf_http_status bad;
155 |
156 | --- must_die
157 |
158 |
159 | === TEST: Bad directive waf_priority
160 |
161 | --- config
162 | waf_priority "W-IP IP VERIFY-BOT CC CAPTCHA UNDER-ATTACK W-URL URL ARGS UA W-REFERER REFERER COOKIE POST"
163 |
164 | --- must_die
165 |
--------------------------------------------------------------------------------
/test/test-nginx/template/cache.t:
--------------------------------------------------------------------------------
1 | use Test::Nginx::Socket 'no_plan';
2 |
3 | run_tests();
4 |
5 |
6 | __DATA__
7 |
8 | === TEST: Cache
9 |
10 | --- config
11 | waf on;
12 | waf_mode FULL !CC;
13 | waf_rule_path ${base_dir}/waf/rules/;
14 | waf_cache capacity=1;
15 |
16 | --- pipelined_requests eval
17 | [
18 | "GET /test0",
19 | "GET /test0",
20 | "GET /test1",
21 | "GET /test1",
22 | "GET /test2",
23 | "GET /test2",
24 | "GET /test3",
25 | "GET /test3",
26 | "GET /test4",
27 | "GET /test4",
28 | ]
29 |
30 | --- error_code eval
31 | [
32 | 404,
33 | 404,
34 | 404,
35 | 404,
36 | 404,
37 | 404,
38 | 404,
39 | 404,
40 | 404,
41 | 404
42 | ]
--------------------------------------------------------------------------------
/test/test-nginx/template/cc.t:
--------------------------------------------------------------------------------
1 | use Test::Nginx::Socket 'no_plan';
2 |
3 | run_tests();
4 |
5 |
6 | __DATA__
7 |
8 | === TEST: CC wihout CAPTCHA
9 |
10 | --- config
11 | waf on;
12 | waf_mode FULL;
13 | waf_rule_path ${base_dir}/waf/rules/;
14 | waf_cc_deny rate=1r/m;
15 |
16 | location /t {
17 | waf_cc_deny rate=100r/m;
18 | }
19 |
20 | --- pipelined_requests eval
21 | [
22 | "GET /",
23 | "GET /",
24 | "GET /",
25 | "GET /",
26 | "GET /",
27 | "GET /t",
28 | "GET /t",
29 | "GET /t",
30 | "GET /t",
31 | "GET /t"
32 | ]
33 |
34 | --- error_code eval
35 | [
36 | 200,
37 | 503,
38 | 503,
39 | 503,
40 | 503,
41 | 404,
42 | 404,
43 | 404,
44 | 404,
45 | 404
46 | ]
--------------------------------------------------------------------------------
/test/test-nginx/template/cookie.t:
--------------------------------------------------------------------------------
1 | use Test::Nginx::Socket 'no_plan';
2 |
3 | run_tests();
4 |
5 |
6 | __DATA__
7 |
8 | === TEST: General
9 |
10 | --- config
11 | waf on;
12 | waf_mode GET COOKIE !CC;
13 | waf_rule_path ${base_dir}/waf/rules/;
14 |
15 | --- request
16 | GET /
17 |
18 | --- more_headers
19 | Cookie: s=test
20 |
21 | --- error_code chomp
22 | 200
23 |
24 | === TEST: Black cookie
25 |
26 | --- config
27 | waf on;
28 | waf_mode GET COOKIE !CC;
29 | waf_rule_path ${base_dir}/waf/rules/;
30 |
31 | location /t {
32 | waf_mode FULL !COOKIE;
33 | }
34 |
35 | --- pipelined_requests eval
36 | [
37 | "GET /",
38 | "GET /t"
39 | ]
40 |
41 | --- more_headers eval
42 | [
43 | "Cookie: s=../",
44 | "Cookie: s=../"
45 | ]
46 |
47 |
48 | --- error_code eval
49 | [
50 | 403,
51 | 404
52 | ]
--------------------------------------------------------------------------------
/test/test-nginx/template/disable.t:
--------------------------------------------------------------------------------
1 | use Test::Nginx::Socket 'no_plan';
2 |
3 | run_tests();
4 |
5 |
6 | __DATA__
7 |
8 | === TEST: General
9 |
10 | --- config
11 | waf off;
12 | waf_mode FULL;
13 | waf_rule_path ${base_dir}/waf/rules/;
14 | waf_cc_deny rate=100r/m;
15 | waf_cache capacity=50;
16 |
17 | --- request
18 | GET /www.bak
19 |
20 | --- error_code chomp
21 | 404
--------------------------------------------------------------------------------
/test/test-nginx/template/ipv4.t:
--------------------------------------------------------------------------------
1 | use Test::Nginx::Socket 'no_plan';
2 |
3 | run_tests();
4 |
5 |
6 | __DATA__
7 |
8 | === TEST: General
9 |
10 | --- config
11 | waf on;
12 | waf_mode GET URL IP;
13 | waf_rule_path ${base_dir}/waf/rules/;
14 |
15 | set_real_ip_from 127.0.0.0/8;
16 | real_ip_header X-Real-IP;
17 |
18 | --- pipelined_requests eval
19 | [
20 | "GET /",
21 | "GET /www.bak",
22 | ]
23 |
24 | --- error_code eval
25 | [
26 | "200",
27 | "403",
28 | ]
29 |
30 | === TEST: White IPV4
31 |
32 | --- config
33 | waf on;
34 | waf_mode GET URL IP;
35 | waf_rule_path ${base_dir}/waf/rules/;
36 |
37 | set_real_ip_from 127.0.0.0/8;
38 | real_ip_header X-Real-IP;
39 |
40 | --- pipelined_requests eval
41 | [
42 | "GET /www.bak",
43 | "GET /www.bak",
44 | "GET /www.bak",
45 | "GET /www.bak",
46 | "GET /www.bak"
47 | ]
48 |
49 | --- more_headers eval
50 | [
51 | "X-Real-IP: 3.3.3.3",
52 | "X-Real-IP: 4.0.0.0",
53 | "X-Real-IP: 4.1.0.0",
54 | "X-Real-IP: 4.0.1.0",
55 | "X-Real-IP: 4.0.0.1"
56 | ]
57 |
58 |
59 | --- error_code eval
60 | [
61 | "404",
62 | "404",
63 | "404",
64 | "404",
65 | "404"
66 | ]
67 |
68 | === TEST: Black IPV4
69 |
70 | --- config
71 | waf on;
72 | waf_mode GET URL IP;
73 | waf_rule_path ${base_dir}/waf/rules/;
74 |
75 | set_real_ip_from 127.0.0.0/8;
76 | real_ip_header X-Real-IP;
77 |
78 | --- pipelined_requests eval
79 | [
80 | "GET /",
81 | "GET /",
82 | "GET /",
83 | "GET /",
84 | "GET /"
85 | ]
86 |
87 | --- more_headers eval
88 | [
89 | "X-Real-IP: 1.1.1.1",
90 | "X-Real-IP: 2.0.0.0",
91 | "X-Real-IP: 2.1.0.0",
92 | "X-Real-IP: 2.0.1.0",
93 | "X-Real-IP: 2.0.0.1"
94 | ]
95 |
96 |
97 | --- error_code eval
98 | [
99 | "403",
100 | "403",
101 | "403",
102 | "403",
103 | "403"
104 | ]
105 |
106 |
--------------------------------------------------------------------------------
/test/test-nginx/template/ipv6.t:
--------------------------------------------------------------------------------
1 | use Test::Nginx::Socket 'no_plan';
2 |
3 | run_tests();
4 |
5 |
6 | __DATA__
7 |
8 | === TEST: General
9 |
10 | --- config
11 | waf on;
12 | waf_mode GET URL IP;
13 | waf_rule_path ${base_dir}/waf/rules/;
14 |
15 | set_real_ip_from 127.0.0.0/8;
16 | real_ip_header X-Real-IP;
17 |
18 | --- pipelined_requests eval
19 | [
20 | "GET /",
21 | "GET /www.bak",
22 | ]
23 |
24 | --- more_headers eval
25 | [
26 | "X-Real-IP: EEEE::",
27 | "X-Real-IP: FFFF::",
28 | ]
29 |
30 | --- error_code eval
31 | [
32 | "200",
33 | "403",
34 | ]
35 |
36 | === TEST: White IPV4
37 |
38 | --- config
39 | waf on;
40 | waf_mode GET URL IP;
41 | waf_rule_path ${base_dir}/waf/rules/;
42 |
43 | set_real_ip_from 127.0.0.0/8;
44 | real_ip_header X-Real-IP;
45 |
46 | --- pipelined_requests eval
47 | [
48 | "GET /www.bak",
49 | "GET /www.bak",
50 | "GET /www.bak",
51 | "GET /www.bak",
52 | "GET /www.bak"
53 | ]
54 |
55 | --- more_headers eval
56 | [
57 | "X-Real-IP: CCCC::",
58 | "X-Real-IP: DDDD:1::",
59 | "X-Real-IP: DDDD::1",
60 | "X-Real-IP: DDDD::1:1",
61 | "X-Real-IP: DDDD::1:1:1",
62 | "X-Real-IP: DDDD::1:1:1:1",
63 | "X-Real-IP: DDDD::1:1:1:1:1",
64 | "X-Real-IP: DDDD::1:1:1:1:1:1",
65 | "X-Real-IP: DDDD::1:1:1:1:1:1:1"
66 | ]
67 |
68 |
69 | --- error_code eval
70 | [
71 | "404",
72 | "404",
73 | "404",
74 | "404",
75 | "404",
76 | "404",
77 | "404",
78 | "404",
79 | "404"
80 | ]
81 |
82 | === TEST: Black IPV4
83 |
84 | --- config
85 | waf on;
86 | waf_mode GET URL IP;
87 | waf_rule_path ${base_dir}/waf/rules/;
88 |
89 | set_real_ip_from 127.0.0.0/8;
90 | real_ip_header X-Real-IP;
91 |
92 | --- pipelined_requests eval
93 | [
94 | "GET /",
95 | "GET /",
96 | "GET /",
97 | "GET /",
98 | "GET /"
99 | ]
100 |
101 | --- more_headers eval
102 | [
103 | "X-Real-IP: AAAA::",
104 | "X-Real-IP: BBBB:1::",
105 | "X-Real-IP: BBBB::1",
106 | "X-Real-IP: BBBB::1:1",
107 | "X-Real-IP: BBBB::1:1:1",
108 | "X-Real-IP: BBBB::1:1:1:1",
109 | "X-Real-IP: BBBB::1:1:1:1:1",
110 | "X-Real-IP: BBBB::1:1:1:1:1:1",
111 | "X-Real-IP: BBBB::1:1:1:1:1:1:1"
112 | ]
113 |
114 |
115 | --- error_code eval
116 | [
117 | "403",
118 | "403",
119 | "403",
120 | "403",
121 | "403",
122 | "403",
123 | "403",
124 | "403",
125 | "403"
126 | ]
127 |
128 |
--------------------------------------------------------------------------------
/test/test-nginx/template/mode.t:
--------------------------------------------------------------------------------
1 | use Test::Nginx::Socket 'no_plan';
2 |
3 | run_tests();
4 |
5 |
6 | __DATA__
7 |
8 | === TEST: No method GET
9 |
10 | --- config
11 | waf on;
12 | waf_mode FULL !GET !CC !CACHE;
13 | waf_rule_path ${base_dir}/waf/rules/;
14 |
15 | --- request
16 | GET /www.bak
17 |
18 |
19 | --- error_code chomp
20 | 404
21 |
22 | === TEST: No method POST
23 |
24 | --- config
25 | waf on;
26 | waf_mode FULL !POST !CC !CACHE;
27 | waf_rule_path ${base_dir}/waf/rules/;
28 |
29 | --- request
30 | POST /www.bak
31 |
32 |
33 | --- error_code chomp
34 | 404
35 |
36 | === TEST: No method HEAD
37 |
38 | --- config
39 | waf on;
40 | waf_mode FULL !HEAD !CC !CACHE;
41 | waf_rule_path ${base_dir}/waf/rules/;
42 |
43 | --- request
44 | HEAD /www.bak
45 |
46 |
47 | --- error_code chomp
48 | 404
49 |
--------------------------------------------------------------------------------
/test/test-nginx/template/post.t:
--------------------------------------------------------------------------------
1 | use Test::Nginx::Socket 'no_plan';
2 |
3 | run_tests();
4 |
5 |
6 | __DATA__
7 |
8 | === TEST: General
9 |
10 | --- config
11 | waf on;
12 | waf_mode GET POST RBODY;
13 | waf_rule_path ${base_dir}/waf/rules/;
14 |
15 | location /t {
16 | }
17 |
18 | --- request
19 | POST /t
20 | s=test
21 |
22 | --- error_code chomp
23 | 404
24 |
25 | === TEST: Black POST
26 |
27 | --- config
28 | waf on;
29 | waf_mode GET POST RBODY;
30 | waf_rule_path ${base_dir}/waf/rules/;
31 |
32 | location /t {
33 | }
34 |
35 | --- request
36 | POST /t
37 | onload=
38 |
39 | --- error_code chomp
40 | 403
41 |
42 |
--------------------------------------------------------------------------------
/test/test-nginx/template/referer.t:
--------------------------------------------------------------------------------
1 | use Test::Nginx::Socket 'no_plan';
2 |
3 | run_tests();
4 |
5 |
6 | __DATA__
7 |
8 | === TEST: General
9 |
10 | --- config
11 | waf on;
12 | waf_mode GET REFERER;
13 | waf_rule_path ${base_dir}/waf/rules/;
14 |
15 | --- request
16 | GET /
17 |
18 | --- more_headers
19 | Referer: /test
20 |
21 | --- error_code chomp
22 | 200
23 |
24 |
25 | === TEST: Black referer
26 |
27 | --- config
28 | waf on;
29 | waf_mode GET REFERER;
30 | waf_rule_path ${base_dir}/waf/rules/;
31 |
32 | --- pipelined_requests eval
33 | [
34 | "GET /",
35 | "GET /"
36 | ]
37 |
38 | --- more_headers eval
39 | [
40 | "Referer: /www.bak",
41 | "Referer: /www.bak"
42 | ]
43 |
44 | --- error_code eval
45 | [
46 | 403,
47 | 403
48 | ]
49 |
50 |
51 | === TEST: White referer
52 |
53 | --- config
54 | waf on;
55 | waf_mode GET REFERER;
56 | waf_rule_path ${base_dir}/waf/rules/;
57 |
58 | --- pipelined_requests eval
59 | [
60 | "GET /",
61 | "GET /"
62 | ]
63 |
64 | --- more_headers eval
65 | [
66 | "Referer: /white/www.bak",
67 | "Referer: /white/www.bak"
68 | ]
69 |
70 | --- error_code eval
71 | [
72 | 200,
73 | 200
74 | ]
--------------------------------------------------------------------------------
/test/test-nginx/template/under-attack.t:
--------------------------------------------------------------------------------
1 | use Test::Nginx::Socket 'no_plan';
2 |
3 | run_tests();
4 |
5 |
6 | __DATA__
7 |
8 | === TEST: Under Attack Mode
9 |
10 | --- config
11 | waf on;
12 | waf_mode FULL !CC;
13 | waf_rule_path ${base_dir}/waf/rules/;
14 | waf_under_attack on uri=/;
15 |
16 | --- pipelined_requests eval
17 | [
18 | "GET /",
19 | "GET /test"
20 | ]
21 |
22 | --- more_headers eval
23 | [
24 | "",
25 | "Cookie: __waf_under_attack_time=123456; __waf_under_attack_uid=123456; __waf_under_attack_hmac=123456"
26 | ]
27 |
28 | --- error_code eval
29 | [
30 | 200,
31 | 303
32 | ]
--------------------------------------------------------------------------------
/test/test-nginx/template/uri.t:
--------------------------------------------------------------------------------
1 | use Test::Nginx::Socket 'no_plan';
2 |
3 | run_tests();
4 |
5 |
6 | __DATA__
7 |
8 | === TEST: General
9 |
10 | --- config
11 | waf on;
12 | waf_mode GET URL !CC;
13 | waf_rule_path ${base_dir}/waf/rules/;
14 |
15 | --- pipelined_requests eval
16 | [
17 | "GET /",
18 | "GET /test0"
19 | ]
20 |
21 | --- error_code eval
22 | [
23 | 200,
24 | 404
25 | ]
26 |
27 | === TEST: Black URI
28 |
29 | --- config
30 | waf on;
31 | waf_mode GET URL !CC;
32 | waf_rule_path ${base_dir}/waf/rules/;
33 |
34 |
35 | --- pipelined_requests eval
36 | [
37 | "GET /www.bak",
38 | "GET /static/index.php"
39 | ]
40 |
41 | --- error_code eval
42 | [
43 | 403,
44 | 403
45 | ]
46 |
47 | === TEST: White URI
48 |
49 | --- config
50 | waf on;
51 | waf_mode GET URL !CC;
52 | waf_rule_path ${base_dir}/waf/rules/;
53 |
54 | --- pipelined_requests eval
55 | [
56 | "GET /white/www.bak",
57 | "GET /white/static/index.php"
58 | ]
59 |
60 | --- error_code eval
61 | [
62 | 404,
63 | 404
64 | ]
--------------------------------------------------------------------------------
/test/test-nginx/template/user-agent.t:
--------------------------------------------------------------------------------
1 | use Test::Nginx::Socket 'no_plan';
2 |
3 | run_tests();
4 |
5 |
6 | __DATA__
7 |
8 | === TEST: General
9 |
10 | --- config
11 | waf on;
12 | waf_mode GET UA !CC;
13 | waf_rule_path ${base_dir}/waf/rules/;
14 |
15 | --- request
16 | GET /
17 |
18 | --- more_headers
19 | User-Agent: test-user-agent
20 |
21 | --- error_code chomp
22 | 200
23 |
24 | === TEST: Black user-agent
25 |
26 | --- config
27 | waf on;
28 | waf_mode GET UA !CC;
29 | waf_rule_path ${base_dir}/waf/rules/;
30 |
31 | --- request
32 | GET /
33 |
34 | --- more_headers
35 | User-Agent: / SF/
36 |
37 | --- error_code chomp
38 | 403
--------------------------------------------------------------------------------
/test/test-nginx/template/var.t:
--------------------------------------------------------------------------------
1 | use Test::Nginx::Socket 'no_plan';
2 |
3 | run_tests();
4 |
5 |
6 | __DATA__
7 |
8 | === TEST: Check value
9 |
10 | --- config
11 | waf on;
12 | waf_mode FULL;
13 | waf_rule_path ${base_dir}/waf/rules/;
14 |
15 | location /error {
16 | return 200 [\$waf_log][\$waf_blocking_log][\$waf_blocked][\$waf_rule_type];
17 | }
18 |
19 | error_page 403 /error;
20 |
21 | --- pipelined_requests eval
22 | [
23 | "GET /www.bak",
24 | "GET /?test=onload="
25 | ]
26 |
27 | --- response_body eval
28 | [
29 | "[true][true][true][BLACK-URL]",
30 | "[true][true][true][BLACK-ARGS]"
31 | ]
32 |
33 | --- error_code eval
34 | [
35 | 403,
36 | 403
37 | ]
38 |
39 |
40 | === TEST: Check run
41 |
42 | --- config
43 | waf on;
44 | waf_mode FULL;
45 | waf_rule_path ${base_dir}/waf/rules/;
46 |
47 | location /t {
48 |
49 | }
50 |
51 | location /error {
52 | return 200 [\$waf_log][\$waf_blocking_log][\$waf_blocked][\$waf_rule_type][\$waf_spend][\$waf_rule_details];
53 | }
54 |
55 | error_page 403 /error;
56 |
57 | --- pipelined_requests eval
58 | [
59 | "GET /",
60 | "GET /www.bak",
61 | "GET /?test=onload="
62 | ]
63 |
64 | --- error_code eval
65 | [
66 | 200,
67 | 403,
68 | 403
69 | ]
70 |
--------------------------------------------------------------------------------
/test/wrk/rand.lua:
--------------------------------------------------------------------------------
1 | -- Store all random strings read from the file
2 | texts = { }
3 |
4 |
5 | function init(args)
6 | -- Set random number seed
7 | math.randomseed(os.time())
8 |
9 | -- Open the file and read the entire contents.
10 | local file = io.open(args[1], "r");
11 | for line in file:lines() do
12 | table.insert(texts, line)
13 | end
14 | io.close(file)
15 | end
16 |
17 |
18 | function request()
19 | --
20 | local path = string.format("/%s",texts[math.random(1, #texts)])
21 | return wrk.format("GET", path, nil, nil)
22 | end
--------------------------------------------------------------------------------