diff --git a/View.aspx.cs b/View.aspx.cs new file mode 100644 index 0000000..8caa644 --- /dev/null +++ b/View.aspx.cs @@ -0,0 +1,471 @@ +using System; +using System.IO; +using System.Security; +using System.Web; +using System.Web.UI; + +namespace FileSharePortal +{ + public partial class View : Page + { + private const string ShareRoot = + @"\\LAN-DC1\Fileshare"; + + + protected void Page_Load( + object sender, + EventArgs e) + { + Response.Cache.SetCacheability( + HttpCacheability.NoCache); + + Response.Cache.SetNoStore(); + + + string relativePath = + Request.QueryString["path"]; + + + if (string.IsNullOrWhiteSpace(relativePath)) + { + Response.StatusCode = 400; + CompleteResponse(); + return; + } + + + string physicalPath; + + + try + { + physicalPath = + GetSafePhysicalFilePath( + relativePath); + } + catch (UnauthorizedAccessException) + { + Response.StatusCode = 403; + CompleteResponse(); + return; + } + catch (SecurityException) + { + Response.StatusCode = 403; + CompleteResponse(); + return; + } + catch (FileNotFoundException) + { + Response.StatusCode = 404; + CompleteResponse(); + return; + } + catch (DirectoryNotFoundException) + { + Response.StatusCode = 404; + CompleteResponse(); + return; + } + catch + { + Response.StatusCode = 400; + CompleteResponse(); + return; + } + + + if (!File.Exists(physicalPath)) + { + Response.StatusCode = 404; + CompleteResponse(); + return; + } + + + /* + * ==================================================== + * BERECHTIGUNG PRÜFEN + * ==================================================== + * + * Durch ASP.NET Impersonation läuft der Zugriff + * unter dem angemeldeten AD-Benutzer. + */ + try + { + using ( + FileStream stream = + new FileStream( + physicalPath, + FileMode.Open, + FileAccess.Read, + FileShare.ReadWrite | + FileShare.Delete)) + { + } + } + catch (UnauthorizedAccessException) + { + Response.StatusCode = 403; + CompleteResponse(); + return; + } + catch (IOException) + { + Response.StatusCode = 403; + CompleteResponse(); + return; + } + + + string extension = + Path.GetExtension( + physicalPath) + .ToLowerInvariant(); + + + string fileName = + Path.GetFileName( + physicalPath); + + + /* + * ==================================================== + * MIME-TYP + * ==================================================== + */ + + string contentType = + GetContentType( + extension); + + + /* + * ==================================================== + * INLINE ODER DOWNLOAD + * ==================================================== + * + * Nur ausdrücklich erlaubte Dateitypen werden + * inline angezeigt. + * + * Alles andere wird als Download behandelt. + */ + bool inline = + IsInlineType(extension); + + + Response.Clear(); + + Response.Buffer = false; + + Response.ContentType = + contentType; + + + /* + * UTF-8-Dateiname. + */ + string encodedFileName = + Uri.EscapeDataString( + fileName); + + + if (inline) + { + Response.AddHeader( + "Content-Disposition", + "inline; filename*=UTF-8''" + + encodedFileName); + } + else + { + Response.AddHeader( + "Content-Disposition", + "attachment; filename*=UTF-8''" + + encodedFileName); + } + + + FileInfo fileInfo = + new FileInfo( + physicalPath); + + + Response.AddHeader( + "Content-Length", + fileInfo.Length.ToString()); + + + /* + * Datei ohne komplettes Einlesen in den + * Arbeitsspeicher übertragen. + */ + Response.TransmitFile( + physicalPath); + + + CompleteResponse(); + } + + + /* + * ======================================================== + * INLINE DATEITYPEN + * ======================================================== + */ + + private bool IsInlineType( + string extension) + { + switch (extension) + { + /* + * PDF + */ + case ".pdf": + + /* + * Bilder + */ + case ".jpg": + case ".jpeg": + case ".png": + case ".gif": + case ".webp": + case ".bmp": + case ".ico": + + /* + * Text + */ + case ".txt": + case ".log": + case ".csv": + case ".json": + case ".xml": + + /* + * Audio + */ + case ".mp3": + case ".wav": + case ".ogg": + + /* + * Video + */ + case ".mp4": + case ".webm": + case ".ogv": + + return true; + + + /* + * Alles andere herunterladen. + * + * Insbesondere: + * + * DOC + * DOCX + * XLS + * XLSX + * PPT + * PPTX + * ZIP + * EXE + * MSI + * + * usw. + */ + default: + + return false; + } + } + + + /* + * ======================================================== + * MIME TYPES + * ======================================================== + */ + + private string GetContentType( + string extension) + { + switch (extension) + { + case ".pdf": + return "application/pdf"; + + + case ".jpg": + case ".jpeg": + return "image/jpeg"; + + + case ".png": + return "image/png"; + + + case ".gif": + return "image/gif"; + + + case ".webp": + return "image/webp"; + + + case ".bmp": + return "image/bmp"; + + + case ".ico": + return "image/x-icon"; + + + /* + * Text-Dateien bewusst als text/plain + * behandeln. + * + * Dadurch werden z.B. HTML-Inhalte nicht + * als HTML ausgeführt. + */ + case ".txt": + case ".log": + case ".csv": + case ".json": + case ".xml": + return "text/plain; charset=utf-8"; + + + case ".mp3": + return "audio/mpeg"; + + + case ".wav": + return "audio/wav"; + + + case ".ogg": + return "audio/ogg"; + + + case ".mp4": + return "video/mp4"; + + + case ".webm": + return "video/webm"; + + + case ".ogv": + return "video/ogg"; + + + default: + return MimeMapping.GetMimeMapping( + "file" + extension); + } + } + + + /* + * ======================================================== + * SICHEREN DATEIPFAD ERMITTELN + * ======================================================== + */ + + private string GetSafePhysicalFilePath( + string relativePath) + { + string decoded = + HttpUtility.UrlDecode( + relativePath); + + + decoded = + decoded.Replace( + '/', + '\\'); + + + /* + * Keine absoluten Pfade. + */ + if ( + decoded.StartsWith("\\") || + decoded.Contains(":")) + { + throw new SecurityException(); + } + + + /* + * Path Traversal verhindern. + */ + string[] parts = + decoded.Split( + new[] { '\\' }, + StringSplitOptions.RemoveEmptyEntries); + + + foreach ( + string part + in parts) + { + if ( + part == "." || + part == "..") + { + throw new SecurityException(); + } + } + + + string fullPath = + Path.GetFullPath( + Path.Combine( + ShareRoot, + decoded)); + + + string normalizedRoot = + Path.GetFullPath( + ShareRoot) + .TrimEnd('\\') + + "\\"; + + + /* + * Der Pfad darf ausschließlich unterhalb + * unseres Shares liegen. + */ + if ( + !fullPath.StartsWith( + normalizedRoot, + StringComparison.OrdinalIgnoreCase)) + { + throw new SecurityException(); + } + + + if (!File.Exists(fullPath)) + { + throw new FileNotFoundException(); + } + + + return fullPath; + } + + + private void CompleteResponse() + { + HttpContext.Current + .ApplicationInstance + .CompleteRequest(); + } + } +} \ No newline at end of file diff --git a/view.aspx b/view.aspx new file mode 100644 index 0000000..74f2c48 --- /dev/null +++ b/view.aspx @@ -0,0 +1,8 @@ +<%@ Page + Language="C#" + ResponseEncoding="utf-8" + ContentType="text/html; charset=utf-8" + AutoEventWireup="true" + CodeFile="View.aspx.cs" + Inherits="FileSharePortal.View" +%> \ No newline at end of file diff --git a/web.config b/web.config new file mode 100644 index 0000000..e4a23cd --- /dev/null +++ b/web.config @@ -0,0 +1,69 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file