Zip Slip in Aspnet with Basic Auth
Zip Slip in Aspnet with Basic Auth — how this combination creates or exposes the vulnerability
Zip Slip is a path traversal vulnerability that occurs when an archive (for example, a ZIP file) contains entries with crafted paths such as ../../../etc/passwd. When the server extracts the archive without sanitizing these paths, files can be written outside the intended directory. In an ASP.NET application that accepts file uploads and processes archives, this becomes a server-side extraction problem. Using HTTP Basic Auth changes how you think about the attack surface: authentication is sent on every request in an easily interceptable header, but it does not directly prevent file extraction behavior. The combination is notable because Basic Auth provides identity for the request yet does nothing to validate or constrain what the server does with uploaded archives.
Consider an endpoint that receives a ZIP archive via POST, extracts it to a temporary folder, and then processes contents. If the endpoint trusts filenames from the archive, an attacker can supply a malicious ZIP where entries traverse directories. Even when the endpoint requires a valid Basic Auth username and password, the extraction code can still follow malicious paths if path normalization and directory traversal checks are absent. Basic Auth therefore becomes a weak gate: it may limit who can invoke the endpoint, but it does not limit what authenticated users can ask the server to do with uploaded data. This is why server-side validation and secure extraction are essential regardless of transport-layer authentication mechanisms.
ASP.NET Core typically receives the uploaded file as an IFormFile. A vulnerable implementation might stream or extract entries directly using methods that do not resolve final paths. For example, using ZipArchiveEntry and concatenating entry.FullName into a destination folder without canonicalizing and validating can lead to absolute or relative path traversal. An attacker authenticated with Basic Auth can upload a ZIP containing entries like ../../malicious/bin/sh, and if the server writes these files under a privileged directory, the impact may include overwriting configuration files or planting executables. The vulnerability is not about breaking authentication; it is about insufficient path validation after successful authentication, which demonstrates that secure handling of untrusted archives must be independent of authentication correctness.
Basic Auth-Specific Remediation in Aspnet — concrete code fixes
To mitigate Zip Slip in ASP.NET when using Basic Auth, focus on secure archive extraction and strict path validation. Do not rely on authentication to prevent malicious file paths. Always resolve the final path with Path.GetFullPath and ensure it remains within the intended directory. Treat filenames from the archive as untrusted input, regardless of transport authentication. Below are concrete code examples demonstrating a secure approach with Basic Auth in ASP.NET Core.
Secure extraction with path validation
Use System.IO.Compression and validate each entry before writing. The following example shows an endpoint that accepts an uploaded ZIP, authenticates via Basic Auth, and safely extracts contents:
using System.IO.Compression;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
[ApiController]
[Route("api/[controller]")]
public class UploadController : ControllerBase
{
private const string AllowedBaseDirectory = "/safe/extraction/root";
[HttpPost("zip")]
[Authorize(AuthenticationSchemes = "Basic")]
public IActionResult UploadZip(IFormFile file)
{
if (file == null || file.Length == 0)
return BadRequest("No file uploaded");
using var stream = file.OpenReadStream();
using var archive = new ZipArchive(stream);
foreach (var entry in archive.Entries)
{
var destinationPath = Path.GetFullPath(Path.Combine(AllowedBaseDirectory, entry.FullName));
// Ensure the resolved path starts with the allowed directory
if (!destinationPath.StartsWith(AllowedBaseDirectory, StringComparison.Ordinal))
return BadRequest("Invalid path in archive");
// Skip directories
if (entry.Length == 0 && string.IsNullOrEmpty(entry.Name))
continue;
// Ensure the entry has a file name and not just a path
var fileName = Path.GetFileName(entry.FullName);
if (string.IsNullOrEmpty(fileName))
continue;
var filePath = Path.Combine(destinationPath, fileName);
// Ensure the directory exists
var directory = Path.GetDirectoryName(filePath);
if (!Directory.Exists(directory))
Directory.CreateDirectory(directory);
entry.ExtractToFile(filePath, overwrite: false);
}
return Ok("Extraction completed safely");
}
}
Basic Auth setup example
Configure Basic Auth in ASP.NET Core using policy-based authentication. This example demonstrates a simple handler that checks a hardcoded credential for clarity; in production, validate credentials against a secure store:
using Microsoft.AspNetCore.Authentication;
using Microsoft.Extensions.Options;
using System.Security.Claims;
using System.Text.Encodings.Web;
using System.Text;
public class BasicAuthHandler : AuthenticationHandler
{
private const string Username = "admin";
private const string Password = "securePassword123";
public BasicAuthHandler(
IOptionsMonitor<AuthenticationSchemeOptions> options,
ILoggerFactory logger,
UrlEncoder encoder,
ISystemClock clock)
: base(options, logger, encoder, clock) { }
protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
{
if (!Request.Headers.ContainsKey("Authorization"))
return AuthenticateResult.Fail("Missing Authorization Header");
var authHeader = AuthenticationHeaderValue.Parse(Request.Headers["Authorization"]);
if (authHeader.Scheme != "Basic")
return AuthenticateResult.Fail("Invalid Scheme");
var credentialBytes = Convert.FromBase64String(authHeader.Parameter);
var credentials = Encoding.UTF8.GetString(credentialBytes).Split(':', 2);
var (user, pass) = (credentials[0], credentials[1]);
if (user == Username && pass == Password)
{
var claims = new[] { new Claim(ClaimTypes.Name, user) };
var identity = new ClaimsIdentity(claims, Scheme.Name);
var principal = new ClaimsPrincipal(identity);
var ticket = new AuthenticationTicket(principal, Scheme.Name);
return AuthenticateResult.Success(ticket);
}
return AuthenticateResult.Fail("Invalid Credentials");
}
}
Register the handler in Program.cs:
builder.Services.AddAuthentication("Basic")
.AddScheme<AuthenticationSchemeOptions, BasicAuthHandler>("Basic", null);
builder.Services.AddAuthorization();
With these measures, even when clients use Basic Auth, the server ensures uploaded archives cannot escape the intended directory, addressing Zip Slip independently of authentication correctness.