Skip to content

Instantly share code, notes, and snippets.

@leonsilicon
Last active February 3, 2022 03:36
Show Gist options
  • Save leonsilicon/3c97f270098b3956576e312a91bfc1c6 to your computer and use it in GitHub Desktop.
Save leonsilicon/3c97f270098b3956576e312a91bfc1c6 to your computer and use it in GitHub Desktop.

My Security Conclusions

I've been thinking about security in my apps, and unfortunately there's a ton of conflicting advice on the Internet about security, so I need to write out my conclusions in one place so I don't have to end up going through the security-googling spiral again every time I need to implement security for my app.

Based on what I've read, security on the web is founded on the idea that the user's webpage could be compromised at any time, and there are certain steps you can take to prevent the user from falling victim to a successful XSS attack. One of these precautions is a cookie.

Cookies

Based on my understanding, the security of cookies come from the guarantees enforced by the browser: that httpOnly cookies can't be accessed and changed from JavaScript, thus becoming safe from an XSS attack. In theory, you could create a browser that doesn't obey these rules, but this wouldn't be an issue because nobody (except you) would be using these browsers. This also holds true for other rules such as those enforced by CORS.

However, not all platforms support cookies (e.g. mobile applications). Thus, you need to have another way of authenticating users that doesn't use cookies.

One of the main questions I initially had about this issue is that your server will need a way to authenticate the user without using cookies in order to support these other platforms. And if the server has this alternative way, doesn't that mean that attackers could just fall back to this alternative way and bypass the use of cookies altogether, basically eliminating the security benefit of cookies?

I couldn't find any information about this problem online (perhaps I haven't Googled enough), but after thinking about it for some time, I've come to a conclusion about an auth flow that I think works well (but since I'm by no means a security expert, I could be wrong).

The Auth Flow I've Come Up With

This auth flow takes advantage of the fact that when the user logs in for the first time using their username + password combination, that their client is uncompromised. This is a good assumption to make, because otherwise a client could just register some sort of keylogger and/or some JS scraper that steals the value of password input boxes and steal the user's username + password combination, and without something like 2FA, there's no possible way to protect against these types of malicious attacks from unauthorized users.

When the user logs in for the first time, the client will send the server the authentication method it should use for authenticating the current session. When the user is logging in from a web browser, the client will tell the server to use cookie-based authentication. When the user is logging in from a mobile app, the client will tell the server to use headers-based authentication.

Because we assume that the user's device is uncompromised at this point, we can just send this information through a normal auth request like a parameter in the body of the /login POST request without worrying about a malicious user tampering with this parameter and deliberately setting it to header-based authentication (which is "less secure" from a browser context since it needs JavaScript to be able to access the token value, which is prone to XSS attacks).

In the server, we read this parameter from the /login request and save this authentication method in the database alongside the generated session token. The way we send this session token to the client depends on the authentication method they requested (either "cookie" or "header").

If the authentication method the client requests is "cookie," we send them an httpOnly cookie to the client containing their session token and we also send them a CSRF token as a response to be saved in JavaScript (and the CSRF token would be saved to a place like localStorage), protecting the client from CSRF attacks in which the cookie is automatically sent to the server from third-party sites. Storing the session token in an httpOnly cookie makes it invisible to the end user so if an attacker gained access to the client via XSS, they wouldn't be able to read the cookie value and then log in from their own clients (they techincally still can make authorized requests through the client's browser, but this is much harder to do so and there's no real way to protect against that once an XSS vulnerability has been exploited).

If the authentication method the client requests is "header," we only send the client their session token as a regular payload. This would be used in places that don't support cookies (like a native app), and it's fine because these places are much less susceptible to XSS attacks.

Whenever the client sends the server a request to a route that needs authentication, it will either send a CSRF token in a header and the session token in the cookie or it will send the session token in the header. Note that the client doesn't send the authentication method here, because we have to assume now that their device might be compromised and so an attacker could maliciously request the server to use header-based authentication if we accepted that paramater.

Instead, when the user sends the tokens to the server, the server will check how the tokens were supposed to be sent through a database lookup. If the authentication method attached to the session is "cookie" and the request sent the session token through a header, the request will be rejected. Likewise, if the authentication method attached to the session is "header" and the session token was sent through a cookie, that request will also be rejected.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment