Hi, #VueFriend! In this post, we cover security best practices around the Vue ecosystem (in a nutshell). Security issues are always related to the development process, putting at risk the most valuable asset, the end user information. To avoid this kind of scenario, we have some best practices to improve the security in your single-page applications, web pages, web apps, or other builds with Vue. Letâs get started!.
Table of Contents
v-if
vsv-show
, they donât have the same logic- Complex formatting shouldnât be welcome on template expressions (Keep it simple)
- Keep your npm packages updated
- The source code never lies, see what dependencies youâre adding
- Manage and keep secret your âenvironmentâ files
- Use production version (Vue through CDN script)
- DOM is not an option
- Avoid âCross-Site Scriptingâ scenarios (Donât trust)
- Your user doesnât need tons of response details, but your attacker will thank you
- Enable security headers
v-if
vs v-show
, they donât have the same logic
Sometimes beginner Vue developers confuse the v-if and v-show directives or use them indifferently. The v-if
lets you render or not some HTML portion through some logic condition. Otherwise, the v-show always renders the HTML portion through some logic condition too, but it displays or hides the content using the display: none
CSS property. The HTML portion is always reflected on the HTML template. In summary v-if
renders or not, and v-show
always renders, but hides or displays the content.
In that sense, v-if
and v-show
are useful in accomplishing the task to show information according to specific logical criteria. Letâs make a case, there is a user interface with buttons according to roles (admin, normal, and guest). If you are an admin, the button should be green, the normal should be gray, and the guest doesnât have any button. Take a look at the next case if you use the v-show
directive.
BAD
1 | <template> |
Open the browserâs developer tools and inspect the HTML code around the button. Can you see?.
Other buttons are there but are not displayed. If you remove the display: none
CSS property from each one, the buttons turn to show.
It will depend on how well the logic behind the buttons is developed so that when the âguestâ user presses it. Nothing serious happens.
CORRECT
To fix this security issue you can replace v-show
with v-if
as the following code shows.
1 | <template> |
Replacing v-show
for v-if
solves the issue? Not really. It depends on how many levels/layers of security are in your Vue app. The v-show
and v-if
Vue directives have their specific use cases, check this link. Use them wisely.
Complex formatting shouldnât be welcome on template expressions (Keep it simple)
Sometimes we donât have lots of time to spend formatting information. Especially, itâs a common practice to see how beginner developers always start formatting and using map
, filter
, and other JavaScript ways of sorting, filtering, or data formatting as template expressions.
We have the following code example for you. The bad, and the correct approach.
BAD
The next code has a complex filtering logic inside the template part of this vue file.
1 | <template> |
This practice can compromise the extensibility of your Vue page and introduce certain unexpected behaviors if its use is frequent. Also, donât forget to use :key
whenever v-for
is present. :key
is used as an ID or hint for Vue to understand what exactly it is youâre trying to achieve in case the list will be changed some moment.
If you need filtering, sorting, or data formatting. The best practice is to take advantage of the âcomputed propertiesâ as follows.
CORRECT
Maybe it is almost the same filter function, but you should have more control, taking advantage of the Vue features and language suggestions.
1 | <template> |
Many times. When the data structure is not defined correctly, formatting or sorting can introduce inconsistency problems. When you delegate this task to the Vue template part in the Vue file, it doesnât have many options to handle inconsistency problems throwing errors and adopting unexpected behaviors at undefined moments. To mitigate this problem, keep your template Vue part free of complex formatting, and handle them in the script part, having a nice way to deal with strange cases. Complex JavaScript logic at templating can easily turn into security holes.
Keep your npm packages updated
The Vue and node ecosystem has a lot of packages and your project has several of them inside as background dependencies (dependencies of dependencies), some of them could be had security issues at a certain time, but these issues are fixed and put in another package version.
As long as you do not update the compromised dependency, it will continue to have security problems, putting your Vue app at risk.
To check if some of your dependencies are compromised you can run:
1 | npm audit |
This command helps you know if some dependency has related low, moderate, high, or critical issues.
In a practical example try to install the [email protected]
package version (here is the link to the package vulnerability report). This package has 1 high and 1 critical vulnerability related to Cross-Site Scripting and Server-Side Request Forgery (Itâs not dangerous if you donât use this dependency in production đ), then run the audit command.
As you can see. The npm report is detailed, and you can correct the issue by running the following command:
1 | npm audit fix --force |
And automatically the dependency will be updated to a fixed version. Check your dependencies regularly and get always the latest version (if donât break your Vue application). Snyk.io itâs a good page to check more details about vulnerabilities on npm and other package ecosystems.
The source code never lies, see what dependencies youâre adding
It is interesting to see how packages and their creators behave over time.
This year the creator of the packages colors.js
and faker.js
self-sabotage their code in a peaceful protest symbol (follow this link to see the thread from GitHub). The bad thing is in those packages whose creatorsâ credentials are compromised and end up in the hands of cyber criminals who alter their functionality (code injection). Some actions that malicious packages can achieve are:
- Theft of environment variables with valuable keys, credentials, ssh keys, digital wallets, and critical information.
- Installation of malware, crypto miners, ransomware, and more.
Then, what can I do?. Well, check out the source code. Maybe take a little or more time than you have to implement functionality, but this practice is a nicer way to improve your skills and keep secure your web page. A usual path for cybercriminals to take advantage of the npm ecosystem is when the dependency installation is underway. More precisely the preinstall stage, some malicious code will reside around this stage, check this conceptual example of a malware package (actual-malware package), and donât forget to take a little moment to review unofficial or third-party source code.
Manage and keep secret your âenvironmentâ files
Another bad practice is to push the .env or environment files to a GitHub repository or copy and pasting to WhatsApp, Telegram, or another app that is not oriented to keep secret your content. The worst, Vue projects with critical keys and URLs inside the source code as text-plain (hardcoded sensitive information). Avoid any one of these examples. Keep your environment keys in files ignored by any VCS (version control system) like Git, and donât publish them publicly. You donât know what are environment files? check this pseudo-default package for environment files management (dotenv package).
Use production version (Vue through CDN script)
One of many ways to use Vue is via CDN script (in versions less than 2.7.4) as the old JQuery ecosystem style đ. Itâs easy to use as copying the CDN script tag in the HTML static web page. Here is when the problem comes the CDN installation has two versions of Vue (the development and production version). Both work well in the development process, but if you go to production you should use the production version. Is it insecure to use at production?. Yeah, the development version has enabled the Vue Devtools by default, and these Devtools let you know a bunch of details about how works your Vue application. See the following example.
1 |
|
When you have the Vue Devtools extension installed properly in your browser, this simple Vue demo HTML will let you open the Devtools, in more complex applications Vue Devtools are the top-notch tool for a Vue Developer (you can see components, storage, events, and more).
If you take this demo to production as it is. The Devtools will remain open for anyone who can view your Vue application via the internet. To mitigate this problem, you can simply replace the CDN development script tag with the CDN production script tag.
1 | <script src="https://cdn.jsdelivr.net/npm/[email protected]"></script> |
Immediately the Devtools will be disabled (take a look at the next Devtools extension message). Take this advice as ready-to-production best practice.
DOM is not an option
Avoid the use of DOM manipulation with vanilla JavasScript. If you require to use it, test the cornerstone cases (expect critical scenarios will keep you a step ahead of your attackers). Direct DOM manipulation can introduce some unexpected vulnerabilities to your Vue application. Some of them are cookie manipulation, javascript injection, WebSocket-URL poisoning, Ajax request-header manipulation, Denial of service, and the most dangerous. Cross-Site Scripting or most knowledge as XSS vulnerability. What is XSS? the next section covers this vulnerability.
Avoid âCross-Site Scriptingâ scenarios (Donât trust)
XSS (Cross-Site Scripting) is a dangerous vector attack, it has many ways to achieve it, so it has been categorized in several ways (in general is a malicious code injection vulnerability). XSS attacks are related to code injection altering the behavior of our Vue application. Where and how is it possible?. The short answer is âuser inputâ when we talk about users we forget that it could be anyone (attackers and cybercriminals included). So, we should adopt a âno trustâ policy. What does it mean?.
As we mentioned XSS can be achieved in many ways, but generally, bad user input manipulation is the reason number one in how this attack can be successful. Where user input means input fields, forms, URLs params, API endpoints, and other sources in which users can introduce data. This topic is quite extensive, so we prepared this practical and detailed post about Cross-Site Scripting on Vue and how you can avoid it (check it now đ).
Some of the best practices around are:
- Avoid using DOM directly or use it wisely.
- Sanitize html tags, if you need to reflect some string with HTML content inside, you can use libraries like vue-3-sanitize, vue-sanitize, or sanitize-html to sanitize it.
- Forms, URLs, and every input data, in general, must be validated before its use (the validation must be carried out both in the frontend and in the backend). A nice library that can help you to fulfill this task is Yup.
You can also check our write-up covering secure programming training with actionable advice on building secure Web applications.
Inputs are the best door
Your user doesnât need tons of response details, but your attacker will thank you
Vue apps often work in conjunction with a backend, when you submit some form, youâre making a POST or some HTTP method to a web server (API calls), this is responsible to handle this information properly and send a response. The response could be anything, information, some flags, statuses, and so on. But something common in web servers turns around the error responses. When they send extra information about themselves and are reflected on the client side (Vue applications) we talk about an âInformation disclosureâ vulnerability. They donât have to be so detailed, the classic example is when your web server (in a login page context) reveals which field is wrong and which is good by an elimination logic. See the next HTTP request example.
1 | # Request |
Now the attacker knows if the user [email protected]
has an active account on the https://example.com
web page. This is an example of how attackers map user accounts on a lot of popular websites. If you are not convinced the best way to be done is to check your application/user logs in which you find how malicious attackers are trying to map URLs, and users as an initial phase of information recollection.
To stay out of this case decreases your error message details or handles error codes as a protocol between your web server and your Vue Application. This advice applies to API calls, HTTP requests, and similar ones.
Enable security headers
Security headers are a kind of mechanism that the server side has to fight against cyber criminals, attackers, and others, keeping top the web security.
In this section, weâve selected the most significant security headers recommended by the OWASP Foundation. OWASP is a non-profit foundation that works to improve the security of software. Most of the best practices around web security have been introduced by this foundation. To know more about OWASP follows this link.
HTTP Strict Transport Security
HTTP Strict Transport Security (also named HSTS) is a web security policy mechanism that helps to protect websites against protocol downgrade attacks and cookie hijacking. It allows web servers to declare that web browsers (or other complying user agents) should only interact with it using secure HTTPS connections, and never via the insecure HTTP protocol.
Here is an example of how to use enable it.
1 | Strict-Transport-Security: max-age=31536000 ; includeSubDomains |
max-age
defines how much time (in seconds, 1 year in the example) the browser or others should remember that this site can only be accessed using HTTPS. includeSubDomains
is an optional parameter that means this rule applies to all of the siteâs subdomains.
X-Frame-Options
The X-Frame-Options response header (also named XFO) improves the protection of web applications against clickjacking. It instructs the browser whether the content can be displayed within frames. Here is the way how you can use it.
1 | X-Frame-Options: deny |
This header tells the browser that it should block any frame-embedded content inside your website.
X-Content-Type-Options
Setting this header will prevent the browser from interpreting files as a different MIME type than what is specified in the Content-Type HTTP header (e.g. treating text/plain
as text/CSS
). To enable it you can use the following way.
1 | X-Content-Type-Options: nosniff |
Content-Security-Policy
A Content Security Policy (also named CSP) requires careful tuning and a precise definition of policies. If enabled, CSP has a significant impact on the way browsers render pages (e.g., inline JavaScript is disabled by default and must be explicitly allowed in the policy). CSP prevents a wide range of attacks, including cross-site scripting and other cross-site injections. This security policy is widely complex and no exists general recipe or way to add this security header. Each web app, website, or other needs a serious evaluation to put some CSP policies. To understand more about CSP you can consult this official MDN link and our blog post about CSP for Angular Apps.
Referrer-Policy
The Referrer-Policy HTTP header controls which referrer information (sent with the Referer header) should be included with requests. Setting the Referer-Policy as non-referer you are avoiding footprinting and tracking of your website.
1 | Referrer-Policy: no-referrer |
Sometimes the Referer
header is used for analytics reasons in this case you can use same-origin
instead of no-referrer
. Exists more ways to use it (check your case) we encourage you to inspect this link.
Clear-Site-Data
The Clear-Site-Data header clears browsing data (cookies, storage, cache) associated with the requesting website. Clear-Site-Data header is often used as part of the logout process, to ensure that all stored content on the client side like cookies, storage, and the cache is removed. To accomplish this job you can use this header as follows.
1 | Clear-Site-Data: "cache","cookies","storage" |
Test your security headers
Once you have some security headers configured properly check them with the SecurityHeaders web application, here is a good example.
Summary
To begin with, Vue is a beautiful frontend framework. Its ecosystem and community are pretty good. The official documentation is your best source to deal with doubts, questions, and problems donât think too much to consult it. Before using any directive or feature of Vue check it the documentation (it offers functionality explanations, examples, and use cases). As we saw previously, v-if
and v-show
shouldnât used indistinctly.
Avoid using complex data formatting in the Vue templating part. Vue offers several ways how can you organize and deal with data formatting, sorting, or filtering.
Donât forget to audit your npm packages frequently, get updated to the latest versions, and check before the source code inside if it is not part of official or trust dependencies.
Avoid the direct manipulation of DOM if exists a better way to achieve your requirements. DOM manipulation often ends in XSS scenarios.
Also, we mentioned some security headers to add to your Vue app. Well configured, they prevent XSS vulnerabilities, Clickjacking attacks, and others, making your app more secure. To understand these concepts in detail you can check this link to the âOWASP Secure Headers Projectâ complementary look at the MDN Web Docs to consult the functionality of each security header.
Maybe this post doesnât expose some quite specific cases but keeping in mind these practices keeps your applications far away from malicious attacks.
Remember: Thereâs no silver bullet!
Comments