Logout Security

After building a login system, there are still a few considerations to take into account. Authorizing each endpoint is a good way to make sure that only logged-in users have access to the web app. Securing logout is also imperative, especially on shared computers.

When a user clicks the logout button, it’s common to redirect them to a publicly-accessible page. However, what happens when he or she (or the next person to use the computer) clicks the browser’s back button? Chances are that the browser has a locally-cached version of content that should no longer be available now that the user has logged out. Fixing this is pretty simple–just add the following headers to the response for each page with authorized data:

response.headers["Cache-Control"] = "no-cache, no-store, must-revalidate"
response.headers["Pragma"] = "no-cache"
response.headers["Expires"] = 0

These headers (for HTTP 1.1, HTTP 1.0, and proxies, respectively) tell the browser (and any proxy) not to cache this response. And that’s it! Now if a user logs out then navigates through the browser history, any authorized page (i.e. any page that was sent with the headers above) will not be displayed. (Ideally, navigation to those pages when logged out would trigger a redirect to the login page.) And for most web applications, there shouldn’t be a noticeable performance hit–if there is, the time-loading tradeoff should be well worth the security benefit.

Have any other security tips for logging in or out of a web application? Let me know in the comments below!

Happy coding!
Ryan from The Bunch

Authorization Decorators

Last time I wrote about decorators, I explained their usefulness in rate limiting API endpoints. Today, I’m demonstrating another common use: authorization.

If you store the usernames of logged-in users in the session, your endpoint and decorator might look something like this:

@app.route("/get_secret", methods=["GET"])
@login_required
def secret_info():
    return "The truth is...I am Iron Man."

def login_required(func):
    def login_wrapper(*args, **kwargs):
        if not flask.session.get("username"):
            return "Unauthorized", 401
        return func(*args, **kwargs)
    return login_wrapper

That’s it! Just add @login_required to any endpoint at which you want to restrict access. It’s likely that you’ll want to do something more complex–maybe different users have different roles, or there are other attributes to check–but the concepts will be the same. There are good libraries for integrating HTTP Basic Authentication into your API which is another useful authentication method.

What authentication decorators do you use in your applications? Let me know in the comments below!

Happy coding!
Ryan from The Bunch

Salts and Hashes

For the past few years, “123456” and “password” have been the top two most-common online passwords. Storing passwords securely is very important for any application–not only to protect the users’ accounts from unauthorized access, but also to protect the passwords themselves. Because many people use the same passwords for multiple applications, a security breach on one site could have ramifications for other sites all over the internet. There are many methods used to protect passwords from getting into the wrong hands, and salts and hashes are two of them.

A hash function converts a user’s password to a string of fixed length from which the original text cannot be recovered. That is, if the password “qwerty” (surprise–another common password!) maps to “QmFzZTY0IGlzIG5vdCBhIGhhc2ggZnVuY3Rpb24hIQ==”, there is no way to recover “qwerty” given the hash function output. So by storing the hash instead of the original password, the security of the database is improved substantially, because even if this hash is compromised, whoever has it won’t be able to recover the original password.

Sounds good, right? Yes, but there’s still more to do. If an unauthorized person gets the hash, he or she can use our hash function on random strings and compare that output to this hash. By testing common passwords, there’s a good chance that the unauthorized individual will try “qwerty” and notice that the hashes match. (Note: It’s possible that two different inputs will map to the same hash, but this is very unlikely.) This is where salts come in handy. A salt is data that is added to the input text before it is hashed. So let’s say that the password is “qwerty” and the salt is “c2FsdA==”. Now, the hash function is run on “qwertyc2FsdA==”, which should have an output distinct from the the hash of “qwerty”. An unauthorized person needs to know the salt in order to test common passwords or use rainbow tables.

There are a plethora of hash functions available, but they are not all created equal. I feel that it’s important to note that Base64 is not a hash function. Contrary to hashes, any Base64-encoded string can be decoded to recover the original text. But there also are true hash functions that aren’t secure. Bcrypt is a good place to start.

Happy coding!
Ryan from The Bunch

Rate-Limiting Decorators

