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)