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