파일 시그니처(매직 바이트) 목록
파일의 앞부분 몇 바이트에 들어 있는 시그니처(매직 바이트) 목록입니다. 파일 타입 판정과 검증 구현에 참고하세요.
매직 바이트란
많은 파일 형식은 파일 앞부분에 고정된 바이트 열(시그니처)을 가집니다. 이를 매직 바이트 또는 파일 시그니처라고 부릅니다. OS와 애플리케이션은 이 시그니처를 보고 파일 종류를 판정합니다. 확장자는 쉽게 위장할 수 있지만 매직 바이트는 바이너리 수준의 식별자이므로 파일 검증의 신뢰성이 높아집니다.
Linux의 file 명령이나 PHP의 finfo 클래스는 이 구조를 이용해 MIME 타입을 판정합니다.
이미지 파일
| 형식 | 확장자 | 매직 바이트(16진수) | ASCII 표현 | 오프셋 |
|---|---|---|---|---|
| PNG | .png | 89 50 4E 47 0D 0A 1A 0A | ‰PNG\r\n\x1a\n | 0 |
| JPEG | .jpg/.jpeg | FF D8 FF | — | 0 |
| GIF87a | .gif | 47 49 46 38 37 61 | GIF87a | 0 |
| GIF89a | .gif | 47 49 46 38 39 61 | GIF89a | 0 |
| WebP | .webp | 52 49 46 46 ?? ?? ?? ?? 57 45 42 50 | RIFF????WEBP | 0 |
| BMP | .bmp | 42 4D | BM | 0 |
| TIFF(리틀 엔디언) | .tiff/.tif | 49 49 2A 00 | II*\0 | 0 |
| TIFF(빅 엔디언) | .tiff/.tif | 4D 4D 00 2A | MM\0* | 0 |
| AVIF / HEIF | .avif/.heif | 66 74 79 70 | ftyp | 4 |
| ICO | .ico | 00 00 01 00 | — | 0 |
| SVG | .svg | <?xml 또는 <svg | 텍스트 | 0(BOM 없음) |
문서·아카이브
| 형식 | 확장자 | 매직 바이트(16진수) | 비고 |
|---|---|---|---|
25 50 44 46 2D | %PDF- | ||
| ZIP | .zip | 50 4B 03 04 | PK\x03\x04 |
| ZIP(빈 파일) | .zip | 50 4B 05 06 | — |
| GZIP | .gz | 1F 8B | — |
| 7-Zip | .7z | 37 7A BC AF 27 1C | 7z\xBC\xAF'\x1C |
| RAR4 | .rar | 52 61 72 21 1A 07 00 | Rar!\x1A\x07\x00 |
| RAR5 | .rar | 52 61 72 21 1A 07 01 00 | — |
| TAR | .tar | 75 73 74 61 72 | ustar(오프셋 257) |
| DOCX / XLSX / PPTX | .docx 등 | 50 4B 03 04 | ZIP 기반(확장자로 구분) |
| XLS(구형식) | .xls | D0 CF 11 E0 A1 B1 1A E1 | OLE2 컨테이너 |
| DOC(구형식) | .doc | D0 CF 11 E0 A1 B1 1A E1 | OLE2 컨테이너 |
텍스트·데이터 형식
| 형식 | 확장자 | 매직 바이트 / BOM | 비고 |
|---|---|---|---|
| UTF-8 BOM 있음 | .txt/.csv 등 | EF BB BF | BOM(Byte Order Mark) |
| UTF-16 LE BOM | .txt 등 | FF FE | 리틀 엔디언 |
| UTF-16 BE BOM | .txt 등 | FE FF | 빅 엔디언 |
| UTF-32 LE BOM | .txt 등 | FF FE 00 00 | — |
| JSON(일반) | .json | 7B 또는 5B | { 또는 [ |
| CSV(일반) | .csv | 텍스트(BOM 없음) | 시그니처 없음 |
| XML | .xml | 3C 3F 78 6D 6C | <?xml |
음성·동영상
| 형식 | 확장자 | 매직 바이트(16진수) | 비고 |
|---|---|---|---|
| MP3 | .mp3 | FF FB / FF F3 / FF F2 | 또는 ID3 태그: 49 44 33 |
| MP4 / M4A / M4V | .mp4 등 | 66 74 79 70 | ftyp(오프셋 4) |
| WAV | .wav | 52 49 46 46 ?? ?? ?? ?? 57 41 56 45 | RIFF????WAVE |
| OGG | .ogg/.ogv | 4F 67 67 53 | OggS |
| FLAC | .flac | 66 4C 61 43 | fLaC |
| AVI | .avi | 52 49 46 46 ?? ?? ?? ?? 41 56 49 20 | RIFF????AVI |
| MKV / WebM | .mkv/.webm | 1A 45 DF A3 | EBML 헤더 |
실행 파일·기타
| 형식 | 확장자 | 매직 바이트(16진수) | 비고 |
|---|---|---|---|
| ELF(Linux 실행 파일) | 없음/.elf | 7F 45 4C 46 | \x7FELF |
| PE(Windows 실행 파일) | .exe/.dll | 4D 5A | MZ |
| Mach-O(macOS 실행 파일) | 없음 | CF FA ED FE | 64bit |
| PHP 스크립트 | .php | 3C 3F 70 68 70 | <?php |
| SQLite | .sqlite/.db | 53 51 4C 69 74 65 20 66 6F 72 6D 61 74 20 33 00 | SQLite format 3\0 |
| ISO | .iso | 43 44 30 30 31 | 오프셋 32769: CD001 |
PHP에서의 매직 바이트 검증 구현 예
['offset' => 0, 'bytes' => "\x89PNG\r\n\x1a\n", 'length' => 8],
'jpg' => ['offset' => 0, 'bytes' => "\xFF\xD8\xFF", 'length' => 3],
'gif' => ['offset' => 0, 'bytes' => "GIF8", 'length' => 4],
'pdf' => ['offset' => 0, 'bytes' => "%PDF-", 'length' => 5],
'zip' => ['offset' => 0, 'bytes' => "PK\x03\x04", 'length' => 4],
'exe' => ['offset' => 0, 'bytes' => "MZ", 'length' => 2],
];
if (!isset($signatures[$expectedType])) {
throw new \InvalidArgumentException("알 수 없는 파일 형식: $expectedType");
}
$sig = $signatures[$expectedType];
$handle = fopen($filePath, 'rb');
if ($sig['offset'] > 0) fseek($handle, $sig['offset']);
$header = fread($handle, $sig['length']);
fclose($handle);
if (substr($header, 0, $sig['length']) !== $sig['bytes']) {
throw new \RuntimeException("매직 바이트가 일치하지 않습니다 (기대: $expectedType)");
}
}
// 사용 예
validateMagicBytes($_FILES['upload']['tmp_name'], 'png');
echo "올바른 PNG 파일입니다";
관련 자료
- MIME 타입 목록 — 확장자와 Content-Type 대응표
- 손상된 파일 / 헤더 변조 파일 — 검증 테스트용
- 파일 검증 구현 체크리스트
❓ 자주 묻는 질문
확장자 검사만으로는 안 되는 이유는?
확장자는 파일 이름의 일부일 뿐이고 공격자가 자유롭게 바꿔 붙일 수 있기 때문입니다. shell.php 를 shell.jpg 로 이름만 바꾸면 확장자 검사는 통과합니다. 매직 바이트는 실제 데이터 선두를 보므로 개명으로는 속일 수 없습니다. 다만 이것도 선두 몇 바이트를 위장한 다중 형식 파일에는 약하므로 확장자, MIME, 매직 바이트 세 가지로 대조하세요.
PNG 와 JPEG 의 시그니처는 몇 바이트 읽으면 판정할 수 있나요?
PNG 는 8 바이트(89 50 4E 47 0D 0A 1A 0A), JPEG 는 선두 3 바이트(FF D8 FF)로 판정할 수 있습니다. 구현상 선두 12 바이트 정도 읽어 두면 주요 형식을 한꺼번에 판별할 수 있고 ZIP(50 4B 03 04)과 PDF(25 50 44 46)도 이 범위에 들어갑니다. 파일 전체를 메모리에 올릴 필요는 없습니다.
Office 문서나 APK 가 ZIP 으로 판정됩니다.
올바른 판정입니다. docx, xlsx, pptx, APK, JAR, EPUB 은 모두 ZIP 컨테이너이며 선두가 50 4B 03 04 입니다. 구별하려면 압축을 풀어 내용을 봐야 하고 docx 라면 [Content_Types].xml, APK 라면 AndroidManifest.xml 의 유무로 판정합니다. 매직 바이트만으로 docx 를 허용하고 APK 를 막을 수는 없습니다.