I believe your head has already generated a lot of answers, but let me answer this for you for this post. Frontend is an application's user interface that resides on your computer or anyone over the internet using the application via a browser. In other words, your app is in everyone's browser where they can do whatever they want their using the JavaScript.
This sounds scary, but we have already taken a lot of security measures, from protocols to servers and so on, to avoid that. However, there are still some risks. In this post, I will try to write some of the threats we may or already faced in our applications and some caution measures that can be taken.
Cross-Site Scripting (XSS)
This is the most common threat FE Engineers tackled, and every major FE Framework has support to prevent this. In XSS, an attacker injects malicious JavaScript into a trusted web application to
- Steal cookies and session tokens
- Perform actions as the user (account takeover)
- Modify UI content (defacement)
- Capture keystrokes
- Redirect users to malicious sites
Scenario 1 - Stored (Persistent):
Let me give you an example of how it is done. Let's say you have a blog site, and the readers can put their comments. Now, in this input field, the attacker puts a comment like the following.
<script>
while(true) {
alert('muhuhahaha!')
}
</script>When the user submits this comment, this script comment will be saved in the database. Now, next time any user comes to the same post, when the browser is rendering this specific comment, instead of printing the comment, the browser will treat this as JavaScript and start executing. I believe you guessed what will happen now. The user's browser will hang after running this loop for a while. Now imagine the attacker comments a script that steals cookies and all of your tokens?
Basic Solutions:
- Sanitise When HTML Is Required
import DOMPurify from 'dompurify';
const cleanHTML = DOMPurify.sanitize(comment.html);
element.innerHTML = cleanHTML;2. Framework-Safe Rendering (React / Vue)
{comments.map(c => (
<p key={c.id}>{c.text}</p>
))}Avoid
<div dangerouslySetInnerHTML={{ __html: c.text }} />3. Render as Text, Not HTML
const p = document.createElement('p');
p.textContent = comment.text;
container.appendChild(p);Scenario 2— Reflected (Non-Persistent):
Notice the following URLs
Original URL - https://example.com/search?q=search-term
Twisted URL - https://example.com/search?q=<script>alert(1)</script>This URL demonstrates a classic reflected Cross-Site Scripting (XSS) vulnerability. In the frontend, there is code similar to the following to handle the query parameter.
const params = new URLSearchParams(window.location.search);
const query = params.get('q');
document.getElementById('searchTerm').innerHTML = query;As it is using the .innerHTML, the browser executes the <script> instead of just printing it.
Basic Solutions:
- Use .textContent
element.textContent = query;2. Framework-safe rendering (React example)
<p>{query}</p> // React escapes HTML by default3. Using a library like DOMPurify (When HTML rendering is unavoidable)
import DOMPurify from 'dompurify';
element.innerHTML = DOMPurify.sanitize(query);Content Security Policy (CSP)
CSP is a browser-enforced security mechanism that restricts which sources of content (scripts, styles, images, frames, etc.) a web application is allowed to load and execute.
CSP primarily mitigates Cross-Site Scripting (XSS) by preventing the execution of injected scripts, even if an XSS vulnerability exists in the application.
Content-Security-Policy:
default-src 'self';
script-src 'self';
object-src 'none';Example usage — Next.js
// next.config.js
module.exports = {
async headers() {
return [
{
source: '/(.*)',
headers: [
{
key: 'Content-Security-Policy',
value: "default-src 'self'; script-src 'self'; object-src 'none';"
}
],
},
];
},
};HttpOnly Cookies
An HttpOnly cookie is a browser cookie that cannot be accessed by JavaScript.
How to set up from BE (express.js)
res.cookie('authToken', jwtToken, {
httpOnly: true,
secure: true,
sameSite: 'strict'
});In this way, the attacker won't be able to read using `document.cookie`