You don’t know enough about cybersecurity.
How do I know that?
Only 9% of the nation’s top universities require cybersecurity courses for computer science and engineering students, and there isn’t enough time in coding bootcamps that teach non-coders how to to code in just 3-6 months.
Security breaches are all over the news, and we all know, deep down, it’s only going to get worse. A LOT worse.
As software professionals, you’re personally responsible for stopping this while the whole world watches. So, what will you do?
The best strategy is to forget about cybersecurity. You already have enough to do.
I want to show you how this will be better in the long run, but let’s not rush things.
Do you remember much about the last thing you rushed and crammed for? I doubt it. It’s simply not an effective way to learn.
Therefore, this article won’t be another “10,000 THINGS YOU NEED TO FIX TODAY BEFORE YOU GET EXPLOITED” cybersecurity rant. Instead, I’ll show you just 2 things you need to stop cybercriminals from abusing your applications, then how and why to forget them.
Writing awesome code for a new feature is exciting. But, before you start, you should know who will use it. This is where you use authentication to verify who’s making the request.
Without authentication, your feature is open to anyone the world.
Oh, is that a problem?
It might be. Missing authentication on a single page can critically undermine the entire security of your application. How would you feel if, right now, the URLs of every sensitive page in your app, including admin pages, were made public to the internet?
Not good? Okay then, I’ll show you an easy authentication pattern to use in every feature you create.
But before we dive into that, I need to show you another very important thing you’ll later forget.
Whether you have 10 users or 10 million, each is trusting you to safeguard their private information.
After using authentication to verify who’s making a request, your second step is to allow or disallow their actions with authorization.
Remember, anyone can make a request to your server. It’s up to you to make sure the right person is doing the right things.
Authentication and authorization are best friends who, together, create access control. If you break access control, you break their friendship, and if you break friendship, you’re a monster.
This could be you.
Do you want to be a monster?
Of course not. But, unfortunately, broken access control is very common in modern web applications and it carries severe consequences.
Here’s a tip: destroyed databases and stolen personal information don’t qualify for the “it’s a feature, not a bug” excuse. Trust me.
Let’s say you’re adding a new admin feature. Adding access control would look like this:
# this method handles requests to /admin/new-feature def new_feature if !user.is_logged_in? # authentication failed # deny access and return end if !user.is_admin? # authorization failed # deny access and return end # perform new feature actions end
If the user isn’t authenticated or authorized, deny access by:
Asking the user to login is most common for authentication, whereas an error message is common for authorization.
But why would you ever want to 404? Isn’t that bad user experience?
I know a doctor.
Her website allows her to look up disease information on her patients like this
If you, as an attacker, access the following path
And get redirected to the login page, but other disease names 404, this would be a MAJOR security flaw exposing patient information – access control would be broken since you now know Mr. Tentacles was diagnosed with the very rare Squid’s Disease.
This means you need to use 404 responses to deny access whenever the existence of a page leaks sensitive information.
I know you have a lot to think about already, and I also know you’re going to forget access control sooner or later. Rather than waiting for bad luck on a busy day, just proactively forget.
How do you do this?
For authentication, just make sure it’s always required. You can then simply disable it on endpoints where it’s not needed, like your login page where everyone needs access.
Similarly, add authorization checks at the highest level practical, like your entire controller – again opting-out when necessary. Ideally you’d use a library to simplify this functionality, but we don’t always get the pleasure of working for the perfect company with the perfect code base, do we?
Your access control checks should run before your request logic. In Rails, before_actions do this and other frameworks can use middleware or similar.
Finally, you’ll need to forget about authorizing actions on resources too.
Actions on a database resource, like editing a blog post, are usually handled by loading the resource from request parameters like this
REQUEST from user with id of 2 : PUT /blog-posts?id=35
blog_post = BlogPost.find(params[:id])
SELECT `blog_posts`.* FROM `blog_posts` WHERE `blog_posts`.`id` = 35 LIMIT 1
As a result, this requires an authorization check:
if blog_post.user != user # deny access end
But a better way that includes built-in authorization is to query through the authenticated user’s own blog posts instead.
blog_post = user.blog_posts.find(params[:id])
SELECT `blog_posts`.* FROM `blog_posts` WHERE `blog_posts`.`user_id` = 2 AND `blog_posts`.`id` = 35 LIMIT 1
If the blog post with the id of 35 didn’t belong to the user making the request, the query would return empty, and you can simply 404 as if the user had provided an invalid id. Therefore, the chance of unauthorized access vanishes entirely.
By using these patterns to have access control on by default, any new feature you develop will automatically be protected – allowing you to actively forget access control for better security and increased productivity.
Other developers are going to work with your code. They’re going to make changes – some good, some bad. But you can protect the access control of every feature you create by adding just a few simple tests.
For most people, there are only 3 cases you need to cover
Let’s see that in code:
def test_request_without_auth # skip authentication request FEATURE_OR_RESOURCE expect ACCESS_DENIED end def test_request_with_auth_and_no_access # basic user without access login_as user request FEATURE_OR_RESOURCE expect ACCESS_DENIED end def test_request_with_auth_and_access login_as user_with_access request FEATURE_OR_RESOURCE expect SUCCESS end
Remember to test the
ACCESS_DENIED with the appropriate
redirect, error message or 404 response.
To finish things off, I’m going to share a very powerful secret access control technique with you.
It’s the ultimate tool you can use to prevent someone from destroying the privacy protections of 404 responses.
Ready for it?
# Add a comment.
If some monster comes in and “optimizes” your code without understanding the purpose of the 404, they might simply change the responses in both the code and the tests, thereby breaking access control.
But explaining why you’re responding with a 404 instead of a redirect can be a very simple, yet powerful, way to protect the security of your code.
In case you didn't realize, this article is aimed at adding an extra layer of defense to your access control – NOT actually convincing you to forget about it. In fact, thinking that a tool can replace security understanding is why XSS vulnerabilities are increasing despite automatic framework protections.
You can learn how to eliminate XSS vulnerabilites through my XSS training course. Preventing broken access control and XSS vulnerabilities can make a HUGE impact on securing your applications properly.
Take action now
I’m just gonna say it.
Reading articles isn’t enough to improve your access control.
You need to apply the knowledge you’ve just learned to make your code more secure, because imaginary to-do lists never really work.
Here’s your next task: schedule 30 minutes to verify you’re not exposing any sensitive pages. Once done, you can then work toward forgetting access control, protecting your freedom to forget with unit tests and then writing the most powerful comments of your life!
And, lastly, share this article with anyone it may help. As the old saying goes, “friends don’t let friends become access control breaking monsters” … or something like that.
Hacking Websites With Cross-Site ScriptingWATCH NOW
The Ultimate XSS Training Course gives you the full, uncensored picture of Cross-Site Scripting from the perspectives of criminal hackers and the engineers whose job it is to stop them.
Cross-Site Scripting (XSS) is the #1 most common appsec vulnerability that allows attackers to steal private data, hijack accounts and spread ransomware on your sites. This course teaches students to:
Discover critical XSS vulnerabilities in web applications.
Create, analyze and stop malicious exploits used by criminal hackers.
Fix XSS vulnerabilities in routine and emergency situations.
Stop costly vulnerabilities before they reach production using the latest best practices and techniques.