Logo
blank Skip to main content

9 Web Application Security Best Practices: Real-Life Examples from Apriorit Experts

Web applications collect tons of sensitive information about users and businesses: personal records, security configurations, financial data, etc. The existence of such data combined with poor data protection make web apps lucrative targets for hackers. If businesses want to deliver successful and reliable web applications, investing time and effort in security is a must.

In this article, we discuss what is web application security and share practices Apriorit developers use in our projects. You’ll learn which cybersecurity mechanisms to implement and how to protect an app from common attacks.

This article will be useful for development teams and project owners who are looking for proven ways to enhance the security of their products.

Why should you care about your web app’s security?

Users interact with numerous web applications on a daily basis: email services, messengers, shopping sites, collaboration tools, and so on. Each web app collects personal and often financial data about its users, and such data attracts many malicious actors who try to steal it for their profit.

The 2023 Data Breach Investigations Report by Verizon names web application attacks the second most common breach pattern, rivaled only by system intrusions.

Causes and results of web app attacks in 2023

Investing in web application security reduces your chance of experiencing a data breach or security incident. While completely protecting an app from all threats is impossible, using security best practices during development and addressing common web app vulnerabilities can help you:

  • Ensure compliance with relevant cybersecurity regulations, laws, and standards
  • Avoid reputational and financial losses associated with data breaches
  • Provide a high level of protection for sensitive data of your organization and users
  • Ensure consistent and uninterrupted app performance
  • Gain a competitive advantage on the market

There are hundreds of development practices, techniques, and mechanisms you can use to improve the security of your web app. However, since implementing them all at once is unrealistic and extremely costly, it’s best to focus on the most efficient and universal.

At Apriorit, we choose security measures based on development best practices, threats relevant to the client’s industry or product type, and our internal expertise. Over 20 years of experience with cybersecurity-focused projects helps us understand which measures will be most useful for which types of products.

In the next section, we examine how to secure web applications with a set of best practices often used by Apriorit developers.

Want to make cybersecurity your web app’s killer feature?

Reach out to discuss how Apriorit’s development team can help you with your journey.

How to ensure web app protection: Apriorit’s experience

It’s best to start thinking about app security at the early development stages and implement new mechanisms as the application grows. One way of making your development security-focused is by adopting the DevSecOps approach, which adds security checks at each stage of product growth. You can also upgrade the security of an existing app, but it may require significant code rework or service downtime. 

Before planning security enhancements, we always research common web application vulnerabilities relevant to a particular app. This crucial step allows us to:

  • Understand the risks the product may face and their impact on both software and the organization
  • Prioritize risks by their severity
  • Plan the implementation of relevant security measures
  • Use development time and efforts efficiently

A development team can start their research with the well-known OWASP Top 10 Web Application Security Risks and the CWE Top 25, which describe the most dangerous development mistakes that can be found in web apps. Both lists are supported by communities of cybersecurity professionals and provide detailed descriptions of risks and practices to mitigate them. 

While each web application has its own security challenges, we at Apriorit recommend following this web application security checklist to mitigate common risks and improve your app’s overall protection:

9 ways to ensure web app security

1. Establish secure HTTPS connections

Hypertext Transfer Protocol Secure (HTTPS) is a network security protocol that encrypts data exchanged between a user’s browser and a web server. This encryption prevents sensitive information such as login credentials, bank account information, and other personal information from being stolen by attackers who intercept the communication. 

The URL of a website that establishes a connection over the HTTPS protocol starts from https:// instead of http://. When a site uses HTTPS, the address bar of the browser also displays a padlock icon indicating a secure connection.

HTTP and HTTPS connections

Implementing HTTPS provides several benefits for a web application:

  • Improved data protection
  • Sense of security for end users who know their sensitive data is encrypted
  • Higher priority during search engine optimization

In most cases, despite a web app using HTTPS, it still handles HTTP requests and redirects to the page working over the HTTPS protocol. This redirection can create an opportunity to perform a man-in-the-middle (MitM) attack, where an attacker intercepts requests between a website and its server and is able to manipulate data exchanged in these requests by standing in the middle of the communication. As a result of such an attack, a hacker can manipulate both incoming and outgoing requests.   

When an attacker intercepts the redirection from HTTP to an HTTPS URL, they can change subsequent requests. To prevent these types of attacks, your team should enable the Strict Transport Security Policy (HSTS) by setting the Strict-Transport-Security header in the website’s server responses. Here’s the syntax of the header:  

