Server-Side Vulnerabilities
Request-processing server flaws — SSRF and DNS rebinding, insecure deserialization, HTTP request smuggling, unsafe file upload, Host-header attacks, web-cache poisoning/deception and file inclusion.
14 questions
JuniorTheoryVery commonWhat is Server-Side Request Forgery (SSRF), and why does a destination allowlist beat an internal-address denylist?
What is Server-Side Request Forgery (SSRF), and why does a destination allowlist beat an internal-address denylist?
SSRF is the server fetching a client-controlled URL, so the request leaves from inside the network and reaches internal services. A denylist of internal addresses is never complete; an allowlist of hosts and ports denies the rest.
Common mistakes
- ✗Treating SSRF as a browser problem that CORS solves
- ✗Believing a denylist of private ranges is a complete filter
- ✗Assuming there is no impact when the response is discarded
Follow-up questions
- →Why does a redirect returned by an allowed host need re-validation?
- →Which schemes and ports should an outbound fetch allowlist permit?
JuniorTheoryVery commonWhy must a server not trust the Content-Type header or the filename extension of an uploaded file?
Why must a server not trust the Content-Type header or the filename extension of an uploaded file?
Both come from the request, so the client sets them freely and any content can be declared an image. The server derives the type from the bytes, stores the file outside the web root under a generated name and serves it without execution.
Common mistakes
- ✗Trusting the declared Content-Type as if the server measured it
- ✗Validating the extension instead of the actual file bytes
- ✗Storing uploads in the web root under the client-supplied name
Follow-up questions
- →Why does storing outside the web root matter more than any name check?
- →What does re-encoding an accepted image add over type detection?
SeniorTheoryVery commonWhat is XML External Entity (XXE) processing, and what is the correct fix at the parser level?
What is XML External Entity (XXE) processing, and what is the correct fix at the parser level?
A document can declare an entity whose value is a URI, and a parser with external entity resolution enabled fetches it, so parsing reads local files or issues requests from the server. Fix it in the parser by disabling those definitions.
Common mistakes
- ✗Believing a document cannot declare its own entities
- ✗Trying to filter the input instead of configuring the parser
- ✗Assuming a parse cannot issue outbound requests
Follow-up questions
- →Why is disabling document type definitions preferred over an entity denylist?
- →Which other document formats reach a parser with the same setting?
JuniorTheoryCommonWhat makes deserializing untrusted data dangerous, and what is the safe default for external input?
What makes deserializing untrusted data dangerous, and what is the safe default for external input?
A native deserializer rebuilds whatever types the bytes name, so untrusted input decides which classes are created and which callbacks run — execution, not parsing. The safe default is a data-only format parsed into declared types.
Common mistakes
- ✗Thinking the risk is only an oversized or malformed payload
- ✗Validating after the objects have already been constructed
- ✗Believing a signed stream makes any format safe to deserialize
Follow-up questions
- →Why does validation after construction come too late?
- →What does an explicit type allowlist add if the format is data-only?
MiddleTheoryCommonWhy did cloud instance metadata endpoints make injection class SSRF critical, and what does IMDSv2 change?
Why did cloud instance metadata endpoints make injection class SSRF critical, and what does IMDSv2 change?
The metadata service answers unauthenticated requests from the instance and can return the attached role credentials, so one server-side fetch becomes cloud access. IMDSv2 requires a token taken by PUT and a low hop limit.
Common mistakes
- ✗Rating metadata exposure as information disclosure rather than credential theft
- ✗Relying on an outbound denylist entry for the metadata address
- ✗Assuming a short credential lifetime removes the impact
Follow-up questions
- →Why does the hop limit matter as much as the session token?
- →What least-privilege change limits the blast radius if metadata is still reached?
MiddleTheoryCommonWhat is mass assignment, and why does binding a request body straight onto a domain object cause it?
What is mass assignment, and why does binding a request body straight onto a domain object cause it?
The binder writes every field it finds in the body onto the target type, so attributes the client should never own — role, owner id, balance — are set whenever the request mentions them. Bind into an input model with only the editable fields.
Common mistakes
- ✗Confusing it with bulk database updates
- ✗Assuming the binder only fills documented fields
- ✗Hiding fields from responses instead of restricting what can be bound
Follow-up questions
- →Why is an allowlist of bindable fields safer than a denylist of protected ones?
- →How should ownership fields be set if they never come from the request?
MiddleTheoryCommonHow can a client-supplied filename write a file outside the upload directory, and how is that stopped?
How can a client-supplied filename write a file outside the upload directory, and how is that stopped?
Joining the received name onto the storage path lets relative segments walk out of the directory, so the write lands where the name points. Fix it by generating the stored name server-side, or by rejecting a resolved path outside the base directory.
Common mistakes
- ✗Assuming the framework already normalises the received filename
- ✗Filtering suspicious characters instead of resolving the final path
- ✗Believing traversal in a name only enables reads
Follow-up questions
- →Why is a generated stored name stronger than sanitising the received one?
- →What must be checked after the path is resolved, and against what base?
MiddleTheoryOccasionalHow does DNS rebinding defeat a validate-then-fetch URL check, and what closes that gap?
How does DNS rebinding defeat a validate-then-fetch URL check, and what closes that gap?
Validation resolves the hostname and approves the address, then the fetch resolves it again and gets a different one — a time-of-check to time-of-use gap. Close it by resolving once, validating that address and connecting to it.
Common mistakes
- ✗Validating the hostname string instead of the address actually connected to
- ✗Assuming one resolution result stays valid for the whole request
- ✗Treating rebinding as a browser-only concern
Follow-up questions
- →Why must a redirect be re-validated against the same address rules?
- →Where does an egress proxy make pinning easier to enforce?
SeniorTheoryOccasionalA server-side fetch returns nothing to the caller — how do you detect it in your own telemetry and contain it?
A server-side fetch returns nothing to the caller — how do you detect it in your own telemetry and contain it?
Nothing shows in the response, so look at what the server emits: resolver queries, outbound connection records and proxy logs reveal destinations no feature should reach. Contain it with an egress proxy that allowlists destinations and drops redirects.
Common mistakes
- ✗Treating an unreturned response as proof of no impact
- ✗Looking only at inbound request logs for evidence
- ✗Allowing the fetcher to follow redirects after validation
Follow-up questions
- →Why are resolver query logs useful when no connection succeeds?
- →What belongs in an egress proxy allowlist besides the hostname?
SeniorTheoryOccasionalWhat is web-cache poisoning through unkeyed input, and how is a shared cache kept safe?
What is web-cache poisoning through unkeyed input, and how is a shared cache kept safe?
An input that changes the response but is not part of the cache key lets one request store an entry that every later client receives. Keep key and response inputs aligned — include the header in the key or strip it at the edge.
Common mistakes
- ✗Assuming only static assets end up in a shared cache
- ✗Treating it as node hardening instead of a keying problem
- ✗Thinking encoding a reflected header removes the caching impact
Follow-up questions
- →Why does the key needing to match the response inputs cut both ways?
- →How does cache deception differ from poisoning in what gets stored?
SeniorTheoryOccasionalWhy is the Host header untrusted input, and what breaks when an application builds URLs from it?
Why is the Host header untrusted input, and what breaks when an application builds URLs from it?
Host arrives in the request, so the client chooses it. An application composing absolute links from it can send a password-reset URL pointing elsewhere, and a cache may store that response. Take the public origin from configuration instead.
Common mistakes
- ✗Assuming the web server normalises Host before the application
- ✗Building absolute links from request headers rather than configuration
- ✗Ignoring that a cache can persist the poisoned response
Follow-up questions
- →Which other forwarding headers deserve the same treatment as Host?
- →Why is a password-reset link the classic place this surfaces?
SeniorTheoryOccasionalWhat causes HTTP request smuggling, and why is it a front-end and back-end parsing disagreement?
What causes HTTP request smuggling, and why is it a front-end and back-end parsing disagreement?
Two servers reusing one connection disagree on where a request ends, because one trusts Content-Length and the other Transfer-Encoding, so leftover bytes start the next request. The fix is consistent framing and rejecting ambiguous headers.
Common mistakes
- ✗Explaining it as packet reordering rather than header framing
- ✗Confusing the desync itself with the cache effects it enables
- ✗Believing a downgrade from HTTP/2 reduces framing ambiguity
Follow-up questions
- →Why does connection reuse between the two servers make the desync persist?
- →What should a proxy do with a request carrying both framing headers?
SeniorDebuggingOccasionalThis avatar-upload handler trusts the request for both the file type and the destination path — find and fix the flaws.
This avatar-upload handler trusts the request for both the file type and the destination path — find and fix the flaws.
Three trust defects. The declared content type is client-set, so detect the type from the bytes. The received filename is joined onto the storage path, so generate the stored name. The directory sits in the web root, so move it out and serve without execution.
Open full question →Common mistakes
- ✗Fixing the type check but keeping the client-supplied filename
- ✗Leaving the storage directory inside the web root
- ✗Writing the file first and validating it afterwards
Follow-up questions
- →Why does a generated stored name remove the traversal question entirely?
- →What must the serving handler set so the browser never interprets the file?
SeniorDesignRareA payments service consumes job envelopes from a queue that any partner may publish to, and today it restores each envelope with the platform native deserializer straight into domain objects; the consumers hold database credentials. Constraints: the third-party producer cannot be moved to a new client library this quarter, the envelope must stay backward compatible with jobs already in flight, processing may not pause during the migration, and no library that picks types out of the payload may be introduced. Design the deserialization defence — the envelope format you move to, how the set of constructible types is constrained, where integrity and authorization are checked relative to parsing, and what signal proves the old path is no longer used.
A payments service consumes job envelopes from a queue that any partner may publish to, and today it restores each envelope with the platform native deserializer straight into domain objects; the consumers hold database credentials. Constraints: the third-party producer cannot be moved to a new client library this quarter, the envelope must stay backward compatible with jobs already in flight, processing may not pause during the migration, and no library that picks types out of the payload may be introduced. Design the deserialization defence — the envelope format you move to, how the set of constructible types is constrained, where integrity and authorization are checked relative to parsing, and what signal proves the old path is no longer used.
Move the envelope to a data-only format parsed into a declared input model with a fixed field set, rejecting unknown types. Verify the message signature before parsing and authorize the job in the consumer after it. Cut over when the native reader records zero calls.
Common mistakes
- ✗Keeping a native deserializer behind a denylist of class names
- ✗Verifying the signature after the payload has already been parsed
- ✗Treating a restricted queue as proof that the payload is trusted
Follow-up questions
- →Why must the integrity check run before parsing rather than after it?
- →How does the input model keep the envelope backward compatible?