Web security is really a state of mind, and not just a cheat sheet of techniques. Get into the right mindset and watch yourself build things better and safer almost as if by osmosis. If you start today, youll be a security superstar before you know it.
Get into these 12 habits and write better code. Its never too early (or too late) to improve your web security skills. Like the saying goes, if you aren't at least a little embarrassed by the code you wrote 6 months ago, you aren't growing fast enough. So true!
Watch on Vimeo Get the slidesTranscript
Web Security - An Introduction to Best Practices
Dated: 2017-07-6. Transcribed: 2017-07-11.
Intro
Well hello everybody, we're going to talk about web security today and building good applications right from the get-go.
You really want to be a web security minded developer because there is a lot of heartache and headache in there. Now lazy developers are going to build "low hanging fruit" for hackers and other malicious people. You're basically putting out the bowl of candy and inviting people to go and wreck your app. Don't do that! Don't put out the bowl!
We have to remember that we are really craftspeople, who have a slightly dangerous fine line that we mitigate with our skillset. Not unlike the glassblower who can end up with a flaming blob of goo in his lap if he's not careful. We need to really protect ourselves and our clients by working smarter.
So don't be an easy target. Developers who only consider their ideal user - that's the user who fills out everything perfectly. That never colors outside the lines you might say. That never tries to find exploits, or even just doesn't do things wrong. Don't just consider your ideal user. Consider all the permutations.
There are some developers who make it work and call it a day. Well a lot of the time there are going to be loose ends, and you've got to go the extra mile in getting your server set up and getting your application configured.
There are developers who simply never learned better, and if you are one of those we are going to talk about a dozen things today that will get you started.
And lastly there are those of us who are just too rushed and think they can't be bothered by putting good securuity behind all their projects. Well you're wrong! Maybe you have a scheduling problem, maybe you have a project management problem, but you are going to need to get those squared away. Because a stitch in time saves nine, and that is so true with web security.
Now the link I want to point you towards today is called OWASP: Open Web Application Security Project. Those are the people who are keeping an eye on, year to year, what are the trends and things facing us as an industry. They have their top ten list, and I highly recommend that you go check them out.
You want to get your good security basics in place because you will have so many fewer instances of fraud, theft, and even vandalism. There could be huge data loss if somebody gained entry and wiped out your database. Hope you had a backup right?! You're going to want to create applications that are hardened to the outside world and perform predictably and safely for you and your clients. This is how you can sleep at night.
So as always, do what you can and chip away at it. Get better everyday and start right now. You'll be there before you know it!
So let's do it! Today I'm just going to talk and we will have some code examples to go over. We're going to go over a dozen ways that you can make your code better today and errday.
Web security slide #1 default credentials
The first one we're going to talk about is default credentials. Now when you got your computer or when you install a new operating system you probably remember that there was no password on your machine. Well a lot of programs you might use for your website are like that. They're not protected and they have default users who have the default passwords.
Now that's two things to guess right? The username and the password. Two things. And you want to make a hacker have to guess both of those things. The root user is a perfect example. A lot of times they come with no password and we already know what the name is, root. So my recommendation to you is to rename all of those default users and give them strong passwords.
Remember, you want to make people guess two things.
#2 every path and port is the default
Similarly we have our default path and port setup. A good example of this would be if you are using the GUI database app PHPMyAdmin. Now gosh, what if we could just go to everybody's website and hit uri /phpmyadmin and start trying to crack away at their username and password for that service? That's a bit scary, right?
There is a bit of predictabilty in all this standardization but you do want to make it hard to guess. That way you can make it three things that they have to guess. The URL, or maybe you're going to tie it to your ip? But let's just say we're going with path and port. These are new things to guess in addition to the credentials.
There is a somewhat related topic Security Through Obscurity. We're not talking about that today exactly. The premise there is that just being very hard to guess and very buried is enough. I would never tell you that. That's not enough. You have to actually write good code and not have glaring open leaks even if they are hard to guess. But giving an extra layer of protection by making things not so obvious... Not the default paths, not the default ports. It's up to you and every configuration is different. But that will give you one more buffer against an attack.
Web Security tip #3 no timeout between failed attempts
Let's talk about login attempts and when someone is trying to guess your password. If you allow me to make a thousand attempts in a minute, then in 5 minutes a password cracker could be guessing 5,000 different combinations. But what if your script says No, bad attempt - wait a minute? Wait two minutes. Maybe after 5 bad attempts in 5 minutes it locks you out.
This is going to prevent a lot of your brute force attacks which is where the attacker just hits your application again and again trying new combinations. And time is on your side here! If you can make the attacker wait they are going to move on to a different server. Trust me, if you had to do five attacks in five minutes, that is not worth your time. It could take you YEARS - hundreds of years even - before you finally crack that server. Not worth it.
#4 allowing weak passwords
Weak passwords can be a real pain point in your organization. Goodness, can you imagine if you had an admin user named "admin" who had a password of "password"? You might as well give away the keys to your app!
We want nice strong passwords. This means good length - 10 characters, twelve, really as long as they'll let me go. I've had 20 character passwords. You want good varied content to your passwords, that would mean characters both uppercase and lowercase, numbers, symbols, the whole shmagoygle. And if you can avoid straight dictionary passwords like combinations of words that are all in the dictionary, that would be wise.
I would encourage you to have your users use passwords that no one can remember. They would be so random and obscure that you would use a password manager program to store.
You can also look into two factor authentication.
But again, if you let your users use the password "password", or if you let them get away with a four character password, that is going to dramatically reduce the time it will take to crack it.
#5 sanitizing input
So here is a big one: sanitizing your input. Everything that your users give you, pretend that it is a cookie that you dropped on the street in New York City. Disgusting, right? Do not assume your users are giving you nice clean data.
I know if you follow a lot of learning materials (like tutorials) or you look at documentation, generally they'll give you the right answer right away. Generally if someone is supposed to be passing the server a name it's going to be a name. But this is where attacks happen.
You need to look at data type. If you're expecting an integer, make sure it's an integer. If you're expecting a username, make sure it's a string and probably 50 chars long max. Make sure any special characters in there are escaped, especially the dangerous ones like quote marks. And the length should be appropriate.
So here is one I want you to never ever do (code sample on slide). This is a standardized query language update statement. This would be for someone who is already in the database and they've submitted a form. They've passed a username, a user level, and a password. And so we're saying here to update the users table and set their username to this, their user level to whatever they said, and their password to whatever they said.
This is a terrible example! You are letting them set their own username, they could totally overwrite their username. You are letting them specify their very own user level. I mean maybe it was a hidden field but they can still alter that data before it is sent. The only field you should even consider letting them change in this scenario is password. But in all of these values we are not escaping any of this data. We are not sanitizing it, we're letting it pass through just as is. Terrible!
#6 be militant about requests
In a related vein you want to be really vigilent about all of the requests that your application is going to accept. Does everything need to have a GET and POST request? What about APIs? Lock it down.
Always ask yourself, "should it be allowed?"
Here's a little example function (code snipped on slide). Let's say we have a gift card service API and this is how we add dollars. So we're going to accept three things: the company who issues the gift card, the account ID or card number, and a dollar amount to add to the card.
Right here in the arguments area we're going to validate our data a little bit. Right here we're going to make sure that the account number and company ID are integers, and we're going to make sure that the dollar amount is a float. A decimal value. This looks good.
Down here we're going to do a little validation and make sure that this card number is actually a valid and issued card number belonging to this company. We want to make sure this is a real request, and not someone hitting our service with weird numbers. There would be an API access token or something on this whole request, but we still want to make sure that this is all valid.
Pretend that you are a person interviewing or a bouncer at a club. You need to "mind the store" and when things don't go right you need to say no (to the request). So here we're throwing the exception, and that is the barrier. They need to have proper variables and we need to make sure everything checks out before we add dollars to that account.
Web Security slide #7 dynamic uris
Dynamic URIs are another one that I see problems with upon occasion. They are really useful for some instances but not really appropriate for others.
Think about user profiles. This an example where you would see your own user page (code snippet on slide) at the URI /my-transactions/:userid. So here is the controller function, and my_transactions() is going to pull a little piece of the URL. This is segment one (my-transactions) and segment #2 (1).
Instead of pulling this user id out of the session data for my-transactions, since we already know who is logged in, it's pulling from the URL. And then it is performing no validation and simply getting all of the transactions for that user and loading them back in the template.
Now maybe this is a page that is restricted to logged in users. But what if I am logged in and I just use my browser to type in a different number (userid) in there? Well with THIS code I could actually see anybody's transaction history. That is a terrible security leak. We need to make sure right about here that there is a comparison. Make sure that the user is logged in and they have the right ID for this page.
#8 sql injection
This is a big one and it is always going to be a big one. You have to assume everything is tainted.
So here is a security risk right here (code snipped on slide). We have a standardized query language select statement. We're going to select all from the transactions table where the ID is: whatever the user passed as a transaction_id in the post request.
So we're doing no validation on this again, we're doing no sanitizing. We're expecting an integer but we really don't know. It could be anything. What if somebody else who knew SQL statements gave us this value (sql injected string)? What if they gave us a string with some special characters and actual SQL in it?
It would change our statement! Select all from transactions where the id is 1 or the amount is not equal to 0. And you can imagine, that would be pretty much every transaction. You could pretty much give away the store with something like this.
You always have to be vigilent for SQL injection.
#9 including sensitive files in your git repo
Here's one that a lot of people don't think about: sensitive files in your git repository. If you are committing your project, especially to a public git site, any credentials or any API access keys that you would commit to that repository.... I would say it's not possible to get them back out.
If you do it by accident IMMEDIATELY go regenerate all of your passwords and all of your API keys. Shut it down. There are bots that look for this. Burn it to the ground. Start a new repository and burn it to the ground. Assume that data is not private any more.
What you really want to do to get around this is use environment files and pull those into your application at runtime. And then set gitignore to not index those environment files.
web security slide #10 apache indexes enabled
This is an Apache problem I would say. This happens when there is no index.html file and Apache thinks it is being helpful. Let's go take a look at this.
(screencast of google image result for apache indexes) So here we have a sample of indexes, you've probably seen this before. In this example it's giving away quite a lot of information, isn't it? We've got all of these various software products that seem to be installed on this machine. Sometimes this will show files that are not linked to, things that are supposed to be a secret.
We can see the software that is running the server, its version, the operating system. If you were a person who knew about a vulnerability with any of these components and you saw a page like this, well you could just have a heyday because you would know just how to exploit this server.
So we want to lock it down. You want to disable indexes on the server level or if you can't be sure that your code will be used on a properly set up server, you're going to need to put a blank index.html file into every directory.
Little Bobby Tables - detour
There is something else I forgot to show you. Here is a cute little comic, this is commonly called the Bobby Tables comic. There's always a relevant xkcd.
(screencast of xkcd.com/327) This is about a mother who input her son's name into a web form with a little bit of standardized query language into it and she sql injected the school. Dropped their entire database.
Very funny!
#11 leaving sensitive files on the server
So here is one that happens too often. I do see this. People will leave sensitive files on their server.
Perhaps they have migrated a website as a nice zipped up tarball with a database inside. Anyone that can figure out the name of that zip archive or that tarball suddenly has access to your environment variables, your database, all of the data inside it. You're pretty much giving away the store again.
So you need to delete all of those zipped copies on your server, and that goes double for any sort of search and replace or database touching scripts. ESPECIALLY if they already have credentials plugged in.
Anybody could run those! Anybody who can find the script name! And there's a lot of popular ones that automated scripts can discover. They'll just hit servers for those popular script names, and you will see this in your access logs.
You need to delete those as soon as you're done doing whatever with the script. You must remove them.
Web Security slide #12 error messages are too revealing
And lastly, we're goint to talk about error messages that give away a little too much information. Sometimes this can look like your PHP throwup code. Fatal errors or stack traces.
A lot of times these give out file names which give clues to the directory structure and server architecture of your site. Nobody needs that. Our users really don't understand that. So let's be nice with our error messages, and let's save the stuff that you and I want to see for the logs.
Just give them a short fail message. "Please provide a valid email" or "Sorry the website is having some problems right now". But we don't want to give those full developer style errors to just anyone on the outside.
Wrap up
You are probably wondering, do you need to be a terrible person to be good at security? No you don't, but the more you can think like a hacker the more effective you can probably be.
The thing to do now is to incrementally get better at your coding practices step by step. Set some goals to get secure. Write good code from here on out.
If you want to scare yourself straight go visit the OWASP site, watch some presentations, or watch some DefCon footage. You might just end up making a tinfoil hat of your own.
So what do you do next? Write good code and breathe a big sigh of relief. You've got to bring the right attitude and a security minded approach to all your new projects. Spend the time and don't let anybody tell you it's not worth it. It totally is.
If you'd like to give this presentation you are more than welcome to use these slides.
If you'd like to ask me a question or tell me how awesome I am, you can totally do that. Hit me up on Reddit. Thanks guys and have a great Thursday.