What is the best way to validate the file format in file-upload control in ASP.NET?
Actually I want that user only upload files with specific format. Although I validate it by checking file name but I am looking for another solution to over come this.
Try the following code which reads the first 256 bytes from the file and return the mime type of the file using an internal dll (urlmon.dll).Then compare the mime type of your file and the returned mime type after parsing.
using System.Runtime.InteropServices; ...
[DllImport(#"urlmon.dll", CharSet = CharSet.Auto)]
private extern static System.UInt32 FindMimeFromData(
System.UInt32 pBC,
[MarshalAs(UnmanagedType.LPStr)] System.String pwzUrl,
[MarshalAs(UnmanagedType.LPArray)] byte[] pBuffer,
System.UInt32 cbSize,
[MarshalAs(UnmanagedType.LPStr)] System.String pwzMimeProposed,
System.UInt32 dwMimeFlags,
out System.UInt32 ppwzMimeOut,
System.UInt32 dwReserverd
);
public string getMimeFromFile(string filename)
{
if (!File.Exists(filename))
throw new FileNotFoundException(filename + " not found");
byte[] buffer = new byte[256];
using (FileStream fs = new FileStream(filename, FileMode.Open))
{
if (fs.Length >= 256)
fs.Read(buffer, 0, 256);
else
fs.Read(buffer, 0, (int)fs.Length);
}
try
{
System.UInt32 mimetype;
FindMimeFromData(0, null, buffer, 256, null, 0, out mimetype, 0);
System.IntPtr mimeTypePtr = new IntPtr(mimetype);
string mime = Marshal.PtrToStringUni(mimeTypePtr);
Marshal.FreeCoTaskMem(mimeTypePtr);
return mime;
}
catch (Exception e)
{
return "unknown/unknown";
}
}
But check the type in different browsers, since the mimetype might be different in different browsers.
Also this will give the exact mimetype even if you changed the extension by editing the name of the file.
Hope this helps you...
The only way to be sure is to actually parse the whole file according to the specification of the file format and check that everything fits.
If you want to do just basic check, most binary file formats have some form of header or magic number at their beginning, that you can check for.
You can use a component like Uploadify that limit's the user which type of files he can choose before uploading.
Related
We have allowed to upload .pdf, .doc, .docx, .xls, .xlsx and images like .jpg, .jpeg, .png files in ASP.net MVC 5 everything working fine but my security team raised security vulnerability. Security team changing the malicious file extension as allowed extensions and changing header with supporting file type header by tool before server hit and file get uploaded on server.
We are finding the solution in asp.net MVC C# but didn't get solution to stop uploading malicious file on server through my asp.net MVC C# application.
We have used following code and also used winista mime detect but unable to get proper solution. They are not returning proper mime type.
public class urlmonMimeDetect
{
[DllImport(#"urlmon.dll", CharSet = CharSet.Auto)]
private extern static System.UInt32 FindMimeFromData(
System.UInt32 pBC,
[MarshalAs(UnmanagedType.LPStr)] System.String pwzUrl,
[MarshalAs(UnmanagedType.LPArray)] byte[] pBuffer,
System.UInt32 cbSize,
[MarshalAs(UnmanagedType.LPStr)] System.String pwzMimeProposed,
System.UInt32 dwMimeFlags,
out System.UInt32 ppwzMimeOut,
System.UInt32 dwReserverd
);
public static string GetMimeFromFile(Stream fs)
{
byte[] buffer = new byte[256];
fs.Read(buffer, 0, 256);
try
{
System.UInt32 mimetype;
FindMimeFromData(0, null, buffer, 256, null, 0, out mimetype, 0);
System.IntPtr mimeTypePtr = new IntPtr(mimetype);
string mime = Marshal.PtrToStringUni(mimeTypePtr);
Marshal.FreeCoTaskMem(mimeTypePtr);
return mime;
}
catch (Exception e)
{
return "unknown/unknown";
}
}
}
I need to create files with path exceeding the MAX_PATH limit. What I expected to work is to shorten the already existing directory name like this:
public static String GetShortPathName(String longPath)
{
StringBuilder shortPath = new StringBuilder(longPath.Length + 1);
if (0 == GetShortPathName(longPath, shortPath, shortPath.Capacity))
{
throw new Exception("Path shortenning failed");
}
return shortPath.ToString();
}
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
private static extern Int32 GetShortPathName(String path, StringBuilder shortPath, Int32 shortPathLength);
and then use the result to create the new file like:
using (FileStream writeStream = File.Create(shortPath + newFile))
...
But it still throws PathTooLongException like before. Any idea why?
I know I can use Delimon Lib or other lib, but I just cannot figure out why it does not work.
Thanks a lot.
I want to send raw data to print, avoiding printer selection (fast print).
I am trying to use this helper provided by Microsoft: https://support.microsoft.com/en-us/kb/322091#top
However, when I call to the method:
RawPrinterHelper.SendStringToPrinter(pd.PrinterSettings.PrinterName, s);
My printer starts to works (makes some noise) but It never takes the white paper and starts to print.
I have tried it with my two printers and the behavior is the same in both printers. Also I discard the possibility that the printers are broken because I can print other documents.
What can be wrong?
Try this:
using System;
using System.Drawing;
using System.Drawing.Printing;
using System.IO;
using System.Runtime.InteropServices;
using System.Windows.Forms;
public class RawPrinterHelper
{
// Structure and API declarions:
[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Ansi)]
public class DOCINFOA
{
[MarshalAs(UnmanagedType.LPStr)] public string pDocName;
[MarshalAs(UnmanagedType.LPStr)] public string pOutputFile;
[MarshalAs(UnmanagedType.LPStr)] public string pDataType;
}
[DllImport("winspool.Drv", EntryPoint="OpenPrinterA", SetLastError=true, CharSet=CharSet.Ansi, ExactSpelling=true, CallingConvention=CallingConvention.StdCall)]
public static extern bool OpenPrinter([MarshalAs(UnmanagedType.LPStr)] string szPrinter, out IntPtr hPrinter, IntPtr pd);
[DllImport("winspool.Drv", EntryPoint="ClosePrinter", SetLastError=true, ExactSpelling=true, CallingConvention=CallingConvention.StdCall)]
public static extern bool ClosePrinter(IntPtr hPrinter);
[DllImport("winspool.Drv", EntryPoint="StartDocPrinterA", SetLastError=true, CharSet=CharSet.Ansi, ExactSpelling=true, CallingConvention=CallingConvention.StdCall)]
public static extern bool StartDocPrinter( IntPtr hPrinter, Int32 level, [In, MarshalAs(UnmanagedType.LPStruct)] DOCINFOA di);
[DllImport("winspool.Drv", EntryPoint="EndDocPrinter", SetLastError=true, ExactSpelling=true, CallingConvention=CallingConvention.StdCall)]
public static extern bool EndDocPrinter(IntPtr hPrinter);
[DllImport("winspool.Drv", EntryPoint="StartPagePrinter", SetLastError=true, ExactSpelling=true, CallingConvention=CallingConvention.StdCall)]
public static extern bool StartPagePrinter(IntPtr hPrinter);
[DllImport("winspool.Drv", EntryPoint="EndPagePrinter", SetLastError=true, ExactSpelling=true, CallingConvention=CallingConvention.StdCall)]
public static extern bool EndPagePrinter(IntPtr hPrinter);
[DllImport("winspool.Drv", EntryPoint="WritePrinter", SetLastError=true, ExactSpelling=true, CallingConvention=CallingConvention.StdCall)]
public static extern bool WritePrinter(IntPtr hPrinter, IntPtr pBytes, Int32 dwCount, out Int32 dwWritten );
// SendBytesToPrinter()
// When the function is given a printer name and an unmanaged array
// of bytes, the function sends those bytes to the print queue.
// Returns true on success, false on failure.
public static bool SendBytesToPrinter( string szPrinterName, IntPtr pBytes, Int32 dwCount)
{
Int32 dwError = 0, dwWritten = 0;
IntPtr hPrinter = new IntPtr(0);
DOCINFOA di = new DOCINFOA();
bool bSuccess = false; // Assume failure unless you specifically succeed.
di.pDocName = "My C#.NET RAW Document";
di.pDataType = "RAW";
// Open the printer.
if( OpenPrinter( szPrinterName.Normalize(), out hPrinter, IntPtr.Zero ) )
{
// Start a document.
if( StartDocPrinter(hPrinter, 1, di) )
{
// Start a page.
if( StartPagePrinter(hPrinter) )
{
// Write your bytes.
bSuccess = WritePrinter(hPrinter, pBytes, dwCount, out dwWritten);
EndPagePrinter(hPrinter);
}
EndDocPrinter(hPrinter);
}
ClosePrinter(hPrinter);
}
// If you did not succeed, GetLastError may give more information
// about why not.
if( bSuccess == false )
{
dwError = Marshal.GetLastWin32Error();
}
return bSuccess;
}
public static bool SendFileToPrinter( string szPrinterName, string szFileName )
{
// Open the file.
FileStream fs = new FileStream(szFileName, FileMode.Open);
// Create a BinaryReader on the file.
BinaryReader br = new BinaryReader(fs);
// Dim an array of bytes big enough to hold the file's contents.
Byte []bytes = new Byte[fs.Length];
bool bSuccess = false;
// Your unmanaged pointer.
IntPtr pUnmanagedBytes = new IntPtr(0);
int nLength;
nLength = Convert.ToInt32(fs.Length);
// Read the contents of the file into the array.
bytes = br.ReadBytes( nLength );
// Allocate some unmanaged memory for those bytes.
pUnmanagedBytes = Marshal.AllocCoTaskMem(nLength);
// Copy the managed byte array into the unmanaged array.
Marshal.Copy(bytes, 0, pUnmanagedBytes, nLength);
// Send the unmanaged bytes to the printer.
bSuccess = SendBytesToPrinter(szPrinterName, pUnmanagedBytes, nLength);
// Free the unmanaged memory that you allocated earlier.
Marshal.FreeCoTaskMem(pUnmanagedBytes);
return bSuccess;
}
public static bool SendStringToPrinter( string szPrinterName, string szString )
{
IntPtr pBytes;
Int32 dwCount;
// How many characters are in the string?
// Fix from Nicholas Piasecki:
// dwCount = szString.Length;
dwCount = (szString.Length + 1) * Marshal.SystemMaxDBCSCharSize;
// Assume that the printer is expecting ANSI text, and then convert
// the string to ANSI text.
pBytes = Marshal.StringToCoTaskMemAnsi(szString);
// Send the converted ANSI string to the printer.
SendBytesToPrinter(szPrinterName, pBytes, dwCount);
Marshal.FreeCoTaskMem(pBytes);
return true;
}
}
Fran_gg7, I had the same issue recently. Firstly, turn on the persistence of documents on the printer. This will allow you to see if the printer successfully received the print request.
You will see items in the printer queue and they will remain there.
In my scenario the print request was correctly being sent to the printer, however I was testing it on a laser printer that ultimately was unable to interpret the raw string data I was passing to it.
I tested the same output on a label printer that could understand the ZPL (zebra programming language) I was passing it and boom it worked fine.
Have a look at this for a detailed explanation
Hope this helps.
In cases where you are using the MSDN example from here https://support.microsoft.com/en-us/kb/322091#top but are trying to use an array of bytes instead of a string or file... This may help
public static void SendBytesToLocalPrinter(byte[] data, string printerName)
{
var size = Marshal.SizeOf(data[0]) * data.Length;
var pBytes = Marshal.AllocHGlobal(size);
try
{
SendBytesToPrinter(printerName, pBytes, size);
}
finally
{
Marshal.FreeCoTaskMem(pBytes);
}
}
This does not change with the byte array encoding. This is useful if you are trying to send some utf8 encoded byte sequences with some binary (ie. image) data mixed in as was the case for our team.
If printing plain text to Dot Matrix using "RawPrinterHelper" Method, it would only work properly for me when I manually added a printer, selected Generic / Text Only, and selected the USB001 port that was assigned to my USB connected printer (okidata in my test). Then RawPrinterHelper.SendStringToPrinter would behave very much like an 'lpd to lpt1:'
Had exactly the same issue with my ZEBRA ZD420 printer.
Sending ZPL string to printer only the data light flashing shortly without printing.
I changed only
Marshal.StringToCoTaskMemAnsi(szString);
to
Marshal.StringToCoTaskMemUTF8(szString);
and it works !
In order to handle cases of downloading data from a url that has no file extension,
I need to know what the file type is.
for example, how can the WebClient.DownloadData method reveal that it downloaded a png [edit: jpeg] image using the url below?
https://encrypted-tbn2.gstatic.com/images?q=tbn:ANd9GcTw4P3HxyHR8wumE3lY3TOlGworijj2U2DawhY9wnmcPKnbmGHg
I did not find anything in the documentation that describes how to do this.
If you trust the header information, this is possible to do using WebClient—you don't need to use HttpClient:
var webClient = new WebClient();
var result = webClient.DownloadData(url);
var contentType = webClient.ResponseHeaders["Content-Type"];
if (contentType != null &&
contentType.StartsWith("image", StringComparison.OrdinalIgnoreCase))
{
// it's probably an image
}
It can't, directly.
If you trust the headers the web server sends back, you could use a different HTTP client (e.g. WebRequest or HttpClient) to make the entire response available rather than just the body. You can then look at the Content-Type header.
Other than that, you'll need to look at the content itself. Various file types have "magic numbers" which you could use to identify the file - they're typically at the start of the file, and if you only have a limited set of file types to look for, this may well be a viable approach. It won't be able to identify all file types though.
As an example, the first four bytes of the image you've linked to are ff d8 ff e0. That reveals that actually it's not a jpeg image. As it happens, the server response also included a header of content-type: image/jpeg.
You may use HttpClient for doing this GET request.
Sample code:
HttpClient client = new HttpClient();
var response = await client.GetAsync("https://encrypted-tbn2.gstatic.com/images?q=tbn%3aANd9GcTw4P3HxyHR8wumE3lY3TOlGworijj2U2DawhY9wnmcPKnbmGHg");
var filetype = response.Content.Headers.ContentType.MediaType;
var imageArray = await response.Content.ReadAsByteArrayAsync();
On the above code, filetype variable has the file type and also extension as image/JPEG or image/PNG etc.
You can try using FindMimeFromData API. Here is the snippet. It may help you.
WebClient webClient = new WebClient();
var result = webClient.DownloadData(new Uri("url"));
IntPtr mimeout;
int result2 = FindMimeFromData(IntPtr.Zero, "sample", result, 4096, null, 0, out mimeout, 0);
if (result2 != 0)
throw Marshal.GetExceptionForHR(result2);
string mime = Marshal.PtrToStringUni(mimeout);
Marshal.FreeCoTaskMem(mimeout);
Console.WriteLine(mime);
And here is the API declaration. (Copied from here)
[DllImport("urlmon.dll", CharSet = CharSet.Unicode, ExactSpelling = true, SetLastError = false)]
static extern int FindMimeFromData(IntPtr pBC, [MarshalAs(UnmanagedType.LPWStr)] string pwzUrl, [MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.I1, SizeParamIndex = 3)]
byte[] pBuffer, int cbSize, [MarshalAs(UnmanagedType.LPWStr)] string pwzMimeProposed, int dwMimeFlags, out IntPtr ppwzMimeOut, int dwReserved);
How do I check the file type of a file uploaded using FileUploader control in an ASP.NET C# webpage?
I tried checking file extension, but it obviously fails when a JPEG image (e.g. Leonardo.jpg) is renamed to have a PDF's extension (e.g. Leonardo.pdf).
I tried
FileUpload1.PostedFile.ContentType.ToLower().Equals("application/pdf")
but this fails as the above code behaves the same way as the first did.
Is there any other way to check the actual file type, not just the extension?
I looked at ASP.NET how to check type of the file type irrespective of extension.
Edit: I tried below code from one of the posts in stackoverflow. But this down't work. Any idea about this.
/// <summary>
/// This class allows access to the internal MimeMapping-Class in System.Web
/// </summary>
class MimeMappingWrapper
{
static MethodInfo getMimeMappingMethod;
static MimeMappingWrapper() {
// dirty trick - Assembly.LoadWIthPartialName has been deprecated
Assembly ass = Assembly.LoadWithPartialName("System.Web");
Type t = ass.GetType("System.Web.MimeMapping");
getMimeMappingMethod t.GetMethod("GetMimeMapping", BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public));
}
/// <summary>
/// Returns a MIME type depending on the passed files extension
/// </summary>
/// <param name="fileName">File to get a MIME type for</param>
/// <returns>MIME type according to the files extension</returns>
public static string GetMimeMapping(string fileName) {
return (string)getMimeMappingMethod.Invoke(null, new[] { fileName });
}
}
Dont use File Extensions to work out MIME Types, instead use "Winista" for binary analysis.
Say someone renames an exe with a jpg extension. You can still determine the real file format. It doesn't detect swf's or flv's but does pretty much every other well known format and you can get a hex editor to add more files it can detect.
Download Winista: here or my mirror or my GitHub https://github.com/MeaningOfLights/MimeDetect.
Where Winista fails to detect the real file format, I've resorted back to the URLMon method:
public class urlmonMimeDetect
{
[DllImport(#"urlmon.dll", CharSet = CharSet.Auto)]
private extern static System.UInt32 FindMimeFromData(
System.UInt32 pBC,
[MarshalAs(UnmanagedType.LPStr)] System.String pwzUrl,
[MarshalAs(UnmanagedType.LPArray)] byte[] pBuffer,
System.UInt32 cbSize,
[MarshalAs(UnmanagedType.LPStr)] System.String pwzMimeProposed,
System.UInt32 dwMimeFlags,
out System.UInt32 ppwzMimeOut,
System.UInt32 dwReserverd
);
public string GetMimeFromFile(string filename)
{
if (!File.Exists(filename))
throw new FileNotFoundException(filename + " not found");
byte[] buffer = new byte[256];
using (FileStream fs = new FileStream(filename, FileMode.Open, FileAccess.Read))
{
if (fs.Length >= 256)
fs.Read(buffer, 0, 256);
else
fs.Read(buffer, 0, (int)fs.Length);
}
try
{
System.UInt32 mimetype;
FindMimeFromData(0, null, buffer, 256, null, 0, out mimetype, 0);
System.IntPtr mimeTypePtr = new IntPtr(mimetype);
string mime = Marshal.PtrToStringUni(mimeTypePtr);
Marshal.FreeCoTaskMem(mimeTypePtr);
return mime;
}
catch (Exception e)
{
return "unknown/unknown";
}
}
}
From inside the Winista method, I fall back on the URLMon here:
public MimeType GetMimeTypeFromFile(string filePath)
{
sbyte[] fileData = null;
using (FileStream srcFile = new FileStream(filePath, FileMode.Open, FileAccess.Read))
{
byte[] data = new byte[srcFile.Length];
srcFile.Read(data, 0, (Int32)srcFile.Length);
fileData = Winista.Mime.SupportUtil.ToSByteArray(data);
}
MimeType oMimeType = GetMimeType(fileData);
if (oMimeType != null) return oMimeType;
//We haven't found the file using Magic (eg a text/plain file)
//so instead use URLMon to try and get the files format
Winista.MimeDetect.URLMONMimeDetect.urlmonMimeDetect urlmonMimeDetect = new Winista.MimeDetect.URLMONMimeDetect.urlmonMimeDetect();
string urlmonMimeType = urlmonMimeDetect.GetMimeFromFile(filePath);
if (!string.IsNullOrEmpty(urlmonMimeType))
{
foreach (MimeType mimeType in types)
{
if (mimeType.Name == urlmonMimeType)
{
return mimeType;
}
}
}
return oMimeType;
}
Update:
To work out more files using magic here is a FILE SIGNATURES TABLE
Checking the names or extension is in no way a reliable idea. The only way you can be sure is that you actually read the content of the file.
i.e. if you want to check the file for image, you should try loading image from the file and if it fails, you can be sure that it is not an image file. This can be done easily using GDI objects.
Same is also true for PDF files.
Conclusion is, don't rely on the user supplied name or extension.
you can check you file type in FileApload by
ValidationExpression="^.+.(([pP][dD][fF])|([jJ][pP][gG])|([pP][nN][gG])))$"
for ex: you can add ([rR][aA][rR]) for Rar file type and etc ...