Since the year 2003, the OWASP organization (Open Web Application Security Project) has been publishing its “Top 10” list that points out major web security threats. Its goal is to educate the whole spectrum of IT workers (such as developers, testers, designers, managers, etc.) about web application security weaknesses. The list has been released in 2003, 2004, 2007, 2010, 2013 and the newest edition in 2017. In all these publications (except 2007), Broken Authentication and Session Management has been frequently close to the top of the list.
What exactly is Broken Authentication and what we can do to prevent this risk?
In short, this is the application weakness that can allow an attacker to capture or bypass the authentication methods used by this app. Some of the following factors may indicate vulnerability to the thread of broken authentication:
- The application lets us perform credential stuffing with a list of valid usernames and passwords
- The application lets us perform brute force attack
- User is allowed to use a weak or well-known password like ‘abc123’, ‘Password1’ etc.
- Recovery or forget-password processes are based on knowledge-based answers
- Passwords are encrypted, weakly hashed or even stored and sent in plain text
- Multifactor authentication is missing or ineffective
- Session IDs are exposed in the URL
- After successful login, Session IDs are not rotated
- After logout or period of inactivity, user sessions and single sign-on tokens are not properly invalidated
Security checklist for software developers
As a developer, what should you do to decrease the chance of creating this kind of vulnerability to the application that you code? The following bullet points show the solution:
- If it is possible, implement multi-factor authentication. It helps to prevent attacks such as credential stuffing, brute force, or even if an attacker has full account credentials. Many portals, e-commerce platforms, and other applications do not provide this possibility for the user. However, if there are stored data, such as credit card numbers, bank details, or similarly sensitive personal details, it's highly recommended to have at least a 2-factor authentication mechanism implemented. At Merixstudio, we built an application that provides a set of REST-API endpoints to supplement django-rest-framework with multi-factor authentication (MFA, 2FA). You can find Django Trench on our GitHub profile - feel free to use it and test it.
- Remember not to deploy or ship with any default credentials of regular or (even more important!) admin user.
- Base on some modern password policies concerning the passwords’ length, complexity, and rotation. All the passwords should be checked for their weaknesses (for example, based on various lists of worst passwords available on the Internet for everyone). Users should also be prevented from reusing their previous passwords. In the case the user wants to change his/her password while being logged in, it is mandatory to provide an old password to perform this action successfully.
- Store the passwords in hashed or encrypted form, not in plain text. Never keep passwords hard coded in source code. Take care also for the protection of decryption keys. If it is stolen, it may be used to get access to the password file.
- Create a mechanism to limit and delay failed login attempts. All those failed attempts should be logged in server logs and administrators should be alerted if there are detected brute force or credential stuffing attacks. Passwords used for failed login attempts shouldn’t be recorded and visible in server logs. Users may use their credentials from a different application by mistake. The system shouldn’t inform whether it was an incorrect username or password.
- Login action must be performed using SSL. Even if the passwords are hashed, they might be caught and retransmitted when not using a secure connection.
- There should be implemented or used secure session manager that generates random session ID with a high level of entropy. Session ID shouldn’t be presented as a part of the URL, always invalidated after logout and securely stored. Session IDs should be very long, complicated and hard to guess. They could be changed frequently - also, when important transactions happen (such as switching to SSL, authenticating, etc.) Try to keep the whole user’s session protected via SSL. If it is done, then Session ID is rather impossible to be grabbed off the network.
- If users list are presented to other users of the application (e.g., for ranking purposes or forum), then only some form of pseudonyms should be visible (not the ones used for login attempts).
- Always use the POST method for authentication and submitting session data. When using the GET method, all the user’s secret data are visible in server logs and browser history. All the authentication pages should be marked with no cache tags, in order to prevent from using the back button of a browser and getting back to the login page with completed credentials.
Web application security testing checklist
Ok, for now, we know what should developers do for improving safety of an application. Now, on the other hand, let’s take a look at testers work. They also have a huge impact on the security of created applications. Here are the security bullet points for testers:
- Ensure if the data is sent through a secure channel. It contains checking if the POST method is used for sending the data through HTTPS. Check also the referer in request header includes address with HTTPS.
- You can already find a lot of publications about testing passwords issues. I am going to point out the most critical ones. Take a look if testing accounts are deleted after production deployment. It would be bad if the attacker takes advantage of getting into the app by using “admin”/”admin” or “admin”/”password” credentials left. Try to put the wrong password for the admin account a couple of times to block the account (it should not happen). The next test to be made is to check if the app forces the user to change automatically generated default password. Many users don’t change their password. They also create passwords with an easy-to-predict pattern that may be guessed by the attacker. It is essential to check if there are some requirements for password complexity and if they work correctly.
- Lockout mechanism is another thing to be tested. If your account is locked, there is an email with a reset link sent to your mailbox to unlock it, you have to check if this link is not equal each time.
- Check if the account is locked according to the policy of the site. If it says that it should be blocked after 3 failed login attempts, test it. The account should be locked for 10 minutes after entering the wrong password? Then check it after 10 minutes.
- Try to bypass the authentication. For example, while being unlogged, enter the URL that should be only accessible for logged users. Check if there is a possibility to change manually ‘authenticated’ parameter from ‘no’ to ‘yes’. If there is visible sessionID, it is good to check, if it is not generated in the easy-to-predict pattern.
- Testing cache and history mechanism weakness is another wide-ranging subject, so I am going to mention just the most critical issues. The most obvious test is to logout from the application and after that to click the back button of the browser. It is also important to check while visiting pages with sensitive data if this data is not cached in a browser. Response headers must contain Cache-Control headers such as Cache-Control: no-cache or Pragma: no-cache.
- If the app has implemented the functionality of secret questions and answers, it creates a risk that user sets an easy-to-guess answer. However, in this case, you should check if the system doesn’t accept an unlimited number of trials. If the logged-in user wants to change the password, the old password should be mandatory.
- Even if the application has strong mechanisms of protection from broken authentication, it may not be the end of testing. Some apps are able to be accessed through alternative channels. There is a possibility to be logged by sharing sessions through other platforms (e.g. platforms supporting OpenID architecture such as Google or Facebook), or getting from the different parts of a complex system (for example banking, government or colleges’ systems). In this case, all of these platforms and parts of the system have to be tested for security issues separately. It’s also necessary to verify the login process on mobile devices. Nowadays circa 50% of Internet traffic is generated by mobile devices, such as phones, tablets, etc. A lot of web applications provide a simplified graphic user interface for login pages on mobile devices. You need to test if the developers didn’t forget to take enough care for security issues on the mobile channel. The next thing to check is the various language versions of the tested site. They may be implemented in a slightly different way. At least it is important to check if HTTPS is used everywhere. When the application comes into production, it’s crucial to remember to check if all the testing accounts with easy-to-guess credentials are removed. Sometimes testers forget about this and it frequently happens that credentials from UAT and staging environments are accessible on production. It should not be the case.
- And, last but not least, if the system is working correctly on the production environment, it is good to perform a non-technical test. If there is a call center provided for the app users, try to call and convince the operator, by using some social engineering tricks, to give you access to some user accounts.
Remember that no matter how great the application could be implemented and protected against security threats, the human factor may ruin all this hard work :)