Đặt vấn đề
Máy tính làm thế nào để biết file nào là ảnh, file nào là văn bản?
Nhìn qua câu hỏi thì có vẻ đơn giản, chẳng phải mỗi file điều có đuôi extension sao, dựa vào đó mà phân biệt thôi.
- Image thì sẽ có những đuôi như .png, .jpeg,…
- Text thì sẽ có những .txt…
Thế bạn đã bao giờ tải nhầm một file ảnh có đuôi .txt mà không thể mở nó chưa? Và với tư cách là một lập trình viên, tôi còn có thêm một số câu hỏi
- Nếu file đó không có phần đuôi extension thì sao ?
- Nếu người dùng chỉnh sửa đuôi của file ảnh thành file .txt thì khi upload lên server thì sao ?
Có nhiều vấn đề để chúng ta tìm hiểu hơn rồi đó phải không? Cùng mình khám phá nha!
Đơn giản mà nói thì đuôi extension không quyết định loại của file đó
Thật vậy, một file có thể không có đuôi extension và nếu có, cũng chưa chắc đuôi extension đó thể hiện đúng loại file.
Mức độ quan trọng của đuôi extension cũng tuỳ vào hệ điều hành.
- (optional) Nhìn chung thì hệ điều hành như linux sẽ chẳng quan tâm đuôi extension của file đâu, thay vào đó cứ file nào executable được là nó chạy à, nó sẽ nhìn vào header file, nội dung file để tìm kiếm những application phù hợp để thực thi file đó. Ví dụ những file có dòng đầu là
#!/bin/bash
đây là một cách đặc biệt để nói với hệ điều hành rằng bạn muốn mở file này bằng application nào.
- (mandatory) Còn một vài hệ điều hành hướng đến sự thuận tiện cho người dùng như Window thì đuôi extension sẽ có nhiều đất diễn hơn.
- File association (Registry): dựa vào đuôi extension, Window sẽ liên kết với các application cụ thể, và dùng application đó để mở file.
- Default programs: Window cũng cho phép bạn tuỳ chỉnh, với đuôi extension này thì sẽ được mở với application kia.
Magic number là gì ?
Đầu tiên, ta cần hình dung các file trông như thế nào, nó sẽ được lưu dưới dạng nhị phân như sau
0100 0111 0100 1001 0100 0110 ....
Thường thì phần đầu của file sẽ có những chuỗi byte đặc biệt, ta gọi nó là Magic number giúp các application chuyên dụng phân biệt được file đó là gì.
Cùng nhìn thêm 1 ví dụ về file PNG bên dưới nhá, sau khi xác định được file đó là gì thì phía sau chúng ta sẽ có các metadata khác nữa, cái metadata dài như thế nào, chứa cái gì thì sẽ tuỳ vào loại file đó.
💡 Metadata là dữ liệu mô tả thông tin chi tiết về dữ liệu, như phía trên thì là dữ liệu để mô tả cái file là ảnh và có những đặc tính gì.
Ví dụ về magic numbers của một vài file:
- PNG: 89 50 4E 47 0D 0A 1A 0A
- JPEG: FF D8 FF
- PDF: 25 50 44 46
- ZIP: 50 4B 03 04
MIME type là gì ?
Định nghĩa này thì chắc hẳn đa số anh em lập trình ta sẽ gặp nhiều.
MIME type (viết tắt của Multipurpose Internet Mail Extensions) là một chuỗi ký tự chuẩn được sử dụng để xác định định dạng của một tệp. Nó gồm hai phần: loại (type) và kiểu con (subtype), được phân tách bằng dấu gạch chéo (/).
Ví dụ:
image/png
: Hình ảnh định dạng PNGimage/jpeg
: Hình ảnh định dạng JPEGtext/plain
: Văn bản thuần túyapplication/pdf
: Tài liệu PDFvideo/mp4
: Video định dạng MP4
Cách hoạt động:
Khi bạn mở một tệp trên máy tính, hệ điều hành sẽ kiểm tra MIME type của tệp đó (thường được xác định dựa trên phần đuôi extension hoặc magic number) để tìm ứng dụng phù hợp để mở tệp. Tương tự, khi trình duyệt web tải xuống một tệp, nó sẽ xem MIME type để xác định cách xử lý tệp đó (hiển thị trực tiếp trong trình duyệt, tải xuống, hoặc mở bằng ứng dụng khác).
Tìm kiếm MIME type:
Bạn có thể tìm thấy danh sách đầy đủ các MIME type được IANA (Internet Assigned Numbers Authority) công nhận trên trang web của họ hoặc trên các nguồn khác như MDN Web Docs:
- https://www.iana.org/assignments/media-types/media-types.xhtml
- https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types
Bonus - Validate file
Validate file input là một tiến trình phức tạp, không có một phương pháp đơn lẽ nào hiệu quả! Chúng ta cần phải kết hợp nhiều phương thức lại với nhau.
-
Với những gì đã học được phía trên, khi kiểm tra định dạng của 1 file, chúng ta không chỉ nên kiểm tra đuôi extension, vì nó rất dễ bị bypass.
-
Kiểm tra MIME type sẽ đáng tin cậy hơn, những vẫn có lỗ hỏng.
Một request upload file thường sẽ có header chứa trường
Content-Type
để xác định MIME type của file đó. Kẻ tấn công có thể chỉnh sửa trường này, nên bạn hãy cẩn thận.
💡 Tôi đã thử kiểm tra thư viện multer, nó xài một thư viện là busboy, và việc check mimeType hoàn toàn dựa vào trường Content-Type
- Và cách bảo mật nhất là kiểm tra nội dung trong file, cụ thể là phần header của file để lấy phần magic number hay file signature, sau đó so sánh với danh sách những loại đáng tin cậy đã được công khai ở những link dưới đây, nếu không có trong đó thì quăng ra lỗi.
Kết bài
Tóm lại, việc xác định loại file không chỉ đơn thuần dựa vào đuôi extension mà là sự kết hợp của nhiều yếu tố như hệ điều hành, magic number và MIME type. Hiểu được chúng, bạn sẽ có thể xử lý file một cách chính xác và an toàn hơn.
Hy vọng bài viết này đã cung cấp cho bạn những kiến thức hữu ích về vấn đề này. Nếu bạn có bất kỳ câu hỏi hoặc góp ý nào, đừng ngần ngại để lại bình luận bên dưới!