using System; using System.IO; using System.Security; using System.Web; using System.Web.UI; namespace FileSharePortal { public partial class Download : Page { private const string ShareRoot = @"\\LAN-DC1\Fileshare"; protected void Page_Load( object sender, EventArgs e) { /* * Kein Caching. */ 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; } /* * Die Datei muss existieren. */ if (!File.Exists(physicalPath)) { Response.StatusCode = 404; CompleteResponse(); return; } /* * ==================================================== * ZUGRIFF DES AKTUELL ANGEMELDETEN BENUTZERS PRÜFEN * ==================================================== * * Wegen * * * * läuft dieser Zugriff unter dem angemeldeten * AD-Benutzer. */ try { using ( FileStream stream = new FileStream( physicalPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite | FileShare.Delete)) { /* * Datei erfolgreich geöffnet. * * Es wird hier absichtlich nichts gelesen. * Das Öffnen reicht als Berechtigungsprüfung. */ } } catch (UnauthorizedAccessException) { Response.StatusCode = 403; CompleteResponse(); return; } catch (IOException) { Response.StatusCode = 403; CompleteResponse(); return; } /* * ==================================================== * DOWNLOAD AUSLIEFERN * ==================================================== */ string fileName = Path.GetFileName( physicalPath); string encodedFileName = Uri.EscapeDataString( fileName); string contentType = MimeMapping.GetMimeMapping( physicalPath); Response.Clear(); Response.Buffer = true; Response.ContentType = contentType; /* * UTF-8-Dateiname. * * Funktioniert auch für: * * Müller.pdf * Änderungen.docx * Köln.xlsx * Rechnung März 2026.pdf */ Response.AddHeader( "Content-Disposition", "attachment; filename*=UTF-8''" + encodedFileName ); /* * Dateigröße setzen. */ FileInfo fileInfo = new FileInfo( physicalPath); Response.AddHeader( "Content-Length", fileInfo.Length.ToString() ); /* * Datei übertragen. */ Response.TransmitFile( physicalPath); CompleteResponse(); } /* * ======================================================== * SICHEREN DATEIPFAD ERMITTELN * ======================================================== */ private string GetSafePhysicalFilePath( string relativePath) { /* * URL-Decoding. */ string decoded = HttpUtility.UrlDecode( relativePath); /* * URL-Pfade in Windows-Pfade umwandeln. */ decoded = decoded.Replace( '/', '\\'); /* * Keine absoluten Pfade erlauben. * * Verboten wären z.B.: * * \Windows\System32 * C:\... * \\Server\Share */ 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(); } } /* * Vollständigen Dateipfad erzeugen. */ string fullPath = Path.GetFullPath( Path.Combine( ShareRoot, decoded)); /* * Root normalisieren. */ string normalizedRoot = Path.GetFullPath( ShareRoot) .TrimEnd('\\') + "\\"; /* * Der Pfad MUSS unterhalb von * \\LAN-DC1\Fileshare bleiben. */ if ( !fullPath.StartsWith( normalizedRoot, StringComparison.OrdinalIgnoreCase)) { throw new SecurityException(); } /* * Datei muss existieren. */ if (!File.Exists(fullPath)) { throw new FileNotFoundException(); } return fullPath; } /* * ======================================================== * RESPONSE BEENDEN * ======================================================== * * Kleine Hilfsmethode, damit Response.End() * nicht unnötig in mehreren Stellen steht. */ private void CompleteResponse() { HttpContext.Current.ApplicationInstance .CompleteRequest(); } } }