HTML
Strict-Transport-Security: max-age=<expire-time-in-seconds>[; includeSubDomains][; preload] 

The max-age parameter is set in the header value. It defines a time frame for the web app to communicate with the server over the HTTPS protocol.

Once the browser detects this header in the response, it will know that the site is strictly working over the HTTPS protocol. In this case, the browser will perform the redirection from HTTP to HTTPS instead of the web server.

2. Configure HTTP security headers

HTTP headers define many parameters of information exchanged between a web server and an end user’s machine. They offer a great and easy way to enhance a web app’s communication that rarely requires making many changes in the app. 

There are two ways of protecting HTTP headers:

  • Use security headers that enhance the web app’s security
  • Remove sensitive information from headers

Use security headers

There are several types of security headers to pay attention to: 

X-Frame-Options stops a page from rendering in a frame created by the HTML tags <frame>, <iframe>, <embed>, or <object>. This header helps secure a website from clickjacking attacks. 

The header has two directives: X-Frame-Options: DENY, which stops frame rendering, and X-Frame-Options: SAMEORIGIN, which allows rendering a page in a frame from the same site.

We can also restrict page loading in a frame with the frame-ancestors directive of the Content-Security-Policy header. This header provides more control over displayed content, but it isn’t supported in all browsers, so it’s best to use X-Frame-Options: DENY.

X-Content-Type-Options tells a browser to prevent content sniffing and only look at MIME type in the Content-Type HTTP header. This header prevents the transformation of non-executable MIME types into executable types that open the door to cross-site scripting. When this header is present, it blocks requests with script or style request destinations that don’t match possible MIME types. 

The X-Content-Type-Options header relies on the Content-Type header and has only one directive: X-Content-Type-Options: nosniff

The Referrer-Policy header helps manage how much information will be added to the Referer header that is usually included in HTTP requests. The Referer header contains the address of the resource from which the request was sent, which may contain sensitive data like an origin, path, and query string. 

Modern browsers by default use behavior that corresponds to the Referrer-Policy’s strict-origin-when-cross-origin directive. Modern browsers send a full address only when requesting a resource from the same origin. For cross-origin requests, only the origin is sent. 

This way, the header avoids revealing sensitive information to external resources. It’s recommended to explicitly set Referrer-Policy to strict-origin-when-cross-origin or choose a stricter policy, as its behavior may be different for older browsers. 

The X-XSS-Protection header is a browser protection from cross-site scripting (XSS) attacks that stops page loading when a cross-site scripting attack is detected. The X-XSS-Protection header is deprecated because it can create XSS vulnerabilities, so we don’t recommend using it in modern browsers. To explicitly disable the header, add the X-XSS-Protection: 0 line to the HTTP response.

The Content-Security-Policy header also provides protection from XSS attacks by controlling what resources can be loaded in a web app. The header is defined as Content-Security-Policy: <policy-directive>; <policy-directive>, where policy-directive contains rules for certain types of resources. When adding a Content-Security-Policy to the HTTP response, we can use the <meta> HTML element on the site’s pages: 

HTML
<meta http-equiv="Content-Security-Policy" content="<policy-directive>; <policy-directive>" />

This header also has several sources that impact web app security. Let’s overview them.

The unsafe-inline source is excluded from the Content-Security-Policy header by default and can be added to script-src, style-src, or default-src policies of the header. When it’s excluded, inline JavaScript and styles are blocked. We recommend keeping this option disabled because inline JavaScript and styles are commonly used for XSS attacks. 

However, excluding unsafe-inline restricts a page or whole application from using inline JavaScript, which should be moved to separate JavaScript files and then be loaded from the <script> src tag. It’s possible to avoid adding unsafe-inline to a script-src policy but allow inline scripts by using nonces and hashes. 

A nonce is a token that should be generated for each HTTP request using a cryptographically secure random token generator. For example, in ASP.NET, you can use the RandomNumberGenerator class to generate a nonce. 

To allow inline scripts to execute, generate a nonce: f2a8e4b5c6d7f8e9a0b1c2d3e4f5a6b7 

Add the nonce to the Content-Security-Policy HTTP response header:

HTML
Content-Security-Policy: script-src 'nonce-f2a8e4b5c6d7f8e9a0b1c2d3e4f5a6b7';

And add a nonce attribute to inline <script>

HTML
<script nonce="f2a8e4b5c6d7f8e9a0b1c2d3e4f5a6b7">…</script> 

In this case, the Content-Security-Policy will not block the script. We add a nonce to pages that are rendered on the server side, so the nonce can be randomly generated per each request. 

Hashes are best for static web pages. Content-Security-Policy supports SHA-256, SHA-384, and SHA-512 hash functions. Let’s consider the following script and use SHA-256:

HTML
<script>alert('Hello, World!')</script> 

Compute the SHA-256 hash of your inline script: 

HTML
qnKOv+O3R+fYldfm0o42Mmo2PyElCuMXJHyntHCfom8= 

Add the previously computed hash as a source in the script-src directive: 

HTML
Content-Security-Policy: script-src 'sha256-qnKOv+O3R+fYldfm0o42Mmo2PyElCuMXJHyntHCfom8=';

If an inline event handler cannot be moved to a JavaScript file or inline script, we can add the unsafe-hashes source:

HTML
Content-Security-Policy: script-src 'unsafe-hashes'  'sha256-qnKOv+O3R+fYldfm0o42Mmo2PyElCuMXJHyntHCfom8=';

Similar approaches can be used for a styles policy to handle inline CSS and allow certain inline styles. 

Unsafe-eval source also relates to the script-src directive that allows the use of JavaScript functions that evaluate code from strings. It’s best to exclude this source from a header because those functions are considered bad practice. 

Remove sensitive information from HTTP headers

Some HTTP response headers can reveal server information that may indicate potential web app security vulnerabilities and ways to abuse a web app. Let’s take a look at some of them:

The Server header contains the name and version of software that handled the request on a web server. For example, IIS adds the Server: Microsoft-IIS/10.0 header.

The X-Powered-By header contains information about technologies used by the server. For example, IIS adds the X-Powered-By: ASP.NET header.

The X-AspNet-Version and X-AspNetMvc-Version headers may be added by a server that is using the ASP.NET framework. 

Note that it’s impossible to delete some of these headers from a web application’s code because they are added by a web server and should be deleted at the server level. For example, to delete the X-AspNet-Version header, we need to add the following string to the web.config file of an ASP.NET application: 

HTML
<httpRuntime enableVersionHeader="false" />

Read also

11 Best Practices for Securing a Web Server Based on Node.js Express

Explore how to further enhance your web app’s security with our tried-and-tested server protection practices.

Learn more
11 Best Practices for Securing a Web Server Based on Node.js Express

3. Encrypt sensitive data 

Encrypting sensitive data is one of the most talked about and reliable web application security best practices to protect data from theft, MitM attacks, and other security threats. Even if malicious actors somehow obtain encrypted records, they won’t be able to decrypt and use them.

While there’s no point in encrypting all information in a web application, it’s best to secure sensitive records like user credentials, personal details, financial records, and communication channels. Such data has to be encrypted:

  • At rest, to prevent data leaks because of theft and other malicious activity
  • In transit, to secure it from communication leaks and eavesdropping

A more reliable option will be to implement end-to-end (E2E) encryption — a method of data protection where records are cyphered on the endpoint that sends them and can be deciphered only on a receiver endpoint. E2E is considered the most reliable encryption method because it makes it impossible for anyone besides the sender and recipient to access the data. However, implementing true E2E encryption is challenging and complex. Also, E2E can make data management more complex because not even app administrators have access to all of the app’s data.

Apart from HTTPS, which we already discussed, we recommend using AES and RSA algorithms to secure data at rest, providing an extra layer of protection for stored information.

4. Protect HTTP cookies

HTTP cookies are stored on a user’s device and contain stateful information about the user’s interaction with a web app: settings, credentials, activity history, order details, etc. While their main goal is to improve the user experience, cookies also contain some security information and therefore can introduce security risks to a web app.

For example, browser cookies often contain a user’s authentication information, which makes them a number one target for attacks. That’s why your web app developers should protect cookies with security attributes and prefixes.

