 All right. It's about 12 o'clock, so we can go ahead and get started. Good afternoon, everyone. Thank you for coming to my talk, Hacking Your Way to Better Security. My name is Colin O'Dell, and I'm a lead web developer at Unleashed Technologies, we're a web and hosting firm based just down the road in Columbia, Maryland, and we are hiring, so if you are interested, stop by our booth downstairs. Some of you might know me as the co-organizer of the Baltimore PHP Meetup Group, or as the maintainer of the Lee Common Mark package, or even as the author of the PHP 7 migration guide ebook. But something you may not know about me is that I won the PHP World 2015 Capture the Flag event. For those of you who aren't familiar with Capture the Flag, it's a security competition where basically the host stands up this website that's full of security vulnerabilities, and all these different contestants gather around and try and hack it. Each vulnerability is considered a flag, so if you're able to find a SQL injection attack, you capture the flag, and whoever can capture the most amount of flags in the quickest amount of time wins. Now, you may notice that my job title is Lead Web Developer. I'm not a security expert, I'm not a researcher, I'm not a pen tester. That's not what I do. But I was able to win this because I was familiar enough with security concepts. And that's really what I want to try and get across to you guys today, is that you don't have to be an expert to be good at security and to secure your sites fairly well. So for today's talk, we're going to explore several top security vulnerabilities from the perspective of an attacker. So for each one, we'll first understand how to detect and exploit that type of vulnerability, and then once we understand how it works and what kind of damage it can cause, then we'll look at how to protect ourselves against those vulnerabilities. Now, I do have to get a couple of disclaimers out of the way before we begin. Number one is that you should never test systems that aren't yours without explicit permission. Some people like to live by the rule that asking forgiveness is easier than asking for permission, but not if you're in jail. So if it's not your website, if you don't have permission, please don't try and hack other people's websites. The second disclaimer, kind of on a lighter note, is that the examples in this talk are fictional, but the concepts I'm going to show you and the demos I'll show you are very real. I don't want to go out and try and hack different websites just for the sake of this presentation, and I don't like live demos because they always go wrong, so instead I have a bunch of pre-made examples here. So even though the examples are fictional, the behaviors that I'll show you are very real, and if you found one of these vulnerabilities in the wild, you would see that same exact behavior. All right, so those disclaimers out of the way, let's go into the OWASP Top 10. So the OWASP Top 10 is a regular publication by the Open Web Application Security Project, or OWASP. And what they do is every few years they put out this publication that lists the 10 most critical security vulnerabilities facing the web. OWASP is a nonprofit organization. They do this for free. They have a lot of really good material on their website if you want to check that out. They provide free resources, articles, and tools for web security, and if you were to go and look at the OWASP Top 10 website, you'd see a list like this showing you the 10 most prominent web vulnerabilities we're facing today, and then you can click into each one and see some more details about them. So if you were to click into the injection one, for example, you'd be able to see how easy is it to exploit, how prevalent is it, what's the potential impact? Am I vulnerable? How do I prevent that type of attack? So for today, I'd like to go through several of those Top 10. I unfortunately don't have enough time to go through all of them, so I've chosen the ones that I think are most interesting, at least from the attacker perspective. So let's go ahead and look at the first one, which is SQL injection. SQL injection attacks are when an attacker is able to modify the SQL statements that a server runs. So an attacker could use this to spoof their identity and log in as a different user that they shouldn't really be able to log in as, or maybe they tamper with data in the database, updating things, changing things, adding and deleting records. They could even use SQL injection to disclose hidden information within the system that they should not have access to. At its core, SQL injection works like this. We take some value from the user, might be a malicious value, and that value is injected directly into the SQL statement that our server runs. So if that value is malicious, then the SQL query that runs will contain that malicious code. Let me show you an example of it. It might make a little more sense. Let's say we have this fictional banking website called My Bank. They've got this nice little padlock icon over here, so it looks like it's kind of secure. But for the sake of this demonstration, let's say that they're not. Let's say that they are vulnerable to SQL injection attacks. And us, as attackers, we want to break into this bank and do some bad things. How would we go about doing that? Well, at first, we may not know that they're vulnerable to SQL injection, so maybe we'll try logging in with some common passwords. Maybe we'll try logging in as admin with the password password, and hopefully we get this error message, otherwise the people at the bank have some explaining to do. But if this doesn't work and we can't figure out any other passwords, maybe we haven't stolen any, we don't know what the passwords are, let's try to see if this site is vulnerable to SQL injection. A really easy way to do that is just put a single quote somewhere in that form input. So let's try password, single quote, and see what happens. We click log in, and now we see unknown error. Well, that's different, because when we tried admin password, we saw invalid username or password. But now we're seeing this different message, unknown error. Why might that be? Well, if we had server access, we could go ahead and take a look at the logs on that server. If we took a look at, say, the Apache error logs, the PHP error logs, we'll see an error like this. MySQL error, you have an error in your SQL syntax near password at line one. If we were logging the queries, we could take a look at what query was executed, and we'll see that we do indeed have a syntax error. There's an extra single quote here. Because there's a syntax error, that error is bubbling up. It's not being caught and handled properly. It's bubbling up as this unknown error on the page. So now we have kind of a good idea that this site might be vulnerable to SQL injection attacks. Let's try a slight variation of this. Instead of putting the single quote at the end, let's put it at the beginning. Let's do single quote space test. Why not? Click log in. We'll see the same unknown error. And when we go over to the logs, we'll see very similar messages there as well. So we have the same MySQL error. There's an error in your syntax. And when we look at the query logs, we see something kind of interesting. Let's look closer at this. This is the query that the server is running as a result of us submitting our password as single quote space test. And it looks kind of like this query. We have select star from users where username is admin and password equals empty string. But then there's this little bit on the end. And that little bit, that test there, we were able to add that in. So is there some way that we could take advantage of this vulnerability to be able to hack this bank account or hack this banking website? What if instead of putting test, we put in some condition that is true? We have or something that is true. Because when this runs, MySQL will look at this thing, this whole bottom part that and password is blank or something that is true. If this is true, then this entire bottom line evaluates to true. And therefore, this is essentially the query that's being run. Select star from users where username is admin. Totally bypassing the password check. So what string do we need to create to put into that field to make that happen? Well, we know if we do single quote space test as our input, that goes into the query like this. And that's essentially what gets executed. So let's clear that out and let's build up the actual queries that we want to run. Well, we know that we want to start off our password with a single quote. Because that's going to terminate this string here. Well, we've got another single quote on the end. So let's put a second single quote towards the end of that string. All right, that's looking good. But now we need to have that or something is true part. Let's put an or. And how about string containing one equals string containing one. So if we were to submit this into the form, this is the query that gets executed. Let's try that out. Go back over to the site, put in that password, and we're logged in. And now we're in admin. We can give people money, take it away, deny credit cards, close accounts, do whatever kind of malicious things we feel like doing. So SQL injection can be pretty dangerous. And as you can see, it can allow you to log in as users that you really shouldn't be able to log in as. But there's more you can do with SQL injection. There's a really cool type of SQL injection called blind SQL injection. Blind SQL injection is where we use SQL injection to issue queries to the website. But the website can answer those directly. So maybe we want to know, does this user exist? Maybe we want to map out the database structure and say, does this table exist with these columns? Does this credit card number exist in the system? The system won't tell us yes or no directly, but we can absorb its behavior to figure out whether the answer was yes or no. Now we know from the previous example there were three different types of messages that could appear on the top based on what our input was. The first one was this unknown error message. We would get that anytime there is any kind of invalid query. Maybe there's a syntax error. Maybe we have a single quote missing its pair, something like that. Maybe we're trying to reference a table or a column name that doesn't exist. If that happens, that's what the browser will see. And so we can kind of infer that there must be some kind of error in our SQL. Now if we get this message instead, this is telling us that the query was valid, but there were no results returned. There was zero rows returned from the database. So if we tried to do a SQL injection and saw this, that tells us that our injection kind of worked. Our database table names were correct, column names were correct. Everything was correct. There just was no data. So the answer to our question that we were asking is no. But if on the other hand we saw this, we know our query is valid, and the answer to our question is yes. So let me show you what that looks like. Let's say we want to map out the database of this banking application. Maybe we'll try a SQL injection attack like this. We've got a subquery here, select ID from user limit one. And when we run this, we might see an unknown error. Now if we had access to the server side, we'd be able to check the query log and see, okay, that looks like a valid query. There are no syntax issues there, but if we check the MySQL error log, we'd know that there's no table named user. So that's why we got the unknown error. But maybe there's a table named users, plural. Let's try that. So we'll wipe that away, add the S to user, run that, and we'll see invalid username or password. So now we know there is a table named users, and it does have an ID column. Now we can, of course, repeat this process, figure out all the table names, all the column names. We can even use this to figure out if certain data exists in those rows. We could say look in this table for a username called admin or a credit card starting with the number four. And we can kind of force our way through that, and that could work, but it's going to be really slow and tedious. So if we really want to get a lot of information from this banking website, we'll want to use a data disclosure attack. Basically have the website output that data for us. Let me show you a different example of this in action. So here's a fictional bookstore website, onlinebookstore.com. And when you go to slash book slash 123, you see a listing for one of the book products. Well, how do you think this website works? Getting to the mindset of an attacker, knowing what you know about web development. How does this work? That when you go to this URL, this appears. Well, we know we have to be grabbing the ID from somewhere. In this case, it's the URL. And then there's probably going to be some kind of SQL query asking the database, hey, do you have a record for this book? And then running that query, getting that data, outputting it through our template. Now what if we try an ID instead of 123? What if we try something that probably doesn't exist, like 999999? Lots of nines. We might see a 404 error page. And when we do that, as developers and as hackers, we can kind of infer that it must be going through that same process. It's still running that same query, but there are no rows. There's no data to return. There's got to be some other code after this that detects that and then shows a 404 page instead. But what if instead of 123 or 99999, we put some kind of malicious query up there and then had that value injected into the SQL statement, have that return different data instead of a title, author, and price, and have the website show that in the template? How could we do that? We can do that using the SQL Union operator. A SQL Union query basically takes two select statements, takes whatever the results are, and then combines them into one set. So if we were to union the actual book data with something else, maybe that's some malicious select query that you built, that data would be returned into a single result set. There's a problem with this, though, because when we go to slash book slash some ID, the website's only expecting there to be one record. Your network are going to have two books with the same ID. So it's only ever going to look at this first row and not actually show us the data we're trying to query. Well, cool thing about the Union operator is that if this left side is empty, if there's no data, then the final result set will be just our malicious query. So we know that we need to build a URL like this. So have 9999 to select that empty data and then union that with some malicious query to grab credit card numbers or something like that. Now there is one other gotcha, and that's when you use a Union query, you do have to have the same number of columns in both sets. So I can't simply grab just the number. This won't work. I'll have to kind of figure out that there are three different fields that are being grabbed. And so as a result, my query needs to return three fields. So let's modify this up here. Instead of selecting just the number, we'll select the number and we'll call that title. And then for author and price, we'll just select a dummy value of one. So what happens when we hit this URL if this website is vulnerable to SQL injection? This query gets executed. This is the data that is sent to the template and there is our credit card number there. So we can use this to get the credit card number out of the database. Now, of course, we can automate this process and get all the credit card numbers. So we could add a limit clause here and say, skip the first one, show me the second one. Skip the second one, show me a third one. Throw that in a bash script and now you have all the credit card numbers from this online store. So how do we protect ourselves against SQL injection attacks now that we know how they work, how they can be taken advantage of? Well, remember, a SQL injection attack is basically some malicious untrusted value being injected directly into the query. So maybe we could block input with special characters. We know that single quotes can be dangerous, so why don't we just block those? Well, we could do that, but then if you have an Irish last name like myself and you try and sign up for United's rewards program, you'll get this message saying your last name is invalid. It's not just United. I'm not just picking on them. This happens maybe every week or two. So we obviously don't want to do that. We don't want to make our users angry. Maybe we can escape the user input instead. Maybe we'll take that raw untrusted data and run it through a function like MySQLI real escape string, which is a really long name. And what that'll do is if there are any single quotes, it'll just put a backslash before them so that when that query gets passed into MySQL, it'll see the backslash and then a single quote. It'll say, oh, this single quote here, that's not ending the string. That's inside of the string. So this is a valid approach. However, you need to make sure you're not escaping things more than once or I get letters in the mail like this. This also happens every week or two. All right, so maybe we don't want to escape user input. Even though it would work, you know, not the best approach. A better approach would be using prepared statements. With a prepared statement, what you basically do is tell the MySQL driver, I want to run a query. It's going to look like this, but there's going to be some value that I want to search on. Some value that might be coming from an outside source and untrusted source. And MySQL, I want you to escape it for me. I don't want to have to deal with did I escape it, did I not, did I escape it twice, did I unescape it? I don't want to deal with that. You do it for me. Here's the query I want to run. Here's the value I want to insert as that first parameter and then execute it. And that's basically all you have to do with a prepared statement. Now, you can do this natively in PHP if you're using MySQL I or PDO MySQL. You can, of course, do it in Drupal. Drupal does support this, as does pretty much every framework and library out there. Doctrine, Eloquent, ZendDB, Magento, Symphony, Laravel, they all do this. So if you are taking user input and using that in a query, use a prepared statement. That's the best way to protect yourself. Now, SQL injection, interesting enough, isn't the only type of injection. You can actually have no SQL injection. You can have injection attacks occur with operating system commands, LDAP queries, SMTP headers. Basically, any time some user submitted data gets put into an instruction that the computer must run or interpret, it could be vulnerable to an injection attack. So for these other types, if they don't offer prepared statements or anything like that, you will need to escape the input and make sure people aren't putting in weird things like single quotes, amber sands, things like that. So that's SQL injection in a nutshell. The next vulnerability I'd like to share with you is cross-site scripting, commonly abbreviated as XSS. Cross-site scripting attacks are when malicious code is injected into a web page. So it's injected into the HTML or the JavaScript sent to the victim's computer and the victim's computer interprets that code and executes it on their machine. So as an attacker, we could use that to execute malicious scripts. We can hijack user sessions, install malware, even deface websites if they're vulnerable to cross-site scripting. At its core, like I said, cross-site scripting attacks happen when raw code or scripts are injected into a page. Those malicious values can come from any number of sources. They can come directly from user input. They can come from a third-party source like an RSS feed or an API or some other system. They could even come from within your own database. If there's some malicious code in your database that gets placed into that value and you're echoing it out directly into the page, the computer may run that. Think about it. When you request a page from a browser, it's sending all that HTML and JavaScript over. And whatever it sees there, it's going to execute. Let me show you an example of this. Here's a fictitious link shortener I've called Shortly. Basically what you do is you provide a URL like colonlodell.com, click on Shorten, and it gives you a short version of that URL, which you can post in a tweet, send to your friends, print out on a business flyer. And when they go to that URL, they're shown that they're going to be redirected to that longer URL, so far so good. But if this website was vulnerable to cross-site scripting, what could we as attackers do with that? Well, let's try something like this. Let's try submitting this as our URL. We have a basic JavaScript tag here, and our JavaScript should show a pop-up box. So we put this in the field, click Shorten, and we immediately see Hello World pop-up. Why is that? Well, let's close that pop-up and... Oh, where's that original URL? I would totally expect to see this here, but I don't. Well, let's look at the source code. There it is. It was put into the HTML of the browser, but the browser saw that as an instruction to run, not as something to show to the user. So that's an example of doing this with JavaScript. What else could we do with this? Maybe we could do an iframe. Maybe we could do something like this. Click Shorten. And I don't know if you can hear it, but we can rig-roll our friends. Better yet, we can send them a short URL, and they'll be stuck on this page because there's nothing to redirect to. So we could do that. It's, of course, a lot of fun to do, but that's kind of a really basic, not very dangerous type of thing we can do with cross-site scripting. Something more malicious might be having the browser execute code like this. Let's say we have a website like reddit.com. For those of you who aren't familiar, reddit is a social network where we share stories and share stories with each other. And they can fill out what the title of the story is in a URL. And that is shown on the homepage in the big list, and right next to that list is a login form. Now, if we were able to inject this code into the homepage of reddit as our title, then anyone who visits the site will have this JavaScript execute. This JavaScript is looking for the login form and changing the URL of it to malicioussite.com. So if we get this onto the homepage of reddit, anyone who logs in is actually sending their credentials to us. So cross-site scripting, really bad. How do we protect ourselves against cross-site scripting attacks? One possible approach might be filtering user input. So maybe we run the user's input through strip tags, strip out the HTML, the iframes, JavaScript tags, take it all out. You could do that, but what if you're trying to build a site like Stack Overflow or Pastebin that people want to share HTML and not have that deleted? We don't want to destroy that data and cause data loss. So strip tags, probably not the best approach. A better approach would be escaping the user input with something like HTML Special Chars. This is a built-in PHP function which will take your less-than signs, your greater-than signs in the HTML, and encode them as HTML entities. So when you echo this out into the browser, it'll know, okay, this isn't a tag to run, I need to show a less-than sign, the word script, and then a greater-than sign. This is totally valid to do. My preference, though, is to escape the output. So not doing it on the input, doing it on the output. So regardless of wherever this data comes, I just leave it as is until it's time to generate the HTML. And at that time, that's when I choose to escape it. The reason I do it this way is because if you do it this other way, if you do it on the input, you have to remember to do it every single time. Anytime you're grabbing stuff from the request, anytime you're grabbing stuff from an API, anytime you're doing stuff with a database, you need to escape it. And if you forget once, you're in trouble. Also, what if you need to process that data? Now you have to un-escape it and then re-escape it. It can be kind of annoying. So I prefer to just keep the data as is. Whatever they send me is fine. But if you do it in the HTML, that's when I'll escape it. However, make sure you don't escape more than twice, more than once rather, or I'll get emails like this. Again, common occurrence here, common theme. Now what's really nice about TWIG, which is in Drupal 8, is that by default, TWIG will escape this kind of stuff for you just by using the double curly braces. And if you really need that raw HTML, maybe you're building your HTML database, you can use the raw filter to get that raw value. But it takes care of it for you by default. Really nice. You don't really have to think about it. And this type of functionality is common in a lot of templating systems. So if you're using Laravel, Laravel Blades has that. Smarty Templating Engine has that. Other platforms like Magento and CakePHP have the ability to escape things for you. Maybe they don't do it automatically. Maybe you need to call a function like E to escape it or something else. Or you need the ability to escape that output to protect yourselves from cross-site scripting attacks. So that's cross-site scripting. And that leads nicely into the next vulnerability I like to talk about, which is cross-site request forgery, or CSRF. CSRF attacks allow the attacker to execute unwanted actions in the victim's browser and make their browser issue HTTP requests to other services. And if those services are vulnerable to cross-site request forgery, they'll see those requests as being legitimate requests from the end user. And so we could take advantage of this as attackers to do things like change the user's password to something that we know so we can log in. We could use that to transfer money from someone else's account into ours. Basically anything the user can do in a browser, if the target website is vulnerable to CSRF, we can take advantage of that. Let me show you what I mean by this. So let's say that I want to log on to Facebook. What does that process look like? My browser goes out to Facebook and says, hey, here are the credentials. Log me in. Facebook says, hey, Colin, I recognize you. Those are the right credentials. Here's your newsfeed. And also, here's a little session cookie. Next time we talk, send this cookie back to me so I know who you are. You don't have to log in every time you try and do something. So I go on Facebook, check my newsfeed, update pictures of my cat, do whatever. And then later on, maybe I browse to a potentially malicious website. And when I go to that site, I say, hey, site, show me your home page. It says, sure, here you go. And then inside of that HTML might be something like this. Here we have a form. It's pointing to facebook.com slash password.php. Maybe that's the changed password page. Here's a predefined password and some JavaScript to automatically submit that. So when my browser gets this, it's going to say, oh, I need to do this thing. I need to tell Facebook to change my password to hacked one, two, three. So my browser is going to send that request up, including my session cookie. And Facebook will see, okay, this is Colin. We know him from this cookie. And if they're vulnerable to CSRF, they'll say, all right, your password has been changed. And I'll talk a little bit more about why that happens and how to protect it, protect against it in a moment. But something I want to show you is that you don't necessarily have to execute this type of attack using a malicious server that's out there. You don't have to trick the user to go into a malicious site. We can actually use cross-site scripting to initiate this type of attack on an otherwise trustworthy site. So here's an example of our shortly website. We know that if we put any HTML into there, it'll show on the page. So let's try putting in this image tag. We'll set the source to paypal.com email is me at evil.com amount is lots and lots of money. And when I shorten this and send it to my victim, this is what they'll see. We'll just see a page with an image that doesn't work. But behind the scenes, the browser is saying, oh, I need to send an HTTP request out to paypal.com slash pay. User is this person amount is lots and lots of money. And if PayPal is not double checking for CSRF attacks, they could potentially cause that transaction to go through. So this also sounds really, really bad. How do we protect ourselves against it? You might think, oh, well, we can avoid that whole image source get thing by using post requests, right? Well, no, because post requests are vulnerable too, like I showed in that first example. That's actually one of a few common misconceptions that some developers have when it comes to protecting against these types of attacks. They might say, well, image tags can only make get requests. If I make everything post, anytime someone wants to transfer money, if I just make that post, I'm safe. Not necessarily. They might also think, well, if a user doesn't click the form, it's not going to submit. Again, JavaScript can do that for us. So only using post requests is not a valid approach here. Maybe we'll say, all right, well, what if we have like a secret cookie, like a super secret cookie that we can check that and know if that really is the user we can't really do that either. Because remember, cookies are something on every single request, just like that session cookie. So having a super, super secret cookie isn't really going to do that for you. What you really want to do is use what's called a randomized CSRF token. Basically what we do, if we're Facebook and we're trying to protect ourselves against this type of attack, is we generate a random string for every user, every time there's a form that they're going to submit. We take that random string, we store it in our session. So on the server we store it in the session and then we also add that value as a hidden field to all of their forms. That way when they submit the form, we can check that value. So if they submit the change password form, we'll look to see is there a CSRF token, does it match? If it does match, we'll go ahead and proceed with doing that action. If not, we'll just reject the request. The reason this works is because other services can't see what that CSRF token is. If you're running that malicious website, you can't tell what that CSRF token is to put that into the request that you make the browser execute. And that's due to the browser's same origin policy. Basically, the browser will prevent that website from going out to Facebook and seeing what that value is. So by only putting it on the form only when they're actually viewing that form we can ensure that yes, they are actually submitting the form at that time. It's not some automated process being kicked off from some other website. So that's how we protect against CSRF attacks using randomized CSRF tokens. The next vulnerability I like to share is what's called insecure direct object references. This is a really long name, which really means accessing and manipulating objects that you really shouldn't have access to. And it's pretty straightforward. Let's look at an example here. So here we have a photo I took at the Sunshine PHP conference. And you'll notice that up in the URL we have this ID. This ID is a reference to a photo in Facebook. It is an object reference. It's referencing that photo object. It's a direct reference to that specific object. But what happens if we change this number to something else? What if we just change that nine on the end to an eight? Maybe we'll see Beverly Cooper's awesome selfie. I don't know where I found that, but I love that picture. So even though Facebook never linked us to this page, we were able to change that object reference and access it. This is assuming that Facebook doesn't check those references and that they're vulnerable to insecure direct object references. In reality, they do check it. So if you try to go to that URL, you'd see this message, sorry, this content right now, the link you followed may have expired or you don't have access to see it. So they do protect against it, but if they didn't, you'd be able to just change the ID and see other pieces of data in the system. Viewing other people's photos, yeah, that's kind of bad, but it could be worse. Because we're not just talking about IDs that appear in URLs. What about IDs that appear in forms? What if you had a banking system? What if your local bank had the ability to transfer funds between accounts? And if you looked at the HTML behind the scenes here, you'd see that each account has a unique ID. So maybe this fictitional checking account has an ID of 68465. We're going to transfer money from that to savings. But what if we want to transfer money from someone else's account? What if instead of 68465, we do 09810 and submit that form? If our bank isn't double checking the reference that we're using, we could potentially transfer money out of someone else's account into our own. This is bad. Luckily, protecting against this is really, really easy. Essentially, you just check permission on any type of data input you're getting from the user. Any type of object they're trying to reference. So maybe that's an ID in the URL. Maybe that's a slug or a machine name in the URL. Maybe it's not in the URL. Maybe it's in a form field. Basically, any time the user tries to do something kind of identifier, some kind of object reference, double check it and make sure that they should have access to view or manipulate that object. And if they don't have permission, either show them a 403 forbidden page or maybe a 404 not found page. Technically, the correct one to use is 403 forbidden, telling the browser and the user you don't have access to this. But maybe we want to mask the fact that the thing they're referencing does exist and they don't have access. So maybe we don't want them to figure out what bank account numbers are real and are fake. So in that case, maybe we'll just show a 404 message even when it really does exist and is found. So that's totally up to you and your use cases if you want to do 403 or 404. Of course, we'll also want to check permission on data output. What I mean by this is anytime you go to show someone a list of objects, make sure that they do have access to those things. Don't show the photos that they can't see or a list of bank account numbers that aren't theirs. Pretty straightforward. Do they have permission to access it? Do they have permission to know it exists? Now, when I talk about this and when I talk about using 403 versus 404, I want to make it clear I'm not talking about security through obscurity. Security through obscurity is a term that really means hiding the IDs or hiding the fact that they exist but doing it as the only line of defense. You really want to take a defense in depth approach where you're both checking the IDs and determining do I want to show 403 or 404. So having both layers of defense as you're doing your security. So that's protecting against insecure direct object references and that takes us into the last section here. And in this section I've combined three different vulnerabilities because they are somewhat similar. But if you were to look them up in the OWASP you would see three separate entries. These three vulnerabilities are sensitive data exposure, security misconfiguration and using components with known vulnerabilities. So let's start off by looking at the sensitive data exposure aspect. Sensitive data exposure encompasses many things. One of the things it does encompass is exposing files to the web browsers and web traffic, web crawlers and bots that could give attackers an inside look into how you've set them up. So things like exposing your changelog or your composer.loc or your git folder or environment variables or your robots.txt even. An attacker can use these files to glean information about your site making it easier to attack you. Let's look at some of these. Let's start by looking at the changelog. What if you have a Drupal 7 site with a changelog in your web root? An attacker might be able to go to your website.com and see you're running an old version of Drupal 7. They could then go look and say, okay, what security vulnerabilities exist in 7.9 and use that against you. Some people also have a composer.loc file available in the web root. We can see Google has indexed 491 instances of this. So I can go to any of these results here, pull that up and I can see that this particular person is using an outdated version of symphony. They're using symphony 2.1. In fact, they're not even using a stable release of symphony 2.1. They're using a development version. So again, I can figure out what types of vulnerabilities exist for this. How can I use those to attack this website now that I know this additional information. A more dangerous one is exposing your .git folder. Here are 578 websites that do that. I can click on these results and browse the repository or I could just do a git clone command and just clone down their entire repo. Which is kind of bad. It's even worse if they've committed security information in there. If they've committed something like their MySQL password or API credentials. Maybe they've said, oh crap, we need to delete those and they did a git rm. But that original file is still in the repository. So we can pull that down and we can find that. So that's pretty bad. Another one that's a little more interesting is the robots.txt file. Of course, using a robots.txt file tells web crawlers like Google and Bing and Ask Jeeves. Well, they're not around anymore. But if they were, it would tell Ask Jeeves that, hey, I have these things that I really don't want you to index. But you have to be careful with that because sometimes they'll index your robots.txt file. And even if they didn't, anyone can pull that up and see, okay, this person is running PHP MyAdmin 2.5.1. So now I know they're running that version. I can look for vulnerabilities that affect PHP MyAdmin. So when it comes to robots.txt it's kind of a balancing act. How much information do I want people to see versus, you know, do I want them to know that this exists? Do I want it to be indexed? Probably a better way of protecting PHP MyAdmin would just be putting HTTP basic off on that area. So they have to either pop up, enter a username and password before you can even load the page. Something like that is probably a little bit more secure than just trying to hide the fact but not really hiding it. So, sensitive data exposure. We've talked about these web accessible public files, but there is a lot more to it. Basically, anytime you have any type of private information that is stored, transmitted or backed up in clear text or even with weak encryption. Weak encryption isn't much better than no encryption at all. So if you're going to encrypt something, you don't have information, encrypt it using strong encryption, put it outside of the web root. So I'm talking about things like customer information, credit card numbers, credentials, things like that. So if you're doing any kind of automated imports or exports with a three-party system that requires FTP transfers, don't put the customer data in the web root. Put it in a separate folder and lock FTP to just that folder. Things like that. So we're going to talk about sensitive data exposure. We also have security misconfiguration and using components with known vulnerabilities. When we talk about security misconfiguration, we're talking about things like using a default account, having that enabled. So using the default admin admin or admin password. I'm sure many of you have seen that when you set up your home Wi-Fi routers. That's the default password. If anyone can get access to that page, they can log into your server. So if you have that, make sure you change those passwords. This also covers things like your security configuration. So is your SSH server set up to allow root access? Can someone log in with the username root? Are you using weak encryption keys? Also, are you using out-of-date software? Are you using an old version that has known issues? Are those version numbers exposed? Because if so, like we saw, anyone can go find out you're using SSH 7.9 or symphony 2.1 and go figure out some common vulnerabilities for that. Another more interesting aspect of this is having unused software running. Maybe you have an old FTP server that you used to use five years ago and you've kind of forgotten about it. Well, that's still running. That's a potential attack vector. And there are more other interesting types of attacks, like with the drown attack. Now, it's been a while since I did a drown attack, but if memory serves, drown attack is an attack against SSL TLS. Basically, the latest and greatest versions, you know, TLS 1.2, are really secure. But there are known issues in things like SSL 3, which is an older version. However, some really clever security researchers found out that if you're running both at the same time, people can use SSL 3 and attack that to gather you and break the stronger connections based on information they found in this older protocol that you're still running to support Microsoft XP users. So be careful what kind of software you're using. Make sure you're not running anything that you're not using anymore. Now, when I talked about using components with known vulnerabilities, I said we can go ahead and look that up. The US government actually runs the National Vulnerability Database where you can search for these types of vulnerabilities that you want. Let's try and attack that. What types of vulnerabilities exist for it? I can search the database and I can see this nice issue here. There's a session fixation vulnerability in the remember me login feature in symphony 2.3 before this version and symphony 2.6 before that. Okay, so now I know there's some type of vulnerability. What I can do is just search for this identifier here, the CVE ID and maybe I'll find a blog post about that vulnerability. And then if I scroll down, I can see there's a patch available. I can pull that up and if I maintain a symphony site, I probably want to apply that patch. But if I'm an attacker, I could go ahead and look at this patch and I apologize you can't read it. But let's say you could read it, you could understand what is it patching and kind of figure out what was the original issue that existed. How can I exploit that? And if you know you have, if there are websites out there using the CVE ID exploit and use it to infiltrate that website. So how do we protect against sensitive data exposure and security misconfiguration and using these types of components that have known vulnerabilities? The number one thing to do is to keep your software up to date. If there is a critical patch, if there is a security update, apply that immediately. Do not wait. Now if there are other non-security updates out there, you still want to apply those regularly. Don't let those go up. Because what might happen is that you fall 20 updates behind, security issue comes out. Now you're not just updating from one minor version to the next. You're updating through 20 minor versions. And all those versions might have cell breaking changes that you have to go fix and you have to test. And now it's taking you a lot longer to get that critical update applied. So keep your website up to date. That way when a security issue does come out, you can immediately patch it up. As we mentioned before, you want to make sure you're keeping sensitive data outside of your web root. You can do that one level up. You can do a separate folder of the server, whatever works for you. So we're talking about files that contain version numbers. So readme files, change logs, composer.lock files, your get directory. Keep those out of the web root. If you have any database credentials or API keys, try and keep that out of your web root as well. If you can't, maybe you're kind of stuck to have your passwords in settings.php in the web root, just make sure that file is locked down. Make sure no one can see that output. Make sure no one can mess with that file. Set your permissions correctly. We're also talking about encryption keys. I know, I believe an older version of UberCart, I want to say in Drupal 6 had like a CC keys encryption key. I don't know if that's still the case. I haven't touched UberCart in a while. But it used to store an encryption key in the file system. And if you weren't careful and you put that in some kind of publicly accessible area, someone could download that encryption key. So the best practice for that was just keep it outside of your web root. Same thing here. Of course, when we talk about encryption, we want to make sure we're using strong encryption. We want to encrypt with a strong private key, keep that out of the web root, and then make sure any sensitive data we have is also encrypted. If we can, we want to encrypt our backups and any kind of data in transit. So if we're communicating with a third party API, use HTTPS. Make sure you're securing that communication channel as well. Now, when it comes to passwords, you don't want to use encryption on passwords. You want to use hashing on passwords. Encryption is a two way operation. You can encrypt and you can decrypt. Hashing is one way. Once you hash a password, there's really no way to figure out what the original value was. So if you're dealing with passwords, make sure you're hashing them and you're using encryption. Don't use MD5. It's outdated. It's vulnerable. Use something a little more modern. And then lastly, test your systems. There are a lot of good tools out there that can automatically scan your site and look for these types of issues. They can look and see, do you have a .git folder? Are you exposing version numbers? Are there issues with your encryption? They can check that for you. Of course, you can also test the critical components yourself. Maybe you write your own automated test to check some of this. Maybe you just manually click around and see, okay, is my .git folder vulnerable? You can do that. And this is kind of good advice for all the topics that we've covered in this talk. Just go ahead and test it. Scan your site to see if they're vulnerable. Now this does wrap up on the last set of vulnerabilities I did want to share today, but I would like to provide some next steps. So now that we know this information, what do we do with it? What do we do once we leave this room alone? Again, number one, test your own applications. See if you're vulnerable. If you are, fix them. Maybe you want to just test it just for the fun of it and see, okay, can I hack my own website? What does that look like? That can be kind of fun, and it'll help you protect your website and lock it down. Maybe you want to get more into security. Maybe you want to get into ethical hacking. There are people who hack websites for a living. It's awesome. If you're interested in that, go check it out. Maybe you even want to enter security services, and you can get a lot of information about the security services that are being used to capture the flag events. Some conferences have them, some meetups have them. There are even meetups dedicated just to capture the flag events. Go find them. They're a lot of fun. But more importantly, I think everyone here should stay informed. Stay up to date with what the latest security trends are. What types of software are being deprecated and are no longer secure? If you have any questions, go to robots.txt. Just stay up to date with what's going on in the security community so that you can protect your own applications. So, at this point, pretty much done. I would like to open it up to any questions. If you have a question, if you could step up to the microphone so we can get it recorded as well. I'd appreciate that. Okay, I got a couple. First of all, robots.txt. We've got to have that. Just be aware of what you're putting in it. Obviously, if you want to protect your admin area and say, don't go to slash admin, that's fine to do. But make sure you have security on slash admin. Don't just kind of use that as the only way to hide things. What about comments in JavaScript and stuff like that? Because we have a scanner that goes through and it grabs and it says, hey, you've got these comments. You've got to get rid of them. They could have bad information in them. Do you recommend getting rid of all those of the linting process? If it's easy, you might as well. If it's difficult, if you're not really compiling them or doing any kind of automated process, it's probably okay to have them because anyone can de-uglify your JavaScript if you're at what it's doing anyway. One more. Pantheon, I believe, uses the changelog file as a key to know when, when you've pushed up a oh, you've upgraded your Drupal Core. Are you sure you wanted to do this? If someone like Pantheon is using that, what do we do? Does it get in touch with them and say, hey, we want to get rid of changelog as part of our security measures? Or what do we do? I'm not familiar enough with Pantheon to know if they make that publicly accessible. If that file does become publicly accessible from your website, I would say, just make sure you don't mention any security vulnerabilities. Or if you do, keep it really vague. Say, we fixed the security of that vulnerability with this ticket number. And then only you know what that vulnerability was. Okay, thank you. Hi there, that was a great presentation. I really liked how you showed it out. I was just wondering what automatic tools there are, because you mentioned Check Your Site with an automatic tool. Do you have any names? I should have anticipated this question. To be honest, I know they exist. I haven't really used them myself. I have used them, but I don't use them often. I'm not a security professional. But I know the OWASP top 10, if you go on their website, they do have a lot of tools. And I'm sure other people here have great recommendations on tools you can use. Okay, all right, thank you. I think I've heard of that one. Thank you. Appreciate that. Thanks. Yes. I was just wondering how much of this stuff Drupal already takes care of just in core. Drupal does a lot of it, but you do have to remember to take advantage of it. I have seen examples where newer interns aren't really familiar with how to bind parameters, and they'll just stick variables right in their SQL queries. So, yes, while Drupal does take care of it for you, I think it's good to know about it, just so you don't make those mistakes, and if you end up working on another platform, you know what to look for, and you know what questions to ask to do things securely. Good question. Appreciate it. Is this presentation going to be available to access? Yes. This is my Twitter handle up here. I'll post the link out to the slides in a few minutes. Okay, perfect. And then I think kind of the only other point would be while Drupal does take care of a lot of it, think about all of the other scripts that you have for other things that are kind of ancillary to Drupal that could give away into something somewhere. Absolutely. So I think that's the kind of your biggest vulnerability when you're using a pre-dump platform. Definitely. Good point. Thank you. Alright, if anyone has any other questions, you're welcome to come up and ask me. If you'd like to leave feedback, just go to I think this link here, find my talk. You can leave feedback on it, leave me a comment. Let me know how I did. Thank you.