Decorators are great for APIs because they cleanly abstract shared functionality from different endpoints. Rate limiting is a great use case for decorators and should be incorporated into just about every API. Rate limiting ensures that there aren’t too many calls originating from the same user/IP address/etc. in a given timeframe. This can prevent the server or database from getting bogged down with spammy requests.

Let’s look at an example. Your application is a math API and has an endpoint /get_fibonacci for computing the nth element of the Fibonacci sequence. This is a computationally expensive operation, and you know that users don’t need to hit this endpoint more than a few times per day. That is, if a user is calling it multiple times in a short window, either there’s an error in the application that is causing that function to be called many times, or the user is abusing the API. Either way, the overloaded server is doing more work than is necessary, and we can prevent this with rate limiting.

To be safe, let’s allow each user call this endpoint 10 times every 5 minutes. If we only expect 2 or 3 calls every 24 hours, this should be plenty, and legitimate users won’t be locked out. If the application has a login system, you can limit based on username. Otherwise, IP address, user agent, or some other identifying data will work. You will need to store a dictionary that maps each user to the number of calls in the past 5 minutes. If you are looking for code to use in your project, you might want to check out a complete example (← code snippet uses Flask).

For an easier code example, let’s say we want to limit API calls by time of day instead of rate–calls are allowed only between midnight and noon. (There’s a use case for that, right?…🙂) This would be our code:

@app.route("/get_fibonacci", methods=["GET"])
@ratelimit
def fibonacci():
    n = int(flask.request.args["n"])
    a = 0
    b = 1
    while b < n:
        a = b
        b = a + b
    return str(b)

def ratelimit(func):
    def ratelimit_wrapper(*args, **kwargs):
        if datetime.datetime.utcnow().time().hour > 12:
            return "Sorry, please try again tomorrow in the AM!"
        return func(*args, **kwargs)
    return ratelimit_wrapper

Then navigate to {host}/get_fibonacci?n=5 to see it in action.

This means that before the given Fibonacci number is computed, the decorator checks to see if the current time is before or after noon. If it’s after noon, the error message is returned to the user. Otherwise, the fibonacci() function runs and returns the result to the requester.

Here’s a line-by-line description of everything that’s going on:

  • 1. This function can be accessed via GET at {host/application_root}/get_fibonacci
  • 2. Attaches the ratelimit decorator to this endpoint (more accurately, it wraps this function with the ratelimit function)
  • 3. Function definition
  • 4. Extract GET parameter from the URL
  • 5/6. Initialize first two elements of the Fibonacci sequence
  • 7. Repeat until we find the nth element
  • 8/9. Get the next element in the sequence
  • 10. Return the nth element (View function expects a string response)
  • 12. Decorator definition
  • 13. Wrapper function that surrounds the decorator–accepts any arguments
  • 14. Check the time in case it’s after noon
  • 15. This is returned to the requester without ever calling fibonacci() if it’s after noon
  • 16. It’s before noon, so return the output of the original function
  • 17. Return this decorator wrapper

Decorators can be confusing at first, but they’re powerful and great for writing simple, clean code. If you have any questions, please let me know in the comments below!

Happy coding!
Ryan from The Bunch

What does LunchBunch do with Phone Numbers?

While optional, adding your phone number to your LunchBunch account is encouraged for account security and for helping friends find you more easily.

In the event that you forget your password, LunchBunch will send you a security code via text message. This is an easy and secure way to help you regain access to your account if you get locked out. Without adding your phone number to your LunchBunch profile, there is no way to reset your account password if it is lost.

The easiest way to add friends as LunchBunch buddies is to add from contacts. Using this feature, you can scroll through the contacts on your phone to see who has a LunchBunch account and send a buddy request with a single tap. If your contact does not have a LunchBunch account, you can invite them to join from within the app. By adding your phone number to your LunchBunch account, friends with your number will easily be able to add you as a buddy.

LunchBunch takes security and privacy very seriously. We never give or sell information like your phone number to any third parties. The only time you should receive text messages from LunchBunch is when you are verifying your number or reseting your password; we don’t send text messages for marketing purposes. Adding your phone number to your LunchBunch account is optional–though encouraged–and used only for the purposes described in this blog post.

If you have any questions about phone numbers and your LunchBunch account, please let us know in the comments below!

Sincerely,
Ryan from The Bunch