How can you secure browser cookies?
  • The HttpOnly attribute restricts cookie access from JavaScript, reducing the risk of XSS attacks. This attribute is often used for authentication cookies. We recommend setting the attribute to true for all your cookies and changing the value to false only when access to a cookie is needed from JavaScript. 
  • Setting the Secure attribute to true ensures that cookies are transmitted only over HTTPS connections.
  • SameSite helps prevent CSRF attacks by restricting how cookies are sent from an external web app to your web app. 
  • The Expires attribute specifies the date and time when a cookie should be deleted by the user’s browser. 
  • The Domain attribute sets a domain to which the cookie will be sent by the browser. If this attribute isn’t set, only the host that sets the cookie will have access to it. If a cookie has the Domain attribute, it will be accessible for the host that sets the cookie and all its subdomains. For example, if we set the attribute to example.com, the cookie will also be accessible for subdomain1.example.com and subdomain2.example.com. 
  • The Path attribute defines the URL path under which the cookie is accessible. It restricts the cookie’s availability to specific directories or pages of a web app. Without a Path attribute, the cookie is available to all pages within the domain. 
  • The __Host- prefix makes a browser require cookies with the Secure attribute, set via HTTPS, without the Domain attribute and with the “/” value for the Path attribute. For example,  __Host-SessionId=1234; Secure; Path=/. Otherwise, the browser will reject the cookie. 
  • The __Secure- prefix requires a cookie to have the Secure attribute and be set via HTTPS. For example, __Secure-SessionId=1234; Secure;  Path=/. If a cookie doesn’t contain these attributes, the browser will reject it.

5. Protect against cross-site request forgery (CSRF) attacks

CSRF is a type of attack that occurs when an attacker creates a malicious website of a legitimate web app and tricks users into sharing their information with this copy. As a result of a CSRF attack, hackers can obtain authentication data along with other sensitive information and perform malicious actions on the legitimate web app on behalf of the user. 

When implementing protection from CSRF attacks, it’s important to combine several measures. Using only one technique will not be enough to ensure application security. 

There are several remediation methods we can use to combat CSRF attacks:

Three ways to protect a web app from CSRF attacks

CSRF tokens 

The most reliable remediation method for a CSRF attack is using anti-forgery tokens. An anti-forgery token is a randomly generated token that is added as a parameter to requests. It’s generated by a web server and is unique for each user so that an attacker can’t guess these tokens. Most modern frameworks, such as ASP.NET, have built-in mechanisms for working with anti-forgery tokens. 

Often, web app forms include a token as a hidden input and send it in the request body on form submission:

HTML
<form method=”POST” action=”targetwebsiteaction”> 



<input name="AntiForgeryToken" type="hidden" value="rBwicadfajl	n3423jfdajll6NA54F2AVoBwGSSuVzE7PWUsRUVG"> 

...	 

</form>

When the form is submitted, the web server validates the token to resolve or reject the request. 

Requests that change the application’s state (add, update, or delete data on the web app) should contain anti-forgery tokens. This is why it’s important not to use GET requests for state-changing requests. 

SameSite cookies 

Using the SameSite attribute helps us to allow or deny cookies sent in cross-site requests. This attribute can take three possible values: None, Lax, or Strict

The None value indicates that cookies will be sent both in cross- and same-site requests. 

Lax and Strict values deny sending cookies in cross-site requests. The only difference between these attributes is that the Lax cookie will be sent when a user comes to the target site from an external site. 

We can mitigate CSRF attacks by setting the SameSite attribute to Lax or Strict. During a CSRF attack, web app cookies with the SameSite attribute set to Lax or Strict will not be sent after form submission from a malicious site. 

Origin and Referer headers and сross-origin resource sharing

Checking the Origin header, we can verify that a request has originated from a safe domain and block cross-site requests. The Referer header helps us prevent cross-origin requests by ensuring that the referer matches the website’s domain. 

Unlike the Origin header, the Referer header includes path information besides the website domain. 

Cross-origin resource sharing (CORS) is a mechanism used mostly in web APIs to control and restrict web apps running at different origins from making requests to our web API. Using CORS helps us prevent or allow websites hosted in different domains to make requests to our web server. 

To implement CORS, we need to use specific response headers like Access-Control-Allow-Origin, Access-Control-Allow-Methods, and Access-Control-Allow-Headers. These headers define which domains can access the web server and which HTTP methods are supported.

Read also

Top 10 Tools for Cross-Browser Testing

Choose among 10 popular tools for your cross-browser testing with an overview of their pros and cons based on our experience.

Learn more

6. Prevent cross-site scripting (XSS) attacks

Cross-site scripting is injecting malicious scripts into a web app page. An injected script can be executed when a user opens the page and accesses sensitive data. Such scripts can display malicious content or redirect a user to a phishing website. 

