Skip to content

Instantly share code, notes, and snippets.

@xeoncross
Last active January 7, 2020 16:27
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save xeoncross/de8adbf2b4f636ba9d3a651672c8f0f5 to your computer and use it in GitHub Desktop.
Save xeoncross/de8adbf2b4f636ba9d3a651672c8f0f5 to your computer and use it in GitHub Desktop.
Ideas for an authentication library in Go to handle login, sessions, and request protection for HTML forms as well as single-page apps (or other clients)

Login / Auth System

Protect against:

  • DoS
  • Lock accounts after failed attempts
  • forgotten password tokens (or remeber me, CSRF, etc..) being used as logins (hash them all)
  • someone faking a login attempt and that action loging out valid user sessions
  • CSRF and XSS using a token and hashed(server-secret + token) token

Supports:

Design

Four storage interfaces (though 2, 3, & 4 can point to the same store)

  1. External store for account (provided by implementat)
  2. Temp session store (prevent CSRF for logins)
  3. Session store (token -> external.id)
  4. Remember me store (token -> external.id)

Session (and temp sessions) use browser session cookies that are erased when browser is closed (and server-side when X minutes/hours have passed). This helps prevent our server from filling up from a DoS attack creating millions/billions of sessions.

Remember me store is more DoS safe since only valid login users can populate the store. Therefore we can keep this data around for months without spending much space.

Consider the interfaces needed: https://github.com/volatiletech/authboss/blob/master/user.go

Storage implementations

  • memory (testing)
  • boltdb (one server)
  • redis (many servers)
  • mysql (many servers)

Remember Me

Dangerous: https://stackoverflow.com/questions/244882/what-is-the-best-way-to-implement-remember-me-for-a-website

  • An additional cookie token (random) using httpOnly and HTTPS secure only
  • This token is stored hashed on the server (so that a DB compromise does not allow the attacker to use it)
  • This is a "second" registration system storing where token -> external.id to lookup user to create session from and data is kept here for days/weeks

DoS Protection

  • Non-authenticated sessions should be removed from the system every 5-10 minutes. They only need to last long enough to be used on a login form.
  • Invalid login attempts can't terminate valid existing sessions

Data bits

  • Session token is stored in httpOnly cookie (prevents XSS)
  • Nonce token is hashed(server-secret + session token) and sent to client

We can set the cookie automatically with a middleware. However, the nonce hash must be inserted into the template or sent to the client manually by the handler.

We can also set the hashed token in a cookie that JS can read so it can pull it out to use in the body or request headers. The server should not look for this hashed value in the cookie to validate the request. It's only there to provide an easy place for the client to fetch it. https://stackoverflow.com/a/39926988/99923

However, please note that this approach means any malicious javascript on the page can read the cookie at any time to find the hashed token and submit that (with the browser providing the main cookie token) to succesfully make a request on behalf of a user.

If the hashed token was only provided once from an endpoint (assuming a SPA here), then Javascript would have to find the value in the React/Angular request object instance and read from there (or use that object itself for the request).

Writing state to the client

When the request comes in we can write state (or the cookie) to the client automatically using a middleware either before or after the main handler.

  1. Before the handler might result in data needing to be changed and multiple set-cookie headers sent.
  2. After the handler the cookie can't be sent because the body was already sent.

The alternative is to have the handler call auther.SendCookies() when it's ready.

To see if method 1 will work, lets look at the senarios

  1. user brings valid sesison
  2. User brings old, expired session (left browser open, but server cleared records) 2.1. with valid remember me?
  3. User sends no session (closed browser which cleared cookie) 3.1. with valid remember me?
  4. User sends invalid data (hijacked session?) 4.1. with valid remember me?

In each case it's safe to set a cookie before calling the handler since we are not storing anything but a unique token (and it's hashed version) used by all requests for this client. We can safely set it on the http.ResponseWriter knowing the handler won't need to change anything.

However, we might have two instances that would require a token change:

User sends valid session and 1) requests a LOGOUT or 2) requests a token change (rotating tokens for security?) For LOGOUT, is there some reason to change the token or should we just clear the external storage link and reuse?

Prior Work

https://github.com/qor/auth https://github.com/volatiletech/authboss/

  • Requires too much work (implementing too many interfaces)
  • multiple Put/INSERT operations per request due to single value->method getter/setter mapping though that might be incorrect as explained here
  • No CSRF protection or Double-Submit Cookie support
  • Large project to audit
  1. Authboss reads request looking for input with certain names: https://github.com/volatiletech/authboss/blob/4d85b23e8ac0a6e80e7c76e167bf8043d0836cba/defaults/values.go#L213
  2. then validates the request fetching the needed data: https://github.com/volatiletech/authboss/blob/master/register/register.go#L75
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment