Web app anti-CSRF & anti-XSS playbook

TL; DR

There’re 2 major ways to store the JWT in the frontend.

  • A: In the local storage and send it via a custom header.

For method A, it’s CSRF-safe but is vulnerable to XSS. For method B, it’s XSS-safe but is vulnerable to CSRF.

Luc Engelen’s opinion (also mine): CSRF is easier to deal with but the amount of work to fight XSS is proportional to the size of the frontend. Hence the method B is preferable.

Prelude: what’s a JSON Web Token?

Here’s an introduction.

JWT, from a cryptography perspective, it only ensures integrity. So the token itself standalone is not a good approach to implement an authentication flow — anyone who got the token can impersonate you! …


Don’t let orders stuck at “pending” in your System

We all have many state machines in our software systems. Take a charging system for example. A charge can have the following states: pending , completed and bad_debt. The goal of the system is to finally charge all the pending orders until they’re completed or we consider we can never collect the money.

Message queue

It’s easy to think of using a message queue. Whenever a new pending order is created, you enqueue a charge job to handle it.

However, as the system runs day by day, you will finally find out some of the orders stuck in the pending state. It’s not hard to understand because systems fail from time to time: if your message queue gets killed brutely your jobs may be lost and there will be no one in charge of those pending orders. …


I thought it’s a common-sense but dozens of times I was told that I was wrong when I reviewing the code. So here’s the shortest yet full explanation.

  1. In ActiveRecord, all CRUD operations are wrapped inside a transaction.


Define ActiveRecord::Relation interface

An “ActiveRecord::Relation interface” (short as Relation interface, RI) is an interface with `ActiveRecord::Relation` return type. Examples are:

class Address
# Address#orders is an RI
has_many :orders
# User.active is an RI
scope :active, -> { where(...) }
# Address#nearby_addresses
def nearby_addresses
where(...).order(...).select(...)
end
end

Normalization of RI

For RI which invoked group, order, select, or any other method with side effects, we should normalize it so their side effect will not leak to the outside.

Here the side effect means it changed the inner state of the Relation so the following chained method will have a different result before the state change.

Why normalization?

An RI, differ from other local code, its result may be used in other places, very often in other files. And at the place where someone invoked this RI, the side effect is nearly impossible to be known by the caller. …


Before talking about Form Objects, let’s talk about refactor first. Many times we refactor the code because we’ve changed the interface of a function. The pain of modifying multiple places is understandable because the implementation and the usages of the function are meant to be coupled by its interface. By John Ousterhout’s definition from the book A Philosophy of Software Design, this is called “Information Leakage”.

Information leakage occurs when the same knowledge is used in multiple places.

And the leakage of the interface is unavoidable because interfaces are meant to be used from the outside. However, the fact that we can’t decouple usages from the definition does not mean we can’t improve our design. …

Yang Liu

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store