Reputation: 12420
I am allowing uploading of files to my C# MVC website, I am restricting those types based on extension at the moment, but also feel I need a server side check to confirm they haven't just renamed it.
Is there a technique that I can use to check all the types I need or a library I can use that will help here?
I have seen people checking the first few bytes of the file, but I am scared I will miss something?
Thanks for your help.
Edit:
There are a lot of suggestions here. I will investigate some of these as a solution.
Upvotes: 7
Views: 36264
Reputation: 374
I personally used the FileSignatures for file content inspection in the past.
/// <summary>
/// Provider for
/// <a href="https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/extension-methods">extension methods</a>
/// on <see cref="Stream">streams</see>.
/// </summary>
public static class StreamExtensions
{
private static readonly ImmutableArray<string> _mimeTypeKeys = ["application/pdf", "image/jpg", "image/jpeg", "image/png"];
private static readonly ImmutableArray<string> _extensionKeys = ["pdf", "jpg", "jpeg", "png"];
/// <summary>Checks if the stream contains a valid file.</summary>
/// <param name="inputStream"><inheritdoc cref="Stream" path="/summary"/></param>
/// <param name="extension">Out: The actual extension of the file provided through the <paramref name="inputStream"/>.</param>
/// <param name="mimeType">Out: The actual mime-type of the file provided through the <paramref name="inputStream"/>.</param>
/// <returns><see langword="true"/> if the stream is a valid file, otherwise <see langword="false"/>.</returns>
public static bool IsValidFile(
this Stream inputStream,
out string extension,
out string mimeType)
{
mimeType = null;
extension = null;
if (inputStream is null)
return false;
var fileStreamInspector = new FileFormatInspector();
var fileHeader = fileStreamInspector.DetermineFileFormat(inputStream);
mimeType = fileHeader.MediaType;
extension = fileHeader.Extension;
return _mimeTypeKeys.Contains(mimeType) && _extensionKeys.Contains(extension);
}
}
How to use:
var isValid = inputFile.IsValidFile(
extension: out var extension,
mimeType: out var mimeType);
Upvotes: 0
Reputation: 73
Client side
<asp:FileUpload runat="server" ID="fileUploader" accept=".pdf,.docx"/>
<asp:RegularExpressionValidator ID="fileUploaderV" runat="server" ControlToValidate="fileUploader" ErrorMessage="Only .pdf, .docx formats are allowed"
ValidationExpression="(.+\.([Pp][Dd][Ff])|.+\.([Dd][Oo][Cc][Xx]))" Display="Dynamic" ForeColor="Red"></asp:RegularExpressionValidator>
Server side
using System.Runtime.InteropServices;
[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);
public static string getMimeFromFile(HttpPostedFile file)
{
IntPtr mimeout;
int MaxContent = (int)file.ContentLength;
if (MaxContent > 4096) MaxContent = 4096;
byte[] buf = new byte[MaxContent];
file.InputStream.Read(buf, 0, MaxContent);
int result = FindMimeFromData(IntPtr.Zero, file.FileName, buf, MaxContent, null, 0, out mimeout, 0);
if (result != 0)
{
Marshal.FreeCoTaskMem(mimeout);
return "";
}
string mime = Marshal.PtrToStringUni(mimeout);
Marshal.FreeCoTaskMem(mimeout);
return mime.ToLower();
}
}
Upvotes: 0
Reputation: 36
For more safety you can also check both mimeType with the ContentType property as ffffff01 said, and file extension with Path.GetExtension method.
Upvotes: 0
Reputation: 3629
You can obtain the MIME type in the following way:
var fileName = ContentDispositionHeaderValue.Parse(file.ContentDisposition).FileName.Trim('"');
mimeType = file.ContentType;
Once MIME type is obtained, it can be compared with string comparison (as shown by @ffffff01 )
Upvotes: 0
Reputation: 12420
I ended up mixing some solutions from here, because I am not using the HttpFileBase and only have the file stream, I had to read the first few bytes of the stream to determine the mime type.
Note: I don't use the registry technique because I don't know what will or wont be installed on the servers.
Upvotes: 0
Reputation: 9225
Try this solution: Using .NET, how can you find the mime type of a file based on the file signature not the extension
It will do file content sniffing for you.
Upvotes: 1
Reputation: 5238
If you are reading the file as an HttpPostedFile you can get the content type which is equal to the mime type.
So then you can do the following:
if (myFile.ContentType == "video/mpeg")
{
// Do your thing
}
else{
// error
}
Upvotes: 4