The simplest XSS attack may look the following:
- The attacker finds
inputfields on the webpage
- Once the attacker finds such an input that is vulnerable, the attacker crafts a link that will inject a give snipper into the page, and sends it to the attacked person
- After the link is opened, it is up to injected script and the attacker on what’s going to happen next
The reason why XSS attacks rank in the top 10 OWASP security risks, is that using this exploit, attackers can easily get access to secrets stored in LocalStorage, SessionStorage or even cookies. This is the main reason why OWASP recommends never to store sensitive information in these storages. Once the attackers manage to read them, they can potentially impersonate the attacked user accounts.
Defending against XSS attacks are not trivial - you have to make sure you never inject unknown user input into the page. You must use the escape syntax for the part of the HTML document you’re putting untrusted data into:
- HTML escape before putting untrusted data into HTML
- basically any function, that evaluates code.
- CSS escape and validate before putting data into HTML style properties
- This is more and more pressing, as a lot of React components chose this approach
To learn more about XSS prevention, read the XSS Prevention Cheat Sheet: https://www.owasp.org/index.php/XSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet.
Never ever in plain text. Also, never ever without salt. Without salt, your passwords can be reversed using Rainbow tables.
CSRF or Cross-Site Request Forgery is an attack vector which exploits the way HTTP requests are sent from the browser: if a user has cookies for the site
foo.com, no matter who starts a given request, cookies set by
foo.com will always be sent with the request.
It becomes an elevated risk, once your users are logged in. If you don’t defend your application against CSRF attacks, you risk your users’ account to be stolen.
The simplest CSRF attack may look the following:
- The attacker finds an unprotected
<form>on the page, like user update.
- The attacker crafts an URL which calls the
actionof the given
form, to follow our example, to update the email address of the user.
- The attacker requests a password reminder and takes over the account.
To protect your users against CSRF attacks, you have to add synchronizer (CSRF) tokens to your form which end up changing state. A CSRF token is:
- unique per user session,
- a large random value,
- generated by a cryptographically secure random number generator.
Once you have this token, you have to add it as a
hidden input field in your form, and the server rejects the request action of the token fails validation.
To implement it for Node.js applications, you can use the csurf package:
const cookieParser = require('cookie-parser')
In the view, you have to pass it to the hidden input field:
<form action="/process" method="POST">
To learn more about CSRF prevention, read the CSRF Prevention Cheat Sheet: https://www.owasp.org/index.php/CSRF_Prevention_Cheat_Sheet.
JWTs or JSON Web Tokens are an open, industry-standard RFC 7519 method for representing claims securely between two parties. Usually, it includes some data on the user, like name and email, as well as two other values, called
iat (issued at) and
exp (expires at). JWTs are signed by a secret key, but the payloads are (in most cases) not encrypted, so you should not store any sensitive information in JWTs.
Take the following JWT as an example: “eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJmb28iOiJiYXIiLCJpYXQiOjE1MTYxNDI0NTMsImV4cCI6MTUxNjE0MjUxM30.cC4J0Vd6zvJmD7-BIDCB75LhwYcstePs7xDeFj-ZHAg”. It consits of three base64-encoded parts, each delimited with a
Let’s take a look at them:
- the first describes the algorithm used and the type,
- the second is the actual payload,
- the third part is the signature.
JWTs are usually used for authenticating against a REST API from single page applications. It can simplify how you authenticate against different APIs hosted on different subdomains, that wouldn’t work out of the box with cookies because of the same-origin policy.
Generally speaking, when it comes to security and authentication, you should stick with session-based authentication:
- cookies can be a lot smaller than JWTs, so you can save bandwidth using them.
You can use JWTs if they are only used for a very short amount of time, like to authenticate a site on a subdomain, download files, or password resets. A scenario like that be seen below:
You should never check your secrets into your version control system, in a plain text form. If you’d like to store secrets in version control, you have to encrypt it. A couple of tools you can use for it:
However, this won’t enable you to easily share secrets between projects - if you want to do that, you can adopt Vault. Vault secures, stores, and tightly controls access to tokens, passwords, certificates, API keys, and other secrets in modern computing. Vault handles leasing, key revocation, key rolling, and auditing. You can use Terraform to start up a Vault cluster quickly on AWS.
To better understand Vault, I recommend following the Vault Getting Started tutorial.
- A collection of awesome software, libraries, documents, books, resources and cool stuff about security
- Snyk Blog
- Schneier on Security
- Troy Hunt on Security
- The RisingStack Blog on Security
Security is a huge topic - so most probably, I missed important items. What would you add? Please let me know in the comments!