Top 4 web security flaws you should fix right now

11/30/2015 10:49:00 AM
Tweetable

Hackers are getting far more sophisticated at stealing websites' databases, deploying tricks that are fairly hard to defend against at the web developers' level, such as so-called "social engineering" attacks which trick users into disclosing important passwords. Nevertheless, old-fashioned, preventable hacks still account for the vast majority of data breaches on the internet. VTech's recent loss of 4.8 million users' data through an SQL injection vulnerability shows how these security flaws remain common and devastating.

So here's the top 4 most common security problems and how to solve them.

  1. Not using TLS/SSL

    Problem:Go look at Troy Hunt's excellent post—there's a huge misconception among web users and web developers that encryption and SSL is merely a matter of keeping sensitive data secret. Far more importantly SSL guarantees a website's authenticity, since without it a hacker can replace content with their own to harvest passwords or infect visitors with malware. Distributed Denial of Service attacks are often performed by injecting javascript into random non-encrypted web traffic, which the Chinese government apparently did to censor political opponents. There are free exploit kits anyone can use to hijack traffic to any unencrypted site they want with just a click. Here's one.

    Solution:

    • Buy yourself an SSL certificate and upgrade your webhost to SSL. You can test the strength of your server's SSL implementation here.
    • Since browsers attempt to go to http if https is not specified, you'll need to redirect them, but it's important to only redirect GET requests. POST,PUT, and DELETE should throw an error if the connection is not https, because you want a loud obnoxious failure whenever a web developer accidentally POSTs sensitive information to a non-encrypted URL (which differs by just one character, this is a common typo).
    • You should also implement HSTS and set the Strict-Transport-Security response header to cause browsers to remember to use only SSL for future requests to that domain.
    I'm hosting this blog with Google's Blogger, which unfortunately won't let me upload my SSL certificate. Also unfortunate, many web hosts still treat SSL—which is really no more costly to them—as a premium service that costs extra. In an ideal world browsers would simply decline all non-https requests and any https request that fails to present a valid security certificate, to force lazy web hosts to get back to work.

  2. SQL injection

    Problem: SQL injection happens when your app generates SQL statements based on user-supplied inputs like search terms to do database operations. Users could write things like "DROP TABLE" and screw up the data. This isn't just a problem when you are concatenating SQL statements either—if you pass user-data to as parameters to a stored procedure and call EXEC inside the procedure, users can still inject whatever SQL commands they want.

    Solution: 99 percent of you should be using an ORM like .Net's Entity Framework. I mean the linq-to-entities functionality—no passing concatenated queries to the ORM, and no stored procedures. You probably can't write SQL that will beat the ORM, and certainly not that would beat the ORM by enough to compensate for the extra development time manually writing SQL requires. As long as you stick to linq-to-entities you're safe from SQL injection. If you really need to write an SQL statement (look: you really don't) then make sure that the query is correctly parameterized. This can't be done at the SQL level (the DECLARE @param1 nvarchar(300) syntax is still vulnerable to injection)—you need to parameterize at the web-framework level. In .Net, this means adding parameters through the parameter collection of the SqlCommand object.

  3. Cross-site scripting

    Problem: Frequently, websites display various kinds of user-generated data. Sometimes this is obvious—such as when readers leave comments on a blog post— but it can happen in less obvious ways such as when a user uses the search box, and the results page displays the user's search term. It can also happen when your ad network just doesn't give a crap. You have to bear in mind that users and third parties can input valid HTML, and when you display this HTML in the page, it will render as HTML. You don't want a commenter to be able to write a javascript tag that will then be executed on every reader's browser!

    Solution: You need to properly escape all user inputs. For the most part, these are the characters that can cause trouble: &, <, >, ", ', /.

    • The first thing to do is set your Content-Security-Policy header. With this header you can explicitly tell the browser not to allow clients or third parties to modify the DOM or inject scripts. Older browsers don't implement this, and it doesn't protect against vulnerabilities served from your own scripts, but it's a start.
    • If you are using javascript to display text entered by a user on the page, then your javascript should escape the text before displaying it. A common way to do this is:

      function escapeHtml(str) {
          var div = document.createElement('div');
          div.appendChild(document.createTextNode(str));
          return div.innerHTML;
      };
      This will escape all HTML characters and render them as text rather than DOM elements. It's pretty standard, and basically what jQuery does. But you should be aware that it does not escape double quotes, and will therefore allow vulnerabilities whenever you insert user-generated text as an attribute, as in for example:
      var ele='<div ng-model="+escapeHTML(boo" some-attribute-you-dont-want="true)+"></div>';
      . You could potentially use javascript's built-in encodeURIComponent to escape data that will be stored in attributes, since human-readability isn't required.
    • A lot of user-content won't be added to the page using javascript though—reader comments, for example, will generally be served right up with the HTML from the server, so escaping in javascript simply won't catch it before it renders in user's browsers. You need to escape this stuff on the server, either when you save it to the database or before you render it into the HTML. The .Net framework has HtmlEncode and HtmlAttributeEncode facilities built in for this purpose. As an alternative to escaping, you could instead reject requests that contain disallowed markup.

  4. Cross-site request forgery

    Problem:Web browsers send all the cookies for a domain along with each request to that domain, meaning that if a user has bob.com and alice.com open in their browser, a script on bob.com can POST (say, a purchase order) to alice.com and the browser will send the user's actual authentication cookie for alice.com along with this forged request. Thus merely checking that the requester has valid credentials is not enough because you cannot verify that the request originated on your site.

    Solution:Use validation tokens for all request methods other than GET. The way this works is you have a pair of related randomly-generated numbers (should be different for every request), set one of them in a cookie for alice.com, and put the other one in the form to be POSTed back along with the request. Make sure that you disallow cross-origin requests (this is default in all browsers). Then the request is POSTed, check the form token against the cookie token—bob.com can't read the cookie token and therefore cannot produce a matching form token. Here's more on the validation token implementations in ASP.Net.