基于nginx的waf方案naxsi源码理解(7)_检测处理

模块处理函数挂载

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;    

模块处理函数: 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,

ngx_http_naxsi_data_parse //实际的检测入口

  /* 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);

对header内容的检测

下面将以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;
}

ngx_http_basestr_ruleset_n //实际的检测

调用libinjection库做检测

  /* 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);
    }
  }
}

naxsi自己的规则检测

  /*
    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)) {

naxsi自己的规则的实际检测函数

/*
** 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);
}

你可能感兴趣的:(#,naxsi,nginx,waf,web防火墙,api安全,naxsi)