Library · Summary & review

Defending APIs

By Colin Domoney. Authorize every object, lock down the contract, pin the token's algorithm: API defense from a practitioner, with a vendor never far away.

FR EN
Defending APIs book cover, Colin Domoney, Packt, 1st edition

Defending APIs

Defending APIs: Uncover advanced defense techniques to craft secure application programming interfaces

6.4 /10

« Sharp moves, code you shouldn't copy, and a vendor never far away. »

  • AuthorColin Domoney
  • ForewordChris Wysopal (Veracode), Isabelle Mauny (42Crunch)
  • OriginalPackt, 2024 · 1st edition, ExpertEdge course version, 6 h 24
  • LanguageEnglish only
  • This page~6 min read
  • Read it Read on ExpertEdge Code : 50% off the yearly plan. Affiliate link: I earn a commission if you subscribe with this code. My ratings do not depend on it.
Book rating across 5 dimensionsIdeas6/10Practical7/10Readability7/10Aged well6/10Examples6/10

API defense from a practitioner: per-object authorization, an OpenAPI contract audited in CI, locked-down JWTs, mTLS between services.

Why this book

A financial API places an order in two calls: set it up, then execute it. But the second call takes the client's word for the source account. An attacker swaps it for an empty account, and the API checks nothing again (lesson 63).

Colin Domoney tells stories like this one in Defending APIs. His thesis, as I read it: you defend an API both before production and after. Before is shift-left: contract, code analysis, tests. After is shield-right: the entry gateway, traffic between services.

For my part, I work in fintech and crypto, in Go and in PHP with Symfony. What does this course bring to someone who already defends sensitive APIs? I read it on ExpertEdge, skimming the basics, the breaches and the reconnaissance tools. My comparisons are flagged "My reading".

Six ideas in two families: 1 to 4 before production, 5 and 6 once live.

The ideas that stay

1The A-B test proves one user can't read another user's object

First family: the code. The book follows the API Top 10 of OWASP (Open Worldwide Application Security Project). At the top sits BOLA (Broken Object Level Authorization): a logged-in user changes the number in the URL and reads another customer's receipt.

The remedy: "authorize each and every access to an object" (lesson 19). BOLA has a cousin, BFLA (Broken Function Level Authorization): being logged in does not unlock admin functions.

The proof: the A-B test. User A creates a resource, user B tries to access it. "If you can, then you have an authorization issue" (lesson 49). The author advises writing these tests yourself, but never shows one (lesson 63).

My reading

After auditing a Go service, I only keep as permanent tests the attacks a good-faith change could bring back. A route that forgets the owner would be one of them.

2Three separate objects shut the door on mass assignment

Authorization says who touches an object, not which fields the API accepts. Mass assignment comes from a shortcut: the framework copies every incoming field to the database. The attacker adds "is_admin": true, and the API stores it.

The book's fix comes down to three objects (lesson 65):

  • The input object: what the client may send.
  • The database object: what gets stored, password hash included.
  • The output object: what the client may read.

For the author, most data flaws come from mapping objects straight onto each other "without considering the sensitivity of the data" (lesson 65). My Go translation, not the book's (which uses Python):

type UserIn struct { // what the client may send
	Email    string `json:"email"`
	Password string `json:"password"`
}

type UserInDB struct { // what gets stored
	Email        string
	PasswordHash string
	IsAdmin      bool
}

type UserOut struct { // what the client may read
	Email string `json:"email"`
}

func createUser(w http.ResponseWriter, r *http.Request) {
	var in UserIn
	if err := json.NewDecoder(r.Body).Decode(&in); err != nil {
		http.Error(w, "invalid request", http.StatusBadRequest)
		return
	}
	// a submitted "is_admin" has nowhere to go: UserIn has no such field
	db := UserInDB{Email: in.Email, PasswordHash: hashPassword(in.Password)}
	save(db)
	json.NewEncoder(w).Encode(UserOut{Email: db.Email}) // no hash, no role
}
// hashPassword (argon2id or bcrypt) and save: not shown

Decoding only fills UserIn. The move to UserInDB is written by hand, hashing included, as in the book. Nothing goes out without room in UserOut.

3The OpenAPI contract becomes a rule that continuous integration enforces

These objects can also live in an OpenAPI contract, the standard format for an API's routes, fields, types and limits. For the book, "The definition is the contract-anything not in the contract is invalid" (lesson 55).

An audit flags whatever is left open, such as an integer with no maximum. The contract requires authentication everywhere, then relaxes it route by route: a forgotten route stays closed (lesson 56).

The audit runs in continuous integration (CI), the pipeline that tests every change before merging, and a serious flaw can break the build. The tool must stay fast, "ideally for less than one minute" (lesson 58).

4A JWT is verified with the server's algorithm, never the token's

One trap escapes the contract audit: the JWT (JSON Web Token), a signed token that carries the user's identity. In CI, Semgrep, a tool that searches code for patterns, catches a token decoded without checking its signature (lesson 58). Yet the JWT's header announces the algorithm, and the client is the one who writes it.

The attacker can write none there, "no signature", or sign symmetrically with the server's public key (lessons 47 and 48). A library that follows the header then accepts the forged token.

The fix: "be explicit about the algorithm and key used and ignore the values supplied in the JWT header" (lesson 60).

5At the gateway, the client never sees the real token

Second family: the API once live. The API gateway receives outside calls and routes them to the services. Yet a JWT is signed, not encrypted: whoever receives it can read it.

A JWT glues three parts together: header, body, signature. The book describes two ways to hide the body (lesson 86):

  • The opaque token (phantom token): the client only gets a reference, swapped for the real JWT on every call, at the cost of processing time and "being reliant on a central single point of failure".
  • The split token: the client only gets the signature, and the gateway finds the whole token in its cache (fewer bottlenecks, per the author).

No code, and nothing on revoking a token already issued (lesson 86).

6Between two services, mTLS is a given

Gateways "typically protect North-South traffic and do not protect West-East traffic", the traffic between services (lesson 80). For the latter, the book prescribes mTLS (mutual TLS). TLS is the encryption behind HTTPS. In mutual mode, client and server each present a certificate and check the other's.

His conclusion: "mTLS should be considered a given in any microservices deployment" (lesson 84). Nothing on rotation, revocation or token-certificate binding.

My reading

My article on revocation in mTLS covers one of these gaps: recheck the certificate on every request, even on an open connection. Another one binds the session to the client certificate's serial number: a stolen cookie is refused elsewhere, unless the private key is stolen too. That's a neighbor of token-certificate binding.

Read it on ExpertEdge

This book is part of ExpertEdge, Packt's subscription for technical books. The code ODILON50 takes 50% off the yearly plan: the book on ExpertEdge. Affiliate link: I earn a commission if you subscribe with this code, and my ratings don't depend on it.

My take, honestly

My verdict, 6.4 out of 10: a good recap, little that's new for a seasoned developer. What does this course bring to someone who already defends sensitive APIs? Six sharp moves, and a lot of pages to skim. Hence a short page: a good third of the course covers attacks, and the end a corporate program.

Its strength: named moves, each boiled down to a testable rule.

Its weaknesses, with what they cost you:

  • The sample code is not for copying. Passwords hashed with md5, an outdated algorithm, the wrong refusal code, incomplete access control (lessons 63 to 65, details below). Cost: take the principles, never the snippets.
  • No defense in Go or PHP, no regulated finance. No banking security profile, and Open Banking gets one passing mention. Cost: everything needs translating and completing elsewhere.
  • The author sells a little of what he recommends. Domoney was an evangelist at 42Crunch, a security tooling vendor whose name his maturity model bears (lesson 90). In the Protection domain, the mature level requires an API firewall: a filter that rejects whatever the contract doesn't allow, like the 42Crunch one the course demos (lesson 80). Cost: every tool it names is a possible ad.
  • Logging and rate limiting stay thin. The course logs at the gateway (lessons 79 and 80) without saying what to keep or for how long. Calls are capped "usually" by IP address (lesson 67). The limiter in my personal Go gateway too, I confess. Cost: you design these alone.

My advice: read lessons 19, 49, 55 to 66, 84 and 86, and skim the rest.

Odilon

Still relevant in 2026?

The principles, yes. Three points have aged or were already missing:

Who is it for?

Read it if

  • You want named, testable moves
  • You're preparing a security review (checklists, lesson 90)
  • You're writing your first OpenAPI contract
  • You're setting up an API security program

Skip it if

  • You want Go or PHP code
  • You work under FAPI or PSD2, which the book ignores
  • You want the attack side first: read Andrew Hoffman's Web Application Security (the summary, 7.8/10)

Based on the ExpertEdge course version, 104 lessons announced, quizzes included. "My reading" and the Go code are mine.

Source: ExpertEdge course version (Packt 1st edition, 2024), lessons 17 to 23 and 44 to 92 read in detail.

Comments (0)

Browse the whole library

More book notes coming: one book at a time, the marrow only.