본문 바로가기

프로젝트

[소프티어부트캠프] 파일 업로드 & 공백 제거의 위험성

소프티어 부트캠프에서 파일 업로드 중, 특정 webp 파일이 깨지는 문제가 발생했다. 기존 코드 베이스는 다음과 같다.

StringBuilder partBodyStr = new StringBuilder();
char[] bodyBuffer = new char[1024];
while((br.read(bodyBuffer)) != -1) {    
    partBodyStr.append(bodyBuffer);
}
byte[] partBody = partBodyStr.toString().trim().getBytes(StandardCharsets.ISO_8859_1);
FormDataUtil.addFormData(req, partName, new FormItem(filename, partBody));

Stringbuilder로 파일 내용을 문자열로 만든 후, 앞뒤 공백을 제거하는 로직이 포함되어 있다. 이전에 폼 데이터를 읽어 partBodyStr.append 라인을 수행할 때 버퍼의 길이 1024 만큼 데이터를 모두 쓰는 문제가 있었기 때문에, 뒤에 붙은 공백을 제거하면 정상 결과가 나올 것이라 생각하고 코드를 작성했었다. 당연하지만, 이 코드가 문제였다.

업로드 전(좌) 업로드 후(우)

업로드 이후에 webp 파일이 깨지는 문제가 발생했다. 파일 크기가 크게 차이났다면 코드 전체를 뜯어 고치려고 했겠지만 두 파일은 딱 2byte 크기 정도만 차이가 났기 때문에, trim() 로직 때문이 아닐까 의심했다. 파일 자체에 포함되어 있던 0x00 또는 공백 문자들이 제거되었을 가능성이 높다고 생각했다.

이유를 알아보기 위해 두 파일을 hex editor를 이용하여 어떤 바이트 정보를 가지고 있는지 비교했다.

업로드 전(좌) 업로드 후(우)

예상한 대로 0x00 데이터가 소실되었다. 이진 데이터에 0x00 같은 것이 포함될 수 있다는 사실을 생각하지 못한 것이 원인이었다. 따라서, 이진 데이터가 망가지지 않도록 trim 대신 읽은 바이트 배열만큼만 데이터를 쓸 수 있도록 append 부분 코드를 수정했다.

StringBuilder partBodyStr = new StringBuilder();
char[] bodyBuffer = new char[1024];
int read;while((read = br.read(bodyBuffer)) != -1) {
    partBodyStr.append(bodyBuffer, 0, read);
}
byte[] partBody = partBodyStr.toString().getBytes(StandardCharsets.ISO_8859_1);
FormDataUtil.addFormData(req, partName, new FormItem(filename, partBody));

코드를 변경한 이후, 기존에 발생했던 바이트 정보 소실 문제가 해결되었다.


이진 데이터에는 0x00 등 문자열로 변경했을 때 공백 문자로 간주되는 데이터가 포함될 수 있다. 이 데이터는 이진 파일에서 의미있는 데이터일 수 있으므로, trim으로 필요 없는 공백을 제거하는 대신 이진 데이터를 정확한 길이만큼 가져올 수 있는 방법을 사용해야 한다.