Search

Django to ELK request body sanitization(JSON 필드 필터)

카테고리
Back-end
태그
Django
ELK
게시일
2023/02/10
수정일
2024/02/24 09:41
시리즈
1 more property

1. 개요

django에서 ELK로 로깅을 남길 때 request JSON body 내의 데이터에서 불필요한 데이터가 들어갈 경우가 있습니다. 불필요한 데이터의 예시는 고객정보, 사진정보, 민감데이터 등의 다양한 데이터가 있습니다. 또한 데이터의 크기가 너무 크기 때문에 용량 문제로 에러가 발생할 수 있습니다. 이러한 이슈를 해결하기 위해서는 Django에서 ELK로 로그 데이터를 넘길 때 특정 부분을 수정해주는 방법을 고려하면 좋습니다.

불필요한 데이터

고객정보(평문, 암호문 등)
사진정보(평문, 암호문, 마스킹된 평문 등)

용량 문제로 발생하는 이슈

용량이 너무 크게 되면 event exceeded the permitted size 에러가 발생합니다.
Failed to submit message: 'HTTP 400: {"accepted":3,"errors":[{"message":"event exceeded the permitted size.","document":"{\\"transaction\\": {\\"id\\": \\"51ef4a9a32461bcb\\", \\"trace_id\\": \\"8b65f8f428211d0e9e7368ee2bac3f40\\", \\"name\\": \\"POST api.versioned.v1.kb.account.views.KBAccountDetailViewSet\\", \\"type\\": \\"request\\", \\"duration\\": 1966.8769729999997, \\"result\\": \\"HTTP 2xx\\", \\"timestamp\\": 1652245425453835, \\"sampled\\": true, \\"span_count\\": {\\"started\\": 3, \\"dropped\\": 0}, \\"context\\" ...
Bash
복사

2. Django - elasticapm

django에서 elk로 데이터를 넘길 때 elasticapm 라이브러리를 사용합니다. 해당 라이브러리를 사용할 때 request body 내의 특정 필드를 마스킹할 수 있도록 지원해주는 방법이 있습니다.

2.1 SANITIZE_FIELD_NAMES

elasticapm 내의 세팅으로 SANITIZE_FIELD_NAMES가 있습니다. 해당 필드에 대한 기본 정보는 다음과 같습니다.

default

BASE_SANITIZE_FIELD_NAMES = [ "authorization", "password", "secret", "passwd", "token", "api_key", "access_token", "sessionid", ]
Python
복사

settings.py

# base.py 혹은 settings.py 내에 추가할 수 있음 ADITIONAL_SANITIZE_FIELD_NAMES = [ "front_image", "encoded_image", "license_no", "customer_no", "photo_info" ] BASE_SANITIZE_FIELD_NAMES += ADITIONAL_SANITIZE_FIELD_NAMES
Python
복사

processors.py

elastic/processors.py 파일 내에 있는 함수 중 sanitize_http_request_body 함수에서 선언된 sanitize_field_names에 대한 내용을 확인하고 해당 필드 내용을 필터링합니다. 해당 함수는 불필요한 필드의 경우 body에서 제외하는 과정입니다.
단, 해당 함수에서는 JSON body의 데이터는 sanitize 할 수 없습니다.
# elastic.processor @for_events(ERROR, TRANSACTION) def sanitize_http_request_body(client, event): """ Sanitizes http request body. This only works if the request body is a query-encoded string. Other types (e.g. JSON) are not handled by this sanitizer. :param client: an ElasticAPM client :param event: a transaction or error event :return: The modified event """ try: body = force_text(event["context"]["request"]["body"], errors="replace") except (KeyError, TypeError): return event if "=" in body: sanitized_query_string = _sanitize_string( body, "&", "=", sanitize_field_names=client.config.sanitize_field_names ) event["context"]["request"]["body"] = sanitized_query_string return event
Python
복사

2.2 Custom sanitize function

DRF에서 JSON body로 데이터를 넘겨주는 형태라면 커스텀 함수를 만들어 processors를 추가해주어야 합니다.

customize - processors.py

JSON body 데이터를 load하여 데이터를 컨트롤합니다. 필드를 삭제하는 것보다는 해당 필드 내의 데이터를 [REDACTED] 하는 방법으로 변경하는 코드입니다.
# elasticapm 5.x # ./common/elasticapm/processors.py import json from elasticapm.utils.encoding import force_text from elasticapm.processors import for_events from elasticapm.conf.constants import ERROR, TRANSACTION @for_events(ERROR, TRANSACTION) def sanitize_http_request_json_body(client, event): """ Sanitizes http request body. This only works if the request body is a query-encoded string. Other types (e.g. JSON) are not handled by this sanitizer. :param client: an ElasticAPM client :param event: a transaction or error event :return: The modified event """ try: body = json.loads(force_text(event["context"]["request"]["body"], errors="replace")) except (KeyError, TypeError): return event for field in client.config.sanitize_field_names: if body.get(field): body[field] = "[REDACTED]" event["context"]["request"]["body"] = json.dumps(body) return event
Python
복사
혹은 다음과 같이 사용할 수 있습니다.
# elasticapm 6.x import json from elasticapm.utils.encoding import force_text from elasticapm.processors import for_events from elasticapm.conf.constants import ERROR, TRANSACTION @for_events(ERROR, TRANSACTION) def sanitize_http_request_json_body(client, event): """ Sanitizes http request body. This only works if the request body is JSON objects :param client: an ElasticAPM client :param event: a transaction or error event :return: The modified event """ try: body = json.loads(force_text(event["context"]["request"]["body"], errors="replace")) event["context"]["request"]["body"] = json.dumps( varmap(_sanitize, body, sanitize_field_names=client.config.sanitize_field_names) ) except (KeyError, TypeError): pass
Python
복사

customize - base.py

Django 관련하여 APM Agent 정보를 입력하고, 추가적으로 SANITIZE_FIELD_NAMES에 데이터를 추가해주어야 합니다. 또한 위에 선언한 processor를 선언해주어야 합니다.
# base.py 혹은 settings.py 내에 추가할 수 있음 ADITIONAL_SANITIZE_FIELD_NAMES = [ "front_image", "encoded_image", "license_no", "customer_no", "photo_info" ] BASE_SANITIZE_FIELD_NAMES += ADITIONAL_SANITIZE_FIELD_NAMES HARDCODED_PROCESSORS += ["common.elasticapm.processors.sanitize_http_request_json_body"]
Python
복사

3. Kibana

floatFirstTOC: right
YAML
복사
oopy