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(); } } }