There are several ways to prevent XSS attacks:

  • Validating input. The most common way to inject a malicious script is by adding it to a form input. To prevent saving a malicious script, we can add input validation on both the client side and server side to make sure forms contain only allowed characters. For example, on the client side, the Pattern attribute for form control can be used to specify the regular expression that the control value should match. 
  • Encoding data. User-controlled data added to a web page should be encoded. For example, the <script> element would be encoded as lt;script&gt;. Encoding prevents the script from executing when inserted into a page. The type of encoding depends on the context of user-controlled data. 
  • Sanitizing code. If there’s an input with HTML markup to be rendered, we can’t use encoding because the encoded version will not be rendered as expected. To prevent XSS attacks in such cases, it’s best to use HTML sanitization that removes dangerous HTML and returns a safe version. For example, if the original HTML looks like <a href= “javascript:alert(‘ XSS attack’)”>Click me</a>, the sanitized version would be <a>Click me</a>.

7. Reduce the chance of successful replay attacks

A replay attack is a type of MitM attack where a request is intercepted and then resent after a certain period of time. The goal of a replay attack is to impersonate the sender of the message and get unauthorized access to the website. This type of attack is particularly dangerous because attackers can just resend the message without decrypting it and still get access rights. 

To protect a web application from replay attacks, we combine some of the following techniques: 

  • Implement an expiration time for authentication tokens to narrow the window of opportunity for replay attacks. 
  • Add a timestamp to each request that will be compared with the server’s current time. If a timestamp is old, the server will reject the request. 
  • Use nonces to make each request unique and store requests in a database or cache. In this case, all requests with the same nonces will be rejected. 

8. Audit third-party packages and libraries

Third-party elements greatly speed up web application development, as they allow a team to integrate a tried-and-tested function into their app instead of developing it from scratch. However, they can contain vulnerabilities that leave an application open to attacks. That’s why we at Apriorit audit all third-party libraries and packages before adding them to our products.

Different web development frameworks suggest their own way of checking vulnerabilities. For example, when using node package manager (npm), we can use npm audit to audit dependencies and list all vulnerabilities. npm also automatically shows vulnerabilities of an installed package after running the npm install command. It’s a good practice to add auditing third-party packages to your continuous integration process.

In .NET applications, we audit packages with the dotnet list [SolutionName].sln package --vulnerable --include-transitive command, which also returns a list of discovered vulnerabilities. 

If these commands detect any vulnerabilities, we recommend one of the following actions: 

  • Check other versions of vulnerable software
  • Update packages and libraries to the latest versions without known vulnerabilities 
  • Apply security patches to address known vulnerabilities 
  • Replace the vulnerable library with an alternative library 

9. Conduct extensive penetration testing

Penetration testing is a targeted approach to security testing that focuses on detecting exploitable vulnerabilities in a web app. It puts an application in near real-world situations where a QA specialist plays the role of a hacker and tries to infiltrate the system by any means, from programming to physical violation.

A regular web app of medium complexity has dozens of entry points, such as cloud access and integrations, that can be used by hackers. QA specialists can use one of the following types of penetration testing:

Three types of penetration testing

It’s generally recommended to employ a third party to conduct penetration testing. While your development team may have the required skills, they might be biased about the app they created and may not see some of its weaknesses. Also, an outside team with expertise in cybersecurity like Apriorit can share testing approaches and best security practices for web applications that your team doesn’t know about. 

Our penetration testing projects result in a report with detailed descriptions of:

  • Pentesting methods used
  • Discovered vulnerabilities
  • Potential consequences of leaving these vulnerabilities unpatched
  • Suggested security improvements

This report gives practical insights about a web app’s security and helps you decide which protection measures to prioritize.

Conclusion

Reliable protection of web applications helps businesses protect their data, service operations, and reputation on the market. We discussed best practices for web application security that will be useful for the majority of web apps; however, each project can face its own security challenges, and your project may require additional security mechanisms.

At Apriorit, we meticulously research and plan web application development projects to deliver apps that comply with our clients’ expectations for security, performance, and ease of use. We choose protection measures based on our experience in the field, our client’s line of work, compliance requirements, relevant standards, and other factors. With this approach, you get an application you can really trust.

Ready to enhance the security of your web app?

Reach out to leverage our experience in building, testing, and improving various types of applications.

Tell us about your project

Send us a request for proposal! We’ll get back to you with details and estimations.

By clicking Send you give consent to processing your data

Book an Exploratory Call

Do not have any specific task for us in mind but our skills seem interesting?

Get a quick Apriorit intro to better understand our team capabilities.

Book time slot

Contact us