[시큐어코딩 가이드] 신뢰할 수 없는 데이터의 역직렬화

 

 

 

■ 코드오류

 

타입 변환 오류, 자원(메모리 등)의 부적절한 반환 등과 같이 개발자가 범할 수 있는 코딩 오류로 인해 유발되는 보안약점이다.

 

 

신뢰할 수 없는 데이터의 역직렬화

 

■ 개요

 

직렬화(Serialization)는 프로그램에서 특정 클래스의 현재 인스턴스 상태를 다른 서버로 전달하기 위해 클래스의 인스턴스 정보를 바이트 스트림으로 복사하는 작업으로, 메모리상에서 실행되고 있는 객체의 상태를 그대로 복제하여 파일로 저장하거나 수신 측에 전달하게 된다.


역직렬화(Deserialization)는 반대 연산으로 바이너리 파일(Binary File) 이나 바이트 스트림(Byte Stream)으로부터 객체 구조로 복원하게 된다. 이 때, 송신자가 네트워크를 이용하여 직렬화된 정보를 수신자에게 전달하는 과정에서 공격자가 전송 또는 저장된 스트림을 조작할 수 있는 경우에는 신뢰할 수 없는 역직렬화를 이용하여 무결성 침해, 원격 코드 실행, 서비스 거부 공격 등이 발생 할 수 있는 보안약점이다.


Python에서는 pickle 모듈을 통해 직렬화 및 역직렬화를 지원하고 각각 pickle, unpickle로 명칭한다. pickle 모듈은 데이터 변조에 대한 검증 과정이 없기 때문에 임의의 코드를 실행하는 악의적인 pickle 데이터를 구성할 수 있어 pickle을 사용하여 역직렬화 하는 경우 hmac으로 데이터에 서명하거나, json 모듈을 사용하는 것을 고려해야 한다.

 

 

■ 안전한 코딩 기법

 

초기화되지 않은 스택 메모리 영역의 변수는 임의 값이라 생각해서 대수롭지 않게 생각할 수 있으나 사실은 이전 함수에서 사용되었던 내용을 포함하고 있다. 공격자는 이러한 약점을 사용하여 메모리에 저장되어 있는 값을 읽거나 특정 코드를 실행할 수 있다. 모든 변수를 사용 전에 반드시 올바른 초기 값을 할당함으로서 이러한 문제를 예방한다.


신뢰할 수 없는 데이터를 역직렬화 하지 않도록 응용프로그램을 구성한다. 민감정보 또는 중요정보를 전송 시 암호화 통신을 적용하지 못하는 경우, 송신 측에서 서명을 추가하고 수신 측에서 서명을 확인하여 데이터의 무결성을 검증한다.

 

또는, 신뢰할수 있는 데이터의 식별을 위해 역직렬화 대상의 데이터가 사전에 검증된 클래스(Class)만을 포함하는지 검증하거나, 제한된 실행 권한을 구성하여 역직렬화 코드를 실행한다.

 

 

코드예제

 

다음 예제는 알 수 없는 사용자로부터 입력 받은 코드를 역직렬화 하고 있는데, 이와 같은 코드는 개발자가 의도하지 않은 임의 코드가 실행될 수 있다.

 

안전하지 않은 코드의 예
import pickle
from django.shortcuts import render

def load_user_object(request):
userinfo = bytes(request.POST.get('userinfo', ''), encoding = "utf-8")
# 사용자로부터 입력받은 알 수 없는 데이터를 직렬화
user_obj = pickle.loads(userinfo)
return render(request, '/load_user_obj.html', {'obj':user_obj})

 

아래 예제는 사용자로부터 전달받은 데이터를 hmac을 이용하여 안전한 사용자로부터 온 것인지 검증한 후 역직렬화 하고 있다.

안전한 코드의 예
import hmac
import hashlib
import pickle
from django.shortcuts import render

def load_user_object(request):
hash_pickle = bytes(request.POST.get('hash_pickle', ''), encoding = "utf-8")
userinfo = bytes(request.POST.get('userinfo', ''), encoding = "utf-8")
# HMAC 검증을 위한 비밀키는 안전하게 저장하여 사용
m = hmac.new(key=b’secret_key’, digestmod=hashlib.sha512)
m.update(userinfo)
# 전달받은 해시값(hash_pickle)과 직렬화 데이터(userinfo)의
# 해시값을 비교하여 검증
if hmac.compare_digest(m.digest(), hash_pickle):
user_obj = pickle.loads(userinfo_pickle)
return render(request, '/load_user_obj.html', {'obj':user_obj})
else:
return render(request, '/error.html', {'error':'직렬화 오류입니다.'})

 

이 외에도 내부의 데이터만을 필요로 하는 경우에는 JSON과 같은 텍스트 형태의 안전한 직렬화 형식을 사용하는 것이 옳다.

 

 

댓글

Designed by JB FACTORY