Overview
Applications frequently cross dangerous boundaries: they invoke operating-system processes, accept files, construct paths, access credentials, and allocate finite resources in response to untrusted requests. A failure at any of these boundaries can turn ordinary input into code execution, data disclosure, persistent malware storage, or loss of service.
The major risks are:
- Command injection: untrusted data changes the meaning of an operating-system command.
- File upload abuse: uploaded content is executable, malicious, oversized, misleading, or later served unsafely.
- Path traversal: attacker-controlled paths escape an intended directory or select an unauthorized file.
- Secrets exposure: credentials, keys, or tokens leak through source code, configuration, logs, build artifacts, or runtime behavior.
- Denial of Service (DoS): one source exhausts application or dependency resources.
- Distributed Denial of Service (DDoS): many sources coordinate resource exhaustion or traffic flooding.
These problems are related by a common design rule: do not give untrusted input more interpretation, authority, or resource control than the use case requires. Avoid interpreters when a typed library can perform the operation, generate server-side filenames, retrieve secrets through managed identity or a secret store, and place admission controls before expensive work.
This topic matters in interviews because it tests practical secure coding, boundary validation, cloud and .NET architecture, defense in depth, and the ability to distinguish application-layer protections from infrastructure-layer controls.
Core Concepts
Data, Instructions, and Interpreters
Injection occurs when data is interpreted as instructions.
Dangerous interpreters include:
- Operating-system shells.
- SQL engines.
- Template engines.
- Expression languages.
- Regular-expression engines in pathological cases.
- Archive extractors and document parsers.
- Image, media, and office-file processors.
The strongest defense is often to remove the interpreter from the path. If the application needs to resize an image, use an image-processing API instead of constructing a shell command around a command-line utility.
Command Injection
Command injection occurs when untrusted input changes a command's structure or causes an additional command, option, path, or redirection to be interpreted.
Unsafe example:
var fileName = request.FileName;
Process.Start(new ProcessStartInfo
{
FileName = "cmd.exe",
Arguments = $"/c type uploads\\{fileName}",
UseShellExecute = false
});
Input containing shell metacharacters can change the command. The exact metacharacters differ among Windows command shells, PowerShell, POSIX shells, and the invoked program.
Avoid the Shell
Prefer a .NET API:
var content = await File.ReadAllTextAsync(
approvedPath,
cancellationToken);
If a child process is genuinely required:
- Invoke a fixed executable directly.
- Set
UseShellExecutetofalse. - Pass arguments as separate values.
- Allowlist operations and values.
- Use a low-privilege service account.
- Set timeouts and output limits.
- Use a constrained working directory.
- Avoid inheriting unnecessary environment variables and handles.
var startInfo = new ProcessStartInfo
{
FileName = trustedExecutablePath,
UseShellExecute = false,
RedirectStandardOutput = true,
RedirectStandardError = true
};
startInfo.ArgumentList.Add("--format");
startInfo.ArgumentList.Add(allowedFormat);
startInfo.ArgumentList.Add(serverGeneratedInputPath);
Separate arguments reduce shell parsing, but they do not make every value safe. Some programs interpret attacker-controlled arguments as options, paths, scripts, templates, or configuration. Validate according to the invoked program's grammar.
Allowlisting Command Operations
Avoid accepting free-form command names or options.
Unsafe:
{ "command": "convert --anything the-user-wants" }
Safer application contract:
{ "operation": "thumbnail", "size": "small" }
Server mapping:
var dimensions = request.Size switch
{
"small" => (Width: 320, Height: 180),
"medium" => (Width: 640, Height: 360),
_ => throw new ValidationException("Unsupported size.")
};
Map a narrow business operation to trusted implementation details instead of exposing an interpreter interface.
Command Execution Containment
Assume child processes and parsers may contain vulnerabilities.
Use:
- A dedicated low-privilege identity.
- Read-only filesystems where practical.
- A temporary isolated working directory.
- No access to application secrets.
- Network restrictions.
- CPU, memory, process, and execution-time limits.
- Sandboxed containers or worker services.
- Patch and dependency management.
- Auditing of executable, fixed options, duration, and outcome.
Do not log attacker-controlled command strings in a way that creates log injection or leaks secret arguments.
File Upload Threats
An uploaded file may be:
- Executable content disguised with another extension.
- Active HTML, SVG, or script content.
- Malware or a malicious document.
- A decompression bomb.
- A parser exploit.
- An oversized file or excessive number of files.
- A filename containing traversal characters.
- A file that overwrites an existing object.
- Public content that exposes sensitive information.
- A polyglot valid under multiple formats.
Validation must consider how the file will be stored, processed, and served.
Secure Upload Pipeline
A robust upload flow commonly performs:
- Authenticate and authorize the upload operation.
- Enforce request and per-file size limits before buffering.
- Stream to a quarantine location.
- Generate a server-side storage name.
- Validate allowed extension and expected media type.
- Inspect file signatures and parse with a maintained library when feasible.
- Scan for malware or send the file through content disarm and reconstruction.
- Process in an isolated worker with resource limits.
- Store outside the web root or on a separate storage service.
- Mark the object available only after validation succeeds.
- Serve through an authorized download endpoint or carefully configured object URL.
- Retain audit and lifecycle metadata.
No individual check proves that a file is safe.
Filename Handling
Treat the client filename as display metadata, not a storage path.
var storageName = $"{Guid.NewGuid():N}.upload";
var destination = Path.Combine(quarantineRoot, storageName);
If the original name is retained for display:
- Extract only the final name component.
- Normalize or replace unsupported characters.
- Limit length.
- Encode it correctly when displayed.
- Set a safe
Content-Dispositionvalue when downloaded. - Never concatenate it into a filesystem path.
Names can contain Unicode confusables, control characters, reserved device names, alternate separators, and trailing characters that different filesystems interpret differently.
Extension, Content Type, and File Signature
Each signal has limitations:
- The extension is attacker-controlled.
- The HTTP
Content-Typeis attacker-controlled. - Magic-byte or signature checks cover only part of a file.
- A valid file can still contain malicious active content.
- A parser may have vulnerabilities.
Use an allowlist based on a real business requirement. Where possible, parse and rewrite the file into a known-safe representation rather than preserving arbitrary source bytes.
Active Content and Download Behavior
Even a non-executable upload may become dangerous when served from the application's origin.
Risks include:
- HTML or SVG executing script.
- Browser MIME sniffing.
- Inline rendering of user-controlled content.
- Public object-store permissions.
- Predictable URLs.
- Stored XSS through metadata or filenames.
Defenses include:
- Serve untrusted content from a separate origin.
- Use
Content-Disposition: attachmentwhen inline rendering is unnecessary. - Set an explicit safe
Content-Type. - Use
X-Content-Type-Options: nosniff. - Apply authorization on every private download.
- Avoid public-read storage by default.
Archive Extraction and Zip Slip
Archives introduce path traversal and resource-exhaustion risks. An archive entry may contain:
../../application/config.json
Safe extraction must:
- Limit entry count and total expanded size.
- Reject absolute paths.
- Canonicalize each destination.
- Verify containment within the extraction root.
- Handle symbolic links and reparse points deliberately.
- Avoid overwriting sensitive files.
- Extract in an isolated temporary directory.
Compression ratio and nested archive depth should be constrained to prevent decompression bombs.
Path Traversal
Path traversal occurs when user input changes a path so it escapes the intended root:
../../secrets.json
..\..\web.config
Encodings, mixed separators, absolute paths, Unicode normalization, alternate data streams, symbolic links, and platform-specific names can complicate detection.
The best design does not accept paths from clients. Accept an opaque object ID and resolve it through trusted metadata:
var document = await repository.FindAuthorizedAsync(
request.DocumentId,
currentUser,
cancellationToken);
Canonicalization and Containment
If a relative path is necessary:
- Reject absolute paths.
- Combine it with a trusted root.
- Resolve the canonical full path.
- Verify that the result remains inside the canonical root.
- Account for case sensitivity and platform behavior.
- Consider symbolic links and races.
Conceptual .NET example:
var root = Path.GetFullPath(configuredRoot)
.TrimEnd(Path.DirectorySeparatorChar)
+ Path.DirectorySeparatorChar;
var candidate = Path.GetFullPath(Path.Combine(root, requestedRelativePath));
if (!candidate.StartsWith(root, StringComparison.Ordinal))
{
throw new SecurityException("Path escapes the allowed root.");
}
The string comparison must match the target filesystem's case behavior. A prefix check also requires the root to end with a separator so /allowed-other is not mistaken for a child of /allowed.
For high-risk cases, open files using APIs and operating-system controls that resist symbolic-link and time-of-check/time-of-use races, or isolate files into storage where clients never control filesystem paths.
Path Traversal Is Also an Authorization Problem
Even a path that remains under the storage root may select another user's file:
/tenant-a/reports/report.pdf
The server must check:
- The file belongs to the active tenant.
- The caller may perform the requested action.
- The object is in an allowed state.
- The storage key matches trusted metadata.
Canonicalization prevents directory escape. It does not provide object-level authorization.
Secrets and Sensitive Configuration
Secrets include:
- Database credentials.
- API keys.
- OAuth client secrets.
- Signing and encryption keys.
- Private certificates.
- Storage access keys.
- Webhook signing secrets.
- Passwords and recovery material.
Secrets should not be committed to source code or copied into container images, frontend bundles, mobile applications, or documentation.
Browser and mobile clients cannot reliably keep a distributed static secret. Anything shipped to the client should be assumed discoverable.
Secret Storage and Managed Identity
Prefer eliminating secrets through workload identity or managed identity. The application receives a short-lived identity from the platform and requests narrowly scoped access without storing a long-lived credential.
When secrets remain necessary:
- Store them in a dedicated secret manager.
- Restrict access by workload identity and least privilege.
- Encrypt them in transit and at rest.
- Rotate them.
- Version and audit access.
- Separate production from development.
- Avoid exposing them as command-line arguments.
- Keep them out of crash dumps and diagnostic endpoints.
Development secrets tools reduce accidental source commits but are not necessarily production vaults.
Secret Exposure Paths
Common exposure paths include:
- Git history and pull requests.
- CI/CD variables and build logs.
- Container layers.
- Environment dumps and support bundles.
- Exception messages.
- Application logs and traces.
- Metrics labels.
- Browser source maps and frontend configuration.
- Process listings and shell history.
- Infrastructure state files.
- Chat, tickets, and documentation.
Redaction should happen before data reaches shared telemetry. A secret accidentally committed to Git must be rotated; deleting the current file does not remove copies or history.
Secret Rotation and Incident Response
Rotation needs a tested process:
- Create a new credential.
- Allow the application to accept or use both old and new during transition when supported.
- Deploy the new credential.
- Verify usage and health.
- Revoke the old credential.
- investigate where it was exposed.
Emergency response should prioritize containment and revocation over repository cleanup. For signing keys, rotation may affect token validation and requires an explicit overlap and trust-removal plan.
Denial of Service
DoS attacks make a service unavailable by exhausting a constrained resource:
- CPU.
- Memory.
- Threads or event-loop capacity.
- Database connections.
- Outbound sockets.
- Disk space or I/O.
- Queue capacity.
- Third-party quotas.
- Expensive cryptographic operations.
- Human operations such as account recovery.
Application-layer DoS may use valid-looking requests that trigger disproportionate work.
Distributed Denial of Service
DDoS uses many sources or reflectors, making source blocking and network capacity more difficult.
Broad categories include:
- Volumetric traffic that saturates bandwidth.
- Protocol or connection-state exhaustion.
- Application-layer request floods.
- Dependency exhaustion against databases, caches, identity systems, or third-party APIs.
Application code cannot absorb a large volumetric attack by itself. Edge networks, CDNs, cloud DDoS protection, load balancers, firewalls, and upstream providers are part of the architecture.
Admission Control and Work Bounding
Reject or defer excess work before expensive operations.
Useful controls include:
- Request body and upload-size limits.
- Header and URL length limits.
- Timeouts and cancellation.
- Rate limits by identity, IP, tenant, and endpoint.
- Concurrency limits.
- Bounded queues.
- Pagination and maximum page sizes.
- Query cost or complexity limits.
- Maximum decompressed size.
- Database statement timeouts.
- Circuit breakers and bulkheads.
- Caching of safe repeated results.
- Backpressure.
Every loop, parser, query, and remote call influenced by input should have a meaningful bound.
ASP.NET Core Rate Limiting
ASP.NET Core provides rate-limiting middleware:
builder.Services.AddRateLimiter(options =>
{
options.AddFixedWindowLimiter("uploads", limiter =>
{
limiter.PermitLimit = 10;
limiter.Window = TimeSpan.FromMinutes(1);
limiter.QueueLimit = 0;
});
});
var app = builder.Build();
app.UseRateLimiter();
app.MapPost("/api/uploads", UploadAsync)
.RequireRateLimiting("uploads");
Production design should consider:
- Whether limits are per instance or coordinated.
- Which partition key represents fairness.
- What happens behind proxies.
- Whether authenticated identity should override raw IP.
- How queued requests consume memory and latency budgets.
- Whether edge controls protect the application before traffic reaches it.
Expensive Queries and Algorithmic Complexity
Valid syntax can still be abusive:
- Unbounded search.
- Deep GraphQL queries.
- Regex patterns with catastrophic backtracking.
- Large JSON nesting.
- Sorting on unindexed fields.
- Exporting entire tenant histories.
- Repeated cache misses.
- High-cost password hashing at unbounded concurrency.
Validate query shape, cap result sizes, precompute where appropriate, use timeouts, and monitor cost per operation rather than only request count.
Resilience Is Not Abuse Prevention
Retries improve transient-failure handling but can amplify overload. Autoscaling helps legitimate spikes but may increase cost without stopping an attack. Circuit breakers protect dependencies but do not authenticate callers.
Availability design needs:
- Prevention and filtering.
- Bounded resource consumption.
- Graceful degradation.
- Isolation of critical workloads.
- Cost controls.
- Operational detection and response.
Logging and Detection
Log security-relevant outcomes without recording dangerous content or secrets:
- Rejected command operations.
- Upload validation and scanning results.
- Path traversal attempts.
- Secret-store access failures and anomalous reads.
- Rate-limit decisions.
- Resource saturation and queue depth.
- Dependency timeouts.
Avoid logging:
- Uploaded file contents.
- Raw malicious payloads without containment.
- Full filesystem paths when sensitive.
- Credentials, tokens, or secret values.
- Unbounded attacker-controlled strings.
Alert on patterns and service impact, not every isolated validation failure.
Common Mistakes
Common failures include:
- Escaping a shell string instead of avoiding the shell.
- Assuming separated process arguments make every argument safe.
- Trusting file extension or MIME type alone.
- Saving uploads under client-provided names.
- Serving untrusted uploads from the primary application origin.
- Extracting archives without path and expansion limits.
- Blocking only literal
../while ignoring encoding and platform behavior. - Canonicalizing paths without authorizing the selected file.
- Storing secrets in source code or frontend configuration.
- Removing a leaked secret without rotating it.
- Adding retries during overload.
- Applying only per-instance or per-IP rate limits.
- Buffering huge request bodies before size validation.
- Assuming autoscaling alone is DDoS protection.
Best-Practice Defense Strategy
For these boundaries:
- Replace shell and interpreter calls with typed APIs.
- Expose narrow business operations rather than free-form commands.
- Isolate unavoidable process and parser execution.
- Stream uploads through size, type, signature, malware, and authorization checks.
- Generate storage names and keep uploads outside the web root.
- Resolve client object IDs through trusted metadata instead of accepting paths.
- Canonicalize and verify containment when paths are unavoidable.
- Use managed identity and secret stores with rotation and audit.
- Put limits before expensive parsing, hashing, querying, and dependency calls.
- Combine application rate limits with edge and infrastructure DDoS controls.
- Monitor resource cost, rejection patterns, and operational impact.