Series bảo mật trong ASP.NET MVC - 4: Upload các tệp tin có hại
Qua 3 bài trước chúng ta đã từng tìm hiểu về cách bảo vệ tất cả các trường nhập liệu. Nhưng chúng ta đã bỏ qua một trường chính là trường File Upload mà chúng ta cần bảo vệ và kiểm tra kỹ càng, control này bị hầu hết các hacker khai thác để upload các tệp tin có hại. Các hacker có thể thay đổi đuôi của tệp tin (ví dụ tuto.exe sang tuto.jpeg] sau đó các đoạn mã có hạo có thể được upload như một file ảnh. Hầu hết các developer chỉ nhìn vào đuôi file và lưu chúng vào thư mục hoặc database nhưng thực tế có thể là loại file khác.

Giải pháp:-
- Đầu tiên chúng ta cần validate file tải lên.
- Thứ 2 là chỉ cho phép truy cập các file extension cụ thể
- Kiểm tra file header.
Đầu tiên thêm mới các file upload vào view.
Thêm upload control

Chúng ta đã thêm file upload control vào View sau đó validate nó ở nút submit.
Validate file trong action method
Đầu tiên chúng ta kiểm tra thuộc tính Content-Length nếu là 0 sau đó chúng ta không upload file lên.
Nếu Content-Length lớn hơn 0 thì chúng ta sẽ đọc tên file, loại file và kích thước file.
[HttpPost]
[ValidateAntiForgeryToken]
publicActionResult Index(EmployeeDetailEmployeeDetail)
{
if (ModelState.IsValid)
{
HttpPostedFileBase upload = Request.Files["upload"];
if (upload.ContentLength == 0)
{
ModelState.AddModelError("File", "Please Upload your file");
}
elseif(upload.ContentLength > 0)
{
stringfileName = upload.FileName; // getting File Name
stringfileContentType = upload.ContentType; // getting ContentType
byte[] tempFileBytes = newbyte[upload.ContentLength]; // getting filebytes
var data = upload.InputStream.Read(tempFileBytes, 0, Convert.ToInt32(upload.ContentLength));
var types = MvcSecurity.Filters.FileUploadCheck.FileType.Image; // Setting Image type
var result = FileUploadCheck.isValidFile(tempFileBytes, types, fileContentType); // Validate Header
if (result == true)
{
intFileLength = 1024 * 1024 * 2; //FileLength 2 MB
if (upload.ContentLength > FileLength)
{
ModelState.AddModelError("File", "Maximum allowed size is: " + FileLength + " MB");
}
else
{
stringdemoAddress = Sanitizer.GetSafeHtmlFragment(EmployeeDetail.Address);
dbcon.EmployeeDetails.Add(EmployeeDetail);
dbcon.SaveChanges();
return View();
}
}
}
}
return View(EmployeeDetail);
}
Các validate cơ bản đã xong, hãy validate file upload được viết trong một class static với tên là FileUploadCheckin, class này có cá phương thức để kiểm tra các loại file khác nhau. Tôi sẽ chỉ cho bạn làm sao để validate các file image và chỉ cho phép file image mà thôi.
FileUploadCheck Class

Ảnh chụp trên có một enum là ImageFileExtension chứa danh sách các đuôi cho phép định dạng ảnh và các file type cho phép.
private enum ImageFileExtension
{
none = 0,
jpg = 1,
jpeg = 2,
bmp = 3,
gif = 4,
png = 5
}
public enum FileType
{
Image = 1,
Video = 2,
PDF = 3,
Text = 4,
DOC = 5,
DOCX = 6,
PPT = 7,
}
Nếu đã qua được các validate căn bản chúng ta sẽ gọi phương thức ValidFileMethod theo byte, loại file, FileContentType
public static bool isValidFile(byte[] bytFile, FileType flType, String FileContentType)
{
bool isvalid = false;
if (flType == FileType.Image)
{
isvalid = isValidImageFile(bytFile, FileContentType);//we are going call this method
}
else if (flType == FileType.Video)
{
isvalid = isValidVideoFile(bytFile, FileContentType);
}
else if (flType == FileType.PDF)
{
isvalid = isValidPDFFile(bytFile, FileContentType);
}
return isvalid;
}
Sau khi gọi phương thức isValidFile sẽ gọi dựa trên loại file.
Nếu loại file là image thì sẽ gọi đến phương thức isValidImageFile, nếu là video thì sẽ gọi phương thức isValidVideoFile, và tương tự như thế với PDF.
Dưới đây là đoạn code kiểm tra của phương thức isValidImageFile
Trong phương thức này chúng ta sẽ giới hạn các đuôi cho phép upload hình ảnh như [jpg, jpeg, png, bmp, gif]
Làm việc với phương thức isValidImageFile
Khi chúng ta pass qua bytes và FileContentType phương thức này sẽ kiểm tra FileContentType
Sau đó nó sẽ kiểm tra các bytes quy định header của file được tải lên đúng với loại file.
public static bool isValidImageFile(byte[] bytFile, String FileContentType)
{
bool isvalid = false;
byte[] chkBytejpg = { 255, 216, 255, 224 };
byte[] chkBytebmp = { 66, 77 };
byte[] chkBytegif = { 71, 73, 70, 56 };
byte[] chkBytepng = { 137, 80, 78, 71 };
ImageFileExtensionimgfileExtn = ImageFileExtension.none;
if (FileContentType.Contains("jpg") | FileContentType.Contains("jpeg"))
{
imgfileExtn = ImageFileExtension.jpg;
}
else if (FileContentType.Contains("png"))
{
imgfileExtn = ImageFileExtension.png;
}
else if (FileContentType.Contains("bmp"))
{
imgfileExtn = ImageFileExtension.bmp;
}
else if (FileContentType.Contains("gif"))
{
imgfileExtn = ImageFileExtension.gif;
}
if (imgfileExtn == ImageFileExtension.jpg || imgfileExtn == ImageFileExtension.jpeg)
{
if (bytFile.Length >= 4)
{
int j = 0;
for (Int32 i = 0; i <= 3; i++)
{
if (bytFile[i] == chkBytejpg[i])
{
j = j + 1;
if (j == 3)
{
isvalid = true;
}
}
}
}
}
if (imgfileExtn == ImageFileExtension.png)
{
if (bytFile.Length >= 4)
{
int j = 0;
for (Int32 i = 0; i <= 3; i++)
{
if (bytFile[i] == chkBytepng[i])
{
j = j + 1;
if (j == 3)
{
isvalid = true;
}
}
}
}
}
if (imgfileExtn == ImageFileExtension.bmp)
{
if (bytFile.Length >= 4)
{
int j = 0;
for (Int32 i = 0; i <= 1; i++)
{
if (bytFile[i] == chkBytebmp[i])
{
j = j + 1;
if (j == 2)
{
isvalid = true;
}
}
}
}
}
if (imgfileExtn == ImageFileExtension.gif)
{
if (bytFile.Length >= 4)
{
int j = 0;
for (Int32 i = 0; i <= 1; i++)
{
if (bytFile[i] == chkBytegif[i])
{
j = j + 1;
if (j == 3)
{
isvalid = true;
}
}
}
}
}
return isvalid;
}
Gọi phương thức isValid file
Chúng ta sẽ gọi FileUploadCheck.isValidFile và đưa tham số File Bytes, Types, FileContentType.
Phương thức này sẽ trả về giá trị Boolean nếu file validate true và ngược lại là false.
string fileName = upload.FileName; // getting File Name
string fileContentType = upload.ContentType; // getting ContentType
byte[] tempFileBytes = new byte[upload.ContentLength]; // getting filebytes
var data = upload.InputStream.Read(tempFileBytes, 0, Convert.ToInt32(upload.ContentLength));
var types = MvcSecurity.Filters.FileUploadCheck.FileType.Image; // Setting Image type
var result = FileUploadCheck.isValidFile(tempFileBytes, types, fileContentType); //Calling isValidFile method
Sau khi hiểu đoạn code bạn sẽ nhìn một demo sau
Form dưới đây hiển thị nhập Employee với upload control
Chúng ta sẽ nhập các form dưới đây và chọn 1 file valid

Chọn một file .jpg và kiểm tra
Chọn 1 ảnh .jpg từ ổ đĩa


Debug phương thức
Trong phần này chúng ta đã đăng Employee với 1 file chúng ta có thể thấy các validate cơ bản.

Sau khi submit form để lưu dữ liệu, hiển thị giá trị thực của file đã upload.

Hình chụp của class FileUploadCheck trong khi phương thức isValidFile được gọi.
Trong phần này gọi phương thức isValidfile nó sẽ goi phương thức khác theo FileContentType.

Phương thức isValidImageFile trong khi kiểm tra các byte header.
Trong phương thức này nó sẽ kiểm tra các byte đầu tiên quy định kiểu file upload là image.

Trích nguồn từ: codeproject.com
Bài viết liên quan
Kẻ tấn công giả danh IT hỗ trợ trên Microsoft Teams, lợi dụng AD CS để đánh cắp dữ liệu doanh nghiệp
Microsoft điều tra hai vụ xâm nhập trong đó kẻ tấn công giả danh nhân viên IT trên Microsoft Teams để chiếm quyền điều khiển máy tính, sau đó khai thác cấu hình AD CS yếu để lấy chứng chỉ đăng nhập của tài khoản đặc quyền và domain controller.
Đọc thêm
Điểm tin tuần: Lỗ hổng CVE nghiêm trọng, botnet DDoS Linux, lừa đảo device-code và spyware Android
Tuần qua ghi nhận loạt sự kiện an ninh mạng lớn: Triều Tiên bị quy trách nhiệm vụ trộm 6,43 tỷ USD crypto từ Binance, nhóm Volt Typhoon dùng mạng Silent Train, cùng nhiều lỗ hổng CVE nghiêm trọng cần vá gấp.
Đọc thêm
VOLTA: Mã độc đánh cắp thông tin mới dùng AI để xếp hạng nạn nhân theo giá trị tài chính
Các nhà nghiên cứu bảo mật phát hiện mã độc VOLTA trên Windows dùng mô hình ngôn ngữ lớn như Claude, Kimi và Gemini để phân tích dữ liệu đánh cắp và xếp hạng nạn nhân theo giá trị kiếm tiền.
Đọc thêm
Lỗ hổng nghiêm trọng trong Langflow cho phép kẻ tấn công chưa xác thực chạy mã trên máy chủ
IBM công bố hai lỗ hổng nghiêm trọng trong Langflow, công cụ mã nguồn mở xây dựng AI agent, cho phép kẻ tấn công không cần tài khoản vượt xác thực hoặc chạy mã tùy ý; bản vá duy nhất là phiên bản 1.13.0.
Đọc thêm
eMudhra và lỗ hổng khóa yếu: dấu vết khai thác và tiến độ vá lỗi
Cloudflare ghi nhận khoảng 22.000 lần bắt tay TLS dùng khóa yếu của eMudhra, phần lớn bị chặn nhờ vá lỗi kịp thời; eMudhra tuyên bố rời thị trường CA công cộng.
Đọc thêm
Lỗ hổng CVSS 10.0 trong LXD và MicroCloud cho phép giả mạo client tin cậy mà không cần khóa riêng
Lỗ hổng nghiêm trọng CVE-2026-112816 trong LXD và MicroCloud của Canonical cho phép kẻ tấn công đăng nhập với tư cách client tin cậy chỉ với chứng chỉ công khai, đạt điểm CVSS tối đa 10.0. Canonical đã phát hành bản vá.
Đọc thêm
NetamLoader tấn công hệ thống Windows 10 của chính phủ châu Á qua .NET injection
Kaspersky phát hiện chuỗi lây nhiễm tinh vi nhắm vào bản Windows 10 tùy chỉnh dành riêng cho cơ quan chính phủ ở châu Á, sử dụng framework .NET có tên NetamLoader.
Đọc thêm
Cloudflare ghi nhận 18,2 triệu cuộc tấn công DDoS lớp mạng trong quý 3, tăng 38%
Cloudflare cho biết đã tự động chặn 18,2 triệu cuộc tấn công DDoS lớp mạng và 14,4 nghìn tỷ yêu cầu HTTP DDoS trong quý 3/2026, tương đương khoảng 8.226 cuộc tấn công mỗi giờ.
Đọc thêm
Lỗ hổng link chia sẻ ChatGPT có thể chạy mã độc trong phiên đăng nhập và đọc hội thoại riêng tư
Các nhà nghiên cứu tại Parameter phát hiện một link chia sẻ ChatGPT độc hại có thể chạy mã của kẻ tấn công trong phiên đăng nhập của nạn nhân, đọc hội thoại riêng tư. OpenAI đã vá lỗi và trả 5.000 USD tiền thưởng.
Đọc thêm
Tin tặc kết hợp ba lỗ hổng Ivanti Connect Secure để chiếm quyền root không cần xác thực
CISA đưa hai lỗ hổng Ivanti Connect Secure vào danh mục KEV sau khi bị khai thác thực tế, trong chuỗi khai thác ba lỗ hổng cho phép chiếm quyền root mà không cần xác thực.
Đọc thêm