coding interview questions

Why SSRF interviews begin at 169.254.169.254

Hand a candidate a web app that fetches a URL the user supplies, a link preview, a webhook tester, an image proxy, and one of the first moves a good security interviewer watches for is whether they point it at http://169.254.169.254/. That address is the cloud instance metadata endpoint. On an EC2 box running with an IAM role attached, a plain GET to it returns temporary AWS credentials in cleartext. So a server-side request forgery bug in a feature nobody files under security turns into the ability to sign API calls as your server.

This is the Capital One question, whether or not the interviewer says the name. In 2019 an attacker sent a forged request through a misconfigured web application firewall running on EC2, read the role credentials off the metadata service, and used them to list and copy roughly 100 million customer records out of S3. The company later paid an $80 million regulatory penalty and a $190 million class-action settlement. Interviewers reach for this case because every link in the chain was an ordinary engineering decision that looked fine on its own and turned fatal in sequence.

What sits behind 169.254.169.254

SSRF by itself only means you can make a server issue an HTTP request to an address you influence. That sounds mild until you count what a server can reach that a browser can’t: internal admin panels on 10.x addresses, a Redis instance with no password because it’s “internal only,” the Kubernetes API, and the link-local metadata service that every major cloud exposes at 169.254.169.254. The metadata service is the prize because it returns secrets to anyone on the box who asks, with no authentication at all. It was built that way on purpose, so an instance can read its own configuration and pick up rotated credentials. The design assumed only trusted code runs on the instance. SSRF is exactly the bug that violates that assumption.

On AWS the path that matters is /latest/meta-data/iam/security-credentials/<role-name>, which returns an access key, a secret key, and a session token as JSON. Those are the keys to whatever the instance role can do. Scope that role to a single S3 prefix and the blast radius stays small. Attach a broad policy because it was quicker than writing a tight one, and the blast radius becomes the account. That gap between the credential theft and the damage it causes is an IAM design choice, and a sharp interviewer will ask you to separate the two.

The other clouds ship the same service with one built-in speed bump: they demand a custom request header that a naive fetcher won’t add on its own.

Cloud provider Metadata base URL What a naive SSRF must get past Sensitive path an attacker targets
AWS EC2 (IMDSv1) http://169.254.169.254/latest/meta-data/ Nothing. A bare GET works. iam/security-credentials/<role> returns temporary access key, secret, and session token
AWS EC2 (IMDSv2) http://169.254.169.254/latest/meta-data/ A PUT to fetch a token, then that token in the X-aws-ec2-metadata-token header; response hop limit defaults to 1 Same credential path, but every read must carry the session token
Google Cloud http://metadata.google.internal/computeMetadata/v1/ (169.254.169.254) A Metadata-Flavor: Google request header instance/service-accounts/default/token returns an OAuth access token
Azure http://169.254.169.254/metadata/ A Metadata: true header plus an api-version query parameter identity/oauth2/token returns a managed-identity bearer token

The table itself answers a common follow-up: why was AWS the poster child? Because IMDSv1 asked for nothing. GCP and Azure already required a header that a typical URL-fetching bug can’t set, so the same class of SSRF quietly failed against them.

IMDSv2 and the hop limit that stops most SSRF

AWS’s fix, shipped in late 2019, is a second version of the metadata service that most SSRF payloads can’t reach. IMDSv2 splits a read into two requests. First a PUT to /latest/api/token asking for a session token with a time-to-live. You get a token back. Then every read has to carry it in the X-aws-ec2-metadata-token header. Two properties break the usual SSRF. Most vulnerable fetchers only send GET and give the attacker no way to add arbitrary headers, so obtaining a token is already off the table. And the token request sets an IP hop limit that defaults to 1, so the response can’t travel back out through a proxy or an extra container network hop the way a plain v1 read can.

The follow-up an interviewer leans on: is turning IMDSv2 on enough? Not by itself. An instance that still accepts v1 keeps the easy path open. You have to require the new version, which means setting the instance metadata options to HttpTokens=required so v1 is refused, and pinning HttpPutResponseHopLimit=1. Someone who has actually rolled this out across a fleet will talk about the migration pain rather than the config flags: hunting down SDK versions old enough to only speak v1, and watching the MetadataNoToken CloudWatch metric drop to zero before flipping the switch, because flipping it early breaks live workloads.

Allowlists, DNS pinning, and the rebinding gap

Ask how to fix the SSRF itself and the weak answer is a blocklist: reject URLs pointing at 169.254.169.254, localhost, and the private ranges. It collapses quickly. That single IP has a pile of alternate spellings, decimal (2852039166), octal, an IPv6-mapped form, and the shortened 169.254.43518 where the last two octets fold into one number. You can also register a public DNS name that resolves to the metadata address. Block the literal string and the attacker changes the encoding. The stronger answer flips the default: an allowlist of destinations the feature is genuinely meant to reach, and outbound requests routed through a dedicated egress proxy that has no route to link-local or RFC 1918 space in the first place.

Then comes the follow-up that separates people who have shipped this from people who have read about it. DNS rebinding. Suppose you do the careful thing: resolve the hostname, confirm the resolved IP is public, and hand the URL to your HTTP client. The client resolves the name a second time when it actually connects. An attacker who controls the DNS answer returns a public IP for your check and 169.254.169.254 a moment later for the real fetch. Your validation and your request looked at two different addresses. The fix is to resolve once, validate that resolved address, and connect to the pinned IP, keeping the original hostname only for TLS and the Host header. Redirects need the same treatment, since a 302 to the metadata endpoint slips past a check you ran only on the first URL.

What good answers sound like

The questions arrive in a few recognizable shapes:

  • “Here’s a thumbnail service that takes a URL. Walk me through attacking it, then defending it.”
  • “You find SSRF but the app only shows an error, never the response body. Still useful?” A yes here shows range: blind SSRF still hits the metadata endpoint, and you read the result through response timing, DNS lookups the server makes, or by aiming it at a host you control.
  • “We’re fully on IMDSv2. Are we done worrying about SSRF?” No. SSRF still reaches internal services, and a loose hop limit or one lingering v1 opt-in reopens the credential path.
  • “How would you find every place in our codebase that can do this?” Grep for the HTTP clients, then check which ones take a host or URL from user input, and treat webhooks, PDF and screenshot renderers, and any “import from URL” feature as the first suspects.

What all of these score is whether you reason about trust boundaries instead of string filters. The credential theft is the flashy part, but SSRF keeps earning a spot in security loops because it lives on the seam between application code and cloud identity, and most engineers own one side of that seam without ever looking across it. A candidate who can name the metadata path, explain why the IMDSv2 hop limit matters, and catch the DNS-rebinding gap has shown they have stood on both sides of it.

None of the individual controls holds the line alone, and saying so out loud is part of a strong answer. IMDSv2 without required tokens, an allowlist without DNS pinning, a tight IAM role sitting behind a WAF that itself carries broad permissions: each of those is the Capital One story with a single detail swapped. The layers exist because any one of them fails in isolation, and the interview is really checking whether you know which layer catches which mistake.

newsletter

What's actually being asked right now

Interview patterns & comp trends, straight to your inbox.

No spam. Unsubscribe anytime.

newsletter

What's actually being asked right now

Interview patterns & comp trends, straight to your inbox.

No spam. Unsubscribe anytime.

1972 Soviet postage stamp commemorating the Mars 2 probe

worth a read

Mars For The Rest of Us — a weekly-or-more deep dive on the technical side of Mars exploration: rocket propulsion, microbiology, mission architecture, and everything in between. Written by Maciej Ceglowski.

Read it on Substack →
Scroll to Top