Elasticsearchによる異常検知: Significant terms aggregation

## はじめに

ElasticsearchのAggregationを使って、Webログから異常検知を試してみます。
Aggregationは、クエリ結果を分類・集計し、データの傾向や変化を示してくれます。
今回の異常検知には、[Significant terms aggregation](https://www.elastic.co/guide/en/elasticsearch/reference/current/search-aggregations-bucket-significantterms-aggregation.html)を使用します。

## やりたいこと

頻繁にHTTPリクエストに失敗し、サイバー攻撃の疑いのあるIPアドレスを検出します。

## サンプルデータ

Kibana提供の`Sample web logs`を登録します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2523098/6d42c391-a38f-9c11-0976-b394e0775a5d.png)

## KibanaのDev Toolsでクエリ発行


- クエリの内容
    - まず、HTTPレスポンスのステータスコードにてグルーピング、
    - つぎ、`significant_terms`を使用し、ステータス別関連度が高いIPアドレスを特定

```sql
GET kibana_sample_data_logs/_search
{
  "size": 0,
  "query": {
    "bool": {
      "must_not": {
        "term": {
          "response": 200
        }
      }
    }
  },
  "aggregations": {
    "clientip_by_response": {
      "terms": {
        "field": "response.keyword"
      },
      "aggregations": {
        "significant_clientip": {
          "significant_terms": {
            "field": "clientip"
          }
        }
      }
    }
  }
}
```

## 実行結果

HTTPステータスコード404(Not Found)において、
スコア上位に位置するのは、IPアドレス`30.156.16.164`。
スコアが2を超えるので、異常なアクセスの疑いもあります。

```sql
{
  "took" : 13,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 1242,
      "relation" : "eq"
    },
    "max_score" : null,
    "hits" : [ ]
  },
  "aggregations" : {
    "clientip_by_response" : {
      "doc_count_error_upper_bound" : 0,
      "sum_other_doc_count" : 0,
      "buckets" : [
        {
          "key" : "404",
          "doc_count" : 801,
          "significant_clientip" : {
            "doc_count" : 801,
            "bg_count" : 14074,
            "buckets" : [
              {
                "key" : "30.156.16.164",
                "doc_count" : 100,
                "score" : 2.0687311896334326,
                "bg_count" : 100
              },
              {
                "key" : "221.241.228.46",
                "doc_count" : 5,
                "score" : 0.03292878729481851,
                "bg_count" : 14
              },
              {
                "key" : "251.234.26.249",
                "doc_count" : 4,
                "score" : 0.03010344435248698,
                "bg_count" : 10
              },
              {
                "key" : "209.94.234.133",
                "doc_count" : 4,
                "score" : 0.024253910659948055,
                "bg_count" : 12
              },
              {
                "key" : "172.0.84.195",
                "doc_count" : 3,
                "score" : 0.020932401913338665,
                "bg_count" : 8
              },
              {
                "key" : "88.209.41.99",
                "doc_count" : 4,
                "score" : 0.020075672308134538,
                "bg_count" : 14
              },
              {
                "key" : "68.242.246.24",
                "doc_count" : 4,
                "score" : 0.015651655229743756,
                "bg_count" : 17
              },
              {
                "key" : "213.235.30.99",
                "doc_count" : 4,
                "score" : 0.01450468783904985,
                "bg_count" : 18
              },
              {
                "key" : "108.200.26.115",
                "doc_count" : 3,
                "score" : 0.01420211456822996,
                "bg_count" : 11
              },
              {
                "key" : "73.105.236.24",
                "doc_count" : 3,
                "score" : 0.01420211456822996,
                "bg_count" : 11
              }
            ]
          }
        },
        {
          "key" : "503",
          "doc_count" : 441,
          "significant_clientip" : {
            "doc_count" : 441,
            "bg_count" : 14074,
            "buckets" : [
              {
                "key" : "65.45.138.4",
                "doc_count" : 4,
                "score" : 0.0799967408325037,
                "bg_count" : 13
              },
              {
                "key" : "30.145.143.210",
                "doc_count" : 4,
                "score" : 0.05525589760553588,
                "bg_count" : 18
              },
              {
                "key" : "49.162.217.157",
                "doc_count" : 3,
                "score" : 0.036617458774893176,
                "bg_count" : 15
              },
              {
                "key" : "130.125.65.24",
                "doc_count" : 3,
                "score" : 0.031509202320383935,
                "bg_count" : 17
              },
              {
                "key" : "246.139.130.246",
                "doc_count" : 3,
                "score" : 0.029380762131005083,
                "bg_count" : 18
              },
              {
                "key" : "199.17.43.174",
                "doc_count" : 3,
                "score" : 0.027476368277350324,
                "bg_count" : 19
              },
              {
                "key" : "120.49.143.213",
                "doc_count" : 3,
                "score" : 0.02576241380906104,
                "bg_count" : 20
              },
              {
                "key" : "236.212.255.77",
                "doc_count" : 3,
                "score" : 0.018247382678869557,
                "bg_count" : 26
              }
            ]
          }
        }
      ]
    }
  }
}
```

## 別のフィールドを用いることで、様々な切り口から異常検知ができそう


- agent
- geo
- host
- machine
- message
- request
- tags
- url

## おわりに

Elasticsearchの`Significant terms aggregation`を用いて、
Webアプリログから、頻繁にHTTP 404が発生する異常なIPアドレスを検知してみました。
今後、別のアプローチでも異常検知を試してみます。お楽しみに。