위험한 형식 파일 업로드
정의
- 서버 측에서 실행 가능한 스크립트파일(asp, jsp, php 등)이 업로드 가능하고 이 파일을 공격자가 웹을 통해 직접 실행시킬수 있는 경우, 시스템 내부 명령어를 실행하거나 외부와 연결해 시스템을 제어할 수 있는 보안약점
- 공격자가 실행 가능한 파일을 서버에 업로드하면 eval()과 exec()를 같이 사용해 여러 변수들을 동적으로 값을 할당받아 실행될 수 있어 웹쉘 공격에 취약
안전한 코딩기법
- 특정 파일 유형만 허용하도록 화이트리스트 방식으로 파일 유형을 제한해야 한다.
- 파일 크기 및 파일 개수를 제한하여 시스템 자원 고갈 등으로 서비스 거부 공격이 발생하지 않도록 제한해야 한다.
- 업로드 된 파일을 저장할 경우에는 최소 권한만 부여하는 것이 안전하다.
코드예제
업로드 할 파일 개수, 크기, 확장자 등의 유효성 검사를 하지 않고 파일 시스템에 그대로 저장할 경우 공격자에 의해 악성코드, 쉘코드 등 위험한 형식의 파일이 시스템에 업로드 될 수 있습니다. 다음은 안전하지 않은 코드예제입니다.
#위험한 형식 파일 업로드(안전X)
def file_upload_bad(request):
if request.FILES['upload_file']:
upload_file = request.FILES['upload_file']
fs = FileSystemStorage(location='media/screenshot', base_url='media/screenshot')
filename = fs.save(upload_file.name, upload_file)
return render(request, 'success.html', {'data': filename})
return render(request, 'error.html', {'msg': '파일 업로드 실패'})
다음 코드는 안전한 코드예제로, 업로드 하는 파일의 개수, 크기, 파일 확장자 등을 검사해 업로드를 제한하고 있습니다.
#위험한 형식 파일 업로드(안전O)
FILE_COUNT_LIMIT = 5
FILE_SIZE_LIMIT = 5242880
WHITE_LIST_EXT = [
'.jpg',
'.jpeg'
]
filename_list = []
def file_upload_good(request):
#파일 개수 제한
if len(request.FILES) == 0 or len(request.FILES) > FILE_COUNT_LIMIT:
return render(request, 'error.html', {'msg': '파일 개수 초과'})
for filename, upload_file in request.FILES.items():
#파일 타입 확인
if upload_file.content_type != 'image/jpeg':
return render(request, 'error.html', {'msg': '파일 타입 오류'})
#파일 크기 제한
if upload_file.size > FILE_SIZE_LIMIT:
return render(request, 'error.html', {'msg': '파일사이즈 오류'})
#파일 확장자 검사
file_name, file_ext = os.path.splitext(upload_file.name)
if file_ext.lower() not in WHITE_LIST_EXT:
return render(request, 'error.html', {'msg': '파일 타입 오류'})
fs = FileSystemStorage(location='media/screenshot', base_url='media/screenshot')
for i in request.FILES.values():
filename = fs.save(i.name, i)
filename_list.append(filename)
return render(request, "success.html", {"data": filename_list})
입력데이터 검증 및 표현/위험한 형식 파일 업로드
[참고문헌] Python 시큐어코딩 가이드(2022) / KISA(한국인터넷진흥원)