본문 바로가기

app/python

pickle

1. pickle?

직렬화 : 개체를 메모리, DB, 파일에 저장하기 위해 개체를 바이트 스트림으로 변환하는 프로세스

 

언어마다 직렬화를 표현하는 단어가 다르다.

java : 직렬화

python : 피클링

perl.. etc : 마샬링

 

사실 마샬링이 가장 큰 개념이다. 직렬화하는 모든 과정을 뜻하는 말이다.

 

 

2. 난 json 잘 쓰고 있는데?

pickle vs json

pickle과  JSON (JavaScript Object Notation) 에는 근본적인 차이가 있다.

  • JSON은 텍스트 직렬화 형식(유니코드 텍스트를 출력하지만, 대개는 utf-8으로 인코딩)인 반면, pickle은 바이너리 직렬화 형식
  • JSON은 사람이 읽을 수 있지만, pickle은 그렇지 않다
  • JSON은 상호 운용이 가능하며 파이썬 생태계 외부에서 널리 사용되는 반면, 피클은 파이썬으로만 한정됨
  • JSON은 기본적으로 파이썬 내장형 일부만 표시할 수 있으며 사용자 정의 클래스는 표시할 수 없다.
  • pickle과 달리 JSON의 역직렬화는 그 자체로 임의 코드 실행 취약점을 만들 수 없다.

 

 

3. pickle 사용법

3-1 변수 저장

1
2
3
4
5
6
7
data = {
    'a': [12.034+6j],
    'b': ("character string", b"byte string"),
    'c': {NoneTrueFalse}
}
pickle.dumps(data)
# b'\x80\x03}q\x00(X\x01\x00\x00\x00aq\x01]q\x02(K\x01G@\x00\x00\x00\x00\x00\x00\x00K\x03cbuiltins\ncomplex\nq\x03G@\x10\x00\x00\x00\x00\x00\x00G@\x18\x00\x00\x00\x00\x00\x00\x86q\x04Rq\x05eX\x01\x00\x00\x00bq\x06X\x10\x00\x00\x00character stringq\x07C\x0bbyte stringq\x08\x86q\tX\x01\x00\x00\x00cq\ncbuiltins\nset\nq\x0b]q\x0c(\x89\x88Ne\x85q\rRq\x0eu.'
cs

 

3-2  함수 , 클래스

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
def test(val):
    print("이것은", val, "이다")
 
= pickle.dumps(test)
= pickle.loads(p)
t("발"# 이것은 발 이다
 
# ======================================================
 
class Bar:
    def __init__(self, data=None):
        print("init start", data)
 
    def __call__(self):
        print("call")
        
= pickle.dumps(Bar)
 
 
data = pickle.loads(p)
data()                # init start None
data("bar class")()   # init start bar class
 
cs

 

 

3-3 파일 저장

1
2
3
4
5
6
7
8
9
10
11
12
13
class Foo:
    attr = 'A class attribute'
 
 
# save
with open('data.pickle''wb'as f:
    pickle.dump(Foo, f)
 
 
# load
with open('data.pickle''rb'as f:
    data = pickle.load(f)
    print(data.attr) # A class attribute
cs

 

다음 형태를 pickle 할 수 있다

  • None, True와 False
  • 정수, 실수, 복소수
  • 문자열, 바이트 열, 바이트 배열(bytearray)
  • 피클 가능한 객체만 포함하는 튜플, 리스트, 집합과 딕셔너리
  • 모듈의 최상위 수준에서 정의된 함수
  • 모듈의 최상위 수준에서 정의된 내장 함수
  • 모듈의 최상위 수준에서 정의된 클래스

한마디로 그냥 인스턴스 할 수 있는 모든 것이 다 됨.

 

3. 머신러닝에서의 pickle

실습 파일

test_LinearRegression.ipynb
0.00MB

 

4. pickle 사용 시 주의할 점

  • pickle에서 실행 중인 객체와 설치된 패키지의 버전이 동일해야 한다.
  • 안전하지 않은 역직렬화

실행 가능한 코드로 전달되기 때문에 안전하지 않은 역직렬화 취약점은 결국 Injection 공격을 허용한다.

아무 파일이나 pickle 실행하면 망하는 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import os
import pickle
 
 
class Exploit(object):
    def __reduce__(self):
        return (os.system, ('whoami',))
 
 
def serialize_exploit():
    shellcode = pickle.dumps(Exploit())
    return shellcode
 
 
def insecure_deserialization(exploit_code):
    pickle.loads(exploit_code)
 
 
shellcode = serialize_exploit()
insecure_deserialization(shellcode) # uiandwe
cs

 

 

번외 1

python에도 marshal을 지원하지만, 내부 파이썬 객체 직렬화 즉. pyc에서 사용하며, pickle에 비해 적용 가능한 범위가 적어 사용을 권장하지 않는다.

1
2
3
4
import marshal
= marshal.dumps(user) # b'\xfb\xda\x04namez\tseung jae\xda\x08positionz\x10Backend Engineer0'
 
marshal.loads(m) # {'name': 'seung jae', 'position': 'Backend Engineer'}
cs
번외 2

python에서 변수를 파일로 저장할 때는 shelve 패키지를 지원하지만, 내부적으로 pickle를 사용하고 있기에, 딱히 구분해서 쓸 필요는 없다. shelve는 객체가 아닌 변수만 가능한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import shelve
 
= shelve.open("shelve_test")  
                           
d['xx'= [012]        
d['xx'].append(3)          
 
temp = d['xx']             
temp.append(5)             
d['xx'= temp             
 
d.close()
 
 
# =========================================
 
= shelve.open("shelve_test")  
for key in d.keys():
    print(key, d[key])
 
# xx [0, 1, 2, 5]
cs

 

참조

https://www.acunetix.com/blog/articles/what-is-insecure-deserialization/

https://docs.python.org/ko/3/library/pickle.html

https://docs.python.org/ko/3/library/marshal.html#module-marshal

https://statkclee.github.io/nlp2/nlp-ml-deployment.html