ngx_http_naxsi_init 函数中:
/* Register for rewrite phase */
// 模块处理函数挂载: NGX_HTTP_REWRITE_PHASE 为 Location请求地址重写阶段
h = ngx_array_push(&cmcf->phases[NGX_HTTP_REWRITE_PHASE].handlers);
if (h == NULL)
return (NGX_ERROR); /*LCOV_EXCL_LINE*/
*h = ngx_http_naxsi_access_handler;
if (!ctx) {
ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_request_ctx_t));
if (ctx == NULL)
return NGX_ERROR;
ngx_http_set_ctx(r, ctx, ngx_http_naxsi_module); // ngx_http_set_ctx 定义为一个macro,就是将ctx保存到ngx_http_request_t的ctx指针数组中,以module的ctx_index为索引
NX_DEBUG(_debug_modifier,
NGX_LOG_DEBUG_HTTP,
r->connection->log,
0,
/* process rules only if request is not already blocked or if
the learning mode is enabled */
// 检测 header
ngx_http_naxsi_headers_parse(main_cf, cf, ctx, r);
/* check uri */
// 检测 url
ngx_http_naxsi_uri_parse(main_cf, cf, ctx, r);
/* check args */
// 检测 参数
ngx_http_naxsi_args_parse(main_cf, cf, ctx, r);
/* check method */
if ((r->method == NGX_HTTP_POST || r->method == NGX_HTTP_PUT) &&
/* presence of body rules (POST/PUT rules) */
(cf->body_rules || main_cf->body_rules) &&
/* and the presence of data to parse */
r->request_body && ((!ctx->block || ctx->learning) && !ctx->drop))
// 检测 body
ngx_http_naxsi_body_parse(ctx, r, cf, main_cf);
下面将以ngx_http_naxsi_headers_parse函数为例做解析,即是对headers的检测。
void
ngx_http_naxsi_headers_parse(ngx_http_naxsi_main_conf_t* main_cf,
ngx_http_naxsi_loc_conf_t* cf,
ngx_http_request_ctx_t* ctx,
ngx_http_request_t* r)
{
ngx_list_part_t* part;
ngx_table_elt_t* h;
unsigned int i;
ngx_str_t lowcase_header;
if (!cf->header_rules && !main_cf->header_rules) // 无 header相关的规则,直接return
return;
// this check may be removed, as it shouldn't be needed anymore !
if ((ctx->block && !ctx->learning) || ctx->drop) // 这种情况也无需后面的检测:因为已经要做block 或 drop 处理了。
return;
part = &r->headers_in.headers.part;
h = part->elts;
// this check may be removed, as it shouldn't be needed anymore !
for (i = 0; ((!ctx->block || ctx->learning) && !ctx->block); i++) {
if (i >= part->nelts) {
if (part->next == NULL)
break;
part = part->next;
h = part->elts; // 获取headers内容
i = 0;
}
lowcase_header.data = h[i].lowcase_key;
lowcase_header.len = h[i].key.len;
if (naxsi_escape_nullbytes(&lowcase_header) > 0) {
ngx_http_apply_rulematch_v_n(
&nx_int__uncommon_hex_encoding, ctx, r, &h[i].key, &h[i].value, HEADERS, 1, 1);
}
if (naxsi_escape_nullbytes(&h[i].value) > 0) {
ngx_http_apply_rulematch_v_n(
&nx_int__uncommon_hex_encoding, ctx, r, &h[i].key, &h[i].value, HEADERS, 1, 0);
}
if (cf->header_rules) // location配置检测: 即用location块中配置的策略做检测
ngx_http_basestr_ruleset_n(
r->pool, &lowcase_header, &(h[i].value), cf->header_rules, r, ctx, HEADERS);
if (main_cf->header_rules) // 主配置检测: 即在http块中配置的策略做检测
ngx_http_basestr_ruleset_n(
r->pool, &lowcase_header, &(h[i].value), main_cf->header_rules, r, ctx, HEADERS);
}
return;
}
/* call to libinjection: 做实际的检测 */
ngx_http_libinjection(pool, name, value, ctx, req, zone);
ngx_http_libinjection函数:
/*
** check variable + name against a set of rules, checking against 'custom'
*location rules too.
*/
void
ngx_http_libinjection(ngx_pool_t* pool,
ngx_str_t* name,
ngx_str_t* value,
ngx_http_request_ctx_t* ctx,
ngx_http_request_t* req,
naxsi_match_zone_t zone)
{
/*
** Libinjection integration :
** 1 - check if libinjection_sql is explicitly enabled
** 2 - check if libinjection_xss is explicitly enabled
** if 1 is true : perform check on both name and content,
** in case of match, apply internal rule
** increasing the LIBINJECTION_SQL score
** if 2 is true ; same as for '1' but with
** LIBINJECTION_XSS
*/
sfilter state;
int issqli;
// 实际的sql注入检测
if (ctx->libinjection_sql) {
/* hardcoded call to libinjection on NAME, apply internal rule if matched.
*/
libinjection_sqli_init(&state, (const char*)name->data, name->len, FLAG_NONE);
issqli = libinjection_is_sqli(&state);
if (issqli == 1) {
// 检测到sql注入攻击, 就将对应的分值有 nx_int__libinject_sql 记录到 请求上下文ctx中
ngx_http_apply_rulematch_v_n(nx_int__libinject_sql, ctx, req, name, value, zone, 1, 1);
}
/* hardcoded call to libinjection on CONTENT, apply internal rule if
* matched. */
libinjection_sqli_init(&state, (const char*)value->data, value->len, FLAG_NONE);
issqli = libinjection_is_sqli(&state);
if (issqli == 1) {
ngx_http_apply_rulematch_v_n(nx_int__libinject_sql, ctx, req, name, value, zone, 1, 0);
}
}
// 实际的xss检测
if (ctx->libinjection_xss) {
/* first on var_name */
issqli = libinjection_xss((const char*)name->data, name->len);
if (issqli == 1) {
ngx_http_apply_rulematch_v_n(nx_int__libinject_xss, ctx, req, name, value, zone, 1, 1);
}
/* hardcoded call to libinjection on CONTENT, apply internal rule if
* matched. */
issqli = libinjection_xss((const char*)value->data, value->len);
if (issqli == 1) {
ngx_http_apply_rulematch_v_n(nx_int__libinject_xss, ctx, req, name, value, zone, 1, 0);
}
}
}
/*
ngx_http_libinjection 检测后,如果 ctx->block 或者 ctx->drop 为真 就不需要再做 id > 1000策略的检测了,
否则需要再做 id > 1000策略 naxsi自己的策略检测
*/
for (i = 0; i < rules->nelts && ((!ctx->block || ctx->learning) && !ctx->drop); i++) {
custom_location就是用 “规则详解” -> “MainRule规则” -> “naxsi自己的规则” -> “还有其他种特定写法:” 下面的规则做检测:
/* does the rule have a custom location ? custom location means checking
* only on a specific argument */
if (name && r[i].br->custom_location) {
就是用 “规则详解” -> “MainRule规则” -> “naxsi自己的规则” 下的非 “还有其他种特定写法:” 规则做检测:
/*
** check against the rule if the current zone is matching
** the zone the rule is meant to be check against
*/
if ((zone == HEADERS && r[i].br->headers) || (zone == URL && r[i].br->url) ||
(zone == ARGS && r[i].br->args) || (zone == BODY && r[i].br->raw_body) ||
(zone == BODY && r[i].br->body_rule) || (zone == FILE_EXT && r[i].br->file_ext)) {
/*
** in : string to inspect, associated rule
** does : apply the rule on the string, return 1 if matched,
** 0 else and -1 on error
*/
int
ngx_http_process_basic_rule_buffer(ngx_str_t* str, ngx_http_rule_t* rl, ngx_int_t* nb_match)
{
ngx_int_t match, tmp_idx, len;
unsigned char* ret;
int captures[30];
if (!rl->br || !nb_match)
return (-1);
*nb_match = 0;
// 正则匹配模式
if (rl->br->match_type == RX && rl->br->rx) {
tmp_idx = 0;
len = str->len;
while
#if defined nginx_version && (nginx_version >= 1002002 && nginx_version != 1003000)
(tmp_idx < len && (match = pcre_exec(rl->br->rx->regex->code,
0,
(const char*)str->data,
str->len,
tmp_idx,
0,
captures,
30)) >= 0)
#elif defined nginx_version && (nginx_version > 1001011)
(tmp_idx < len && (match = pcre_exec(rl->br->rx->regex->pcre,
0,
(const char*)str->data,
str->len,
tmp_idx,
0,
captures,
30)) >= 0)
#elif defined nginx_version && (nginx_version <= 1001011)
(tmp_idx < len &&
(match = pcre_exec(
rl->br->rx->regex, 0, (const char*)str->data, str->len, tmp_idx, 0, captures, 30)) >= 0)
#elif defined nginx_version
#error "Inconsistent nginx version."
(0)
#else
#error "nginx_version not defined."
(0)
#endif
{
*nb_match += match;
tmp_idx = captures[1];
}
if (*nb_match > 0) {
if (rl->br->negative)
return (0);
else
return (1);
} else if (*nb_match == 0) {
if (rl->br->negative)
return (1);
else
return (0);
}
return (-1);
}
// 字符串匹配模式
else if (rl->br->match_type == STR && rl->br->str) {
match = 0;
tmp_idx = 0;
while (1) {
ret = (unsigned char*)strfaststr((unsigned char*)str->data + tmp_idx,
(unsigned int)str->len - tmp_idx,
(unsigned char*)rl->br->str->data,
(unsigned int)rl->br->str->len);
if (ret) {
match = 1;
*nb_match = *nb_match + 1;
} else
break;
if (nb_match && ret < (str->data + str->len)) {
tmp_idx = (ret - str->data) + 1;
if (tmp_idx > (int)(str->len - 1))
break;
} else
break;
}
if (match) {
if (rl->br->negative)
return (0);
else
return (1);
} else {
if (rl->br->negative)
return (1);
else
return (0);
}
}
// LIBINJ_XSS 匹配模式, 即通过libinjection库做xss攻击检测
else if (rl->br->match_type == LIBINJ_XSS) {
if (libinjection_xss((const char*)str->data, str->len) == 1)
return (1);
}
// LIBINJ_SQL 匹配模式, 即通过libinjection库做sql注入检测
else if (rl->br->match_type == LIBINJ_SQL) {
sfilter state;
libinjection_sqli_init(&state, (const char*)str->data, str->len, FLAG_NONE);
if (libinjection_is_sqli(&state) == 1)
return (1);
}
return (0);
}