[시큐어코딩 가이드] 2-1-6. 위험한 형식 파일 업로드

위험한 형식 파일 업로드

정의

  • 서버 측에서 실행 가능한 스크립트파일(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(한국인터넷진흥원)