默认只显示问题 —— 先自己在心里答一遍,再展开对答案。答不上来就标「不会」,下次抽认卡会先抽它。Only the question shows by default. Answer it in your head first, then open the answer. Mark the ones you miss; the flashcard round puts those first.
0 / 105道自评过self-assessed
0会Got it0模糊Shaky0不会No idea105还没做Not seen
正在读这台浏览器里的标记…Reading your marks from this browser…
标记不是打分,是给下一轮排队Marks are a queue, not a score
标「不会」的题会排到抽认卡最前面,标「会」的进低频池排最后。所以别客气 —— 觉得答得磕磕巴巴就标「模糊」。准备好了就去抽认卡。“No idea” jumps to the front of the next round; “got it” drops to the back. So be honest — if it came out shaky, mark it shaky. Then go run a flashcard round.
找一道题Find one按方向、掌握状态筛Filter by topic and mark网络与安全Web & security
In one line: the browser’s same-origin policy stops a page from reading a cross-origin response by default; CORS is the mechanism by which the server uses response headers to authorise some of those requests.
Three things to understand — this is what separates people:
The browser blocks it; the server did not refuse.The request usually went out and the server usually handled it — the browser just will not let your JS read the response. So a CORS error does not mean the endpoint did not run. Watch out with non-idempotent endpoints: the record may already exist.
Which is why the front end cannot fix it. The server has to send the headers, or you go through a proxy. No request header you add on the client will help.
Same origin means scheme, host and port all match.http and https are different origins; 3000 and 3001 are different origins.
Simple requests vs preflighted ones:GET / HEAD / POST with only safe headers and a Content-Type limited to three values (form-urlencoded, multipart/form-data, text/plain) go straight out. Anything else sends an OPTIONS preflight first. Note that application/json triggers a preflight — that is why “it is a POST but I see an extra OPTIONS request”.
Four ways to fix it:
Send the headers from the server (the real fix) — Access-Control-Allow-Origin, or app.use(cors()) in Express.
Proxy through the dev server while developing — Vite’s server.proxy, so the browser thinks it is same-origin.
In production, deploy same-origin or put a gateway in front — front end and API on the same domain, different paths.
(Historical) JSONP — GET only, obsolete.
Follow-up: “What if I need to send cookies?” — credentials: "include" on the client, Allow-Credentials: true on the server, and at that point Allow-Origin cannot be * — it must name the origin. This is the most common reason for “I configured cors and it still does not work”. “Can the preflight be cached?” — Access-Control-Max-Age, so you do not pay a round trip per request.
In one line: HTTPS is HTTP plus a TLS layer. Same protocol; the transport is now encrypted and authenticated.
It gives you three things — name all three:
Encryption — someone in the middle cannot read the contents
Authentication — the certificate proves “you really are talking to the server for this domain”. People forget this one, and it is the part that stops phishing
Integrity — tampering is detected
Roughly how the handshake goes: client says hello → server sends its certificate → client verifies the chain → they use asymmetric crypto to agree on a symmetric key → everything after that is symmetric. Why mix the two? Asymmetric is secure but slow; symmetric is fast but needs the key exchanged safely first — so you use asymmetric to exchange the symmetric key. That sentence is the bonus point.
Ports: 80 for HTTP, 443 for HTTPS.
Follow-up: “Is HTTPS slow?” — the handshake costs something, but TLS 1.3 gets it down to one round trip, and HTTP/2 and HTTP/3 are only available over HTTPS — the multiplexing usually more than pays for the encryption. So “HTTPS makes it slower” no longer really holds. “Does HTTPS make me secure?” — no. It protects the transport. XSS, SQL injection, weak passwords and broken authorisation are all still yours to solve.
In one line: a JSON Web Token is a string that carries its own signature, so the server can verify it has not been tampered with without storing it.
Three parts, separated by dots:header.payload.signature
header — which algorithm signed it
payload — the data: user id, expiry and so on
signature — the first two parts signed with your secret
The one thing you must say: the first two parts are Base64URL encoded, not encrypted — anyone can decode and read them. So never put a password or a national id number in the payload. The signature guarantees “unchanged”, not “unreadable”.
The upside:it is stateless — the server stores no session, which suits many instances and microservices, since any one of them can verify it alone.
The downsides — this half is the real question:
You cannot revoke it. Once issued it is valid until it expires. User changes their password, an admin bans the account — the old token still works.
It is bigger than a session id and rides along on every request.
Every place you store it has a risk — localStorage is exposed to XSS, a cookie is exposed to CSRF.
Follow-up: “How do you expire a JWT early?” — this is where the question separates people:
Short expiry plus a refresh token — the access token lives 15 minutes and you trade a revocable refresh token for a new one. This is the standard answer.
A blocklist — keep revoked token ids in Redis. But now you are stateful again, which gives up the main reason you chose JWT.
One classic security trap: when you verify, you must pin the algorithm you expect rather than trust the one in the header — otherwise an attacker sets alg to none and walks past the signature entirely.
First, clear up the usual confusion:these are not two options at the same level.A cookie is a browser mechanism for storing a small value; a session is a server-side way of remembering who the user is — and a session normally uses a cookie to carry its id.
Cookie
Session
Lives where
The browser
The server (memory / Redis / database)
Holds what
A small string, 4 KB or less
User data of any size
Security
The user can read it and change it
The user only ever holds an id
Can you revoke it?
Only by expiry or overwrite
Yes — delete the server-side record
The typical flow: login succeeds → the server creates a session and a session id → it goes out via Set-Cookie → the browser attaches it to every subsequent request → the server looks the user up by that id.
You have to know the four cookie security attributes:
HttpOnly — JS cannot read it, which stops XSS from stealing the cookie
Secure — only sent over HTTPS
SameSite — Strict / Lax / None, the main defence against CSRF
Max-Age / Domain / Path — its scope
Follow-up: “Session or JWT?” —
You need to kick someone out right now (admin panels, anything touching payments) → session
Many services, cross-domain, one API for both mobile and web → JWT, with a refresh token
They will also ask: “What happens to sessions across several instances?” — keeping them in memory means “refresh the page and I am logged out” when the request lands on a different instance. The fix is to put sessions in Redis, or sticky sessions (not recommended).
Name the five classes first, then give examples — it reads as organised: 1xx informational, 2xx success, 3xx redirect, 4xx client error, 5xx server error.
Code
Means
When
200 OK
Success
A successful GET / PUT / PATCH
201 Created
Created
A successful POST — send a Location header
204 No Content
Success, nothing to return
A successful DELETE
301 / 302
Permanent / temporary redirect
Browsers cache 301, so a wrong one is hard to take back
304 Not Modified
Unchanged, use your cache
Paired with ETag or Last-Modified
400 Bad Request
The request is wrong
Missing parameter, bad format, failed validation
401 Unauthorized
Not logged in, or the token is invalid
“Who are you?”
403 Forbidden
Logged in but not allowed
“I know who you are, and you cannot do this”
404 Not Found
No such resource
409 Conflict
Conflict
Duplicate signup, concurrent edit
422
Semantically wrong
Well-formed but invalid for the business rules
429
Too many requests
Rate limiting
500
Server failed
An uncaught error
502 / 503 / 504
Bad gateway / unavailable / timeout
Upstream is down, in maintenance, or too slow
401 vs 403 is the pair they ask about most: 401 means not authenticated, 403 means authenticated but not authorised. One detail from practice: to avoid leaking whether a resource exists, some endpoints return 404 for “not allowed” as well.
Follow-up: “Should a business error be a 4xx or a 200 with an error code?” — REST says 4xx, so the HTTP semantics carry the error, but watch out: some older gateways swallow the body of a 4xx. GraphQL always returns 200 and puts errors in the errors field, because one request can be partially successful and no single status code says that. Drawing that contrast earns you points — in the Federation course, extensions.code is exactly this.
In one line: smallest scope to largest — unit → integration → end-to-end. That is the testing pyramid: higher means slower and flakier, so you write fewer of them.
Unit — one function or one component, everything else mocked. Fast, numerous, and precise about where the problem is. A pure function; a React component rendering.
Integration — do a few modules work together, possibly against a real database or a test server. Call an API endpoint and assert the row really landed.
End-to-end — a real browser walking a whole user journey. Playwright or Cypress. Closest to reality, and also the slowest and the most prone to random failure.
Others worth mentioning: regression tests (so old behaviour does not break), snapshot tests (comparing rendered output — which easily degrades into rubber-stamping “update snapshot”), performance and load tests, accessibility tests, and smoke tests for a quick check of the main flow after a deploy.
Testing Library’s core idea is worth stating:“test it the way a user uses it” — find elements by visible text and role, not by class name or internal component structure. Then refactoring the internals does not shatter the tests.
Follow-up: “What coverage number should you aim for?” — do not give a number. The right answer is that coverage tells you code was executed, not that the assertions are any good. Here is a measured example: in the source project behind the Federation course, six endpoints that all just return null still passed 3 tests, and of the 4 passes in node-subgraph, 3 were an empty implementation happening to satisfy the assertion. So a green test does not mean you got it right — the strength of the assertions matters more than the coverage percentage.
99 道来自面试题库 #269–#387;TypeScript 深度那 6 道是 DrillLab 自出的(senior 补强,卡片上有标注)。答案都是 DrillLab 写的,所以讲解里的代码块一律标「示意」。每道题都能点回它出处的那一节课。99 questions come from the interview bank (#269–#387); the 6 TypeScript deep-dive ones are DrillLab-made (marked on the card). All answers are written by DrillLab, so every code block here is labelled “demo”. Each card links back to the lesson it came from.