Server-Side JavaScript Injection SSJI
Server-Side JavaScript Injection, also called SSJI, is a security vulnerability where user-provided input gets executed as JavaScript code on the server.
This is dangerous because if attacker-controlled code runs on the server, it may interact with the server environment, file system, or other internal resources.
What It Is
SSJI happens when the server evaluates user input as JavaScript code.
Simple meaning:
User sends input
↓
Server treats input like JavaScript code
↓
Code gets executed on the server
↓
Attacker may run malicious logic
The main problem is not just bad input. The problem is that the server executes that input.
Why It Matters
SSJI can be very dangerous because server-side code usually has more power than browser-side code.
If attacker-controlled JavaScript runs on the server, it may lead to:
| Risk | Meaning |
|---|---|
| Arbitrary code execution | Attacker may execute unwanted JavaScript |
| File system access | Malicious code may interact with server files |
| Server compromise | The host system may be affected |
| Data leakage | Internal data may be exposed |
| Global exceptions | Bad execution flow can crash or damage server behavior |
In simple terms:
Client-side code affects the browser.
Server-side injected code can affect the actual server.
Core Flow
A typical SSJI flow looks like this:
Attacker sends malicious JavaScript input
↓
Server accepts the input
↓
Server uses eval/new Function/timer execution/deserialization unsafely
↓
Input becomes executable code
↓
Attacker gains control over server-side behavior
Main Causes
The PDF highlights these major causes of SSJI:
| Cause | Meaning |
|---|---|
| Inadequate input validation | User input is accepted without proper checks |
| Direct execution of user-provided code | Server runs code received from the user |
| Dangerous JavaScript functions | Functions like eval() and Function() can execute strings as code |
| Unsafe timer usage | setTimeout() or setInterval() can cause issues if misused |
| Insecure deserialization | Serialized data is processed without proper validation |
Dangerous JavaScript Functions
Some JavaScript functions can be exploited when they are used with user-controlled input.
The PDF mentions these functions:
eval()
setTimeout()
setInterval()
Function()
new Function()
These functions become risky when user input is passed into them.
Risky example:
const userCode = req.body.code;
// Dangerous: directly executing user-provided code
eval(userCode);
Another risky example:
const userCode = req.body.code;
// Dangerous: creating a function from user input
const fn = new Function(userCode);
fn();
Safer idea:
const userCode = req.body.code;
// Do not directly execute user-provided code
// Validate input and use predefined actions instead
Mental model:
User input should be data, not executable code.
Inadequate Input Validation
Inadequate input validation means the server accepts input without checking whether it is safe and expected.
Risky flow:
Request body contains input
↓
Server accepts it directly
↓
Input reaches sensitive logic
↓
Attack becomes possible
Safer approach:
const userInput = req.body.input;
if (!isValidInput(userInput)) {
return res.status(400).send("Invalid input");
}
function isValidInput(input) {
const regex = /^[a-zA-Z0-9\s]+$/;
return regex.test(input);
}
This example allows only letters, numbers, and spaces.
Important rule:
Always validate user input before using it in server-side logic.
Direct Execution of User-Provided Code
Direct execution of user-provided code is one of the clearest SSJI risks.
Risky example:
const userCode = req.body.code;
// Issue: directly executing user-provided JavaScript code
eval(userCode);
Safer approach:
const userCode = req.body.code;
// Mitigation:
// Do not directly execute user-provided JavaScript code
Instead of executing user input, use predefined operations.
Example:
const allowedActions = {
uppercase: (value) => value.toUpperCase(),
lowercase: (value) => value.toLowerCase(),
};
function runAction(action, value) {
if (!allowedActions[action]) {
throw new Error("Invalid action");
}
return allowedActions[action](value);
}
Flow:
User selects action
↓
Server checks allowed action list
↓
Server runs predefined safe function
Using Dangerous Functions
Using new Function() with user-provided code is dangerous because it creates executable JavaScript from a string.
Risky example:
const userCode = req.body.code;
// Issue: using new Function with user input
const func = new Function(userCode);
func();
Safer approach:
const userCode = req.body.code;
// Avoid using dangerous functions with user-provided code
Functions like eval() and new Function() should not be used with untrusted input.
Simple mental model:
If a function turns a string into code,
do not feed it user input.
setTimeout and setInterval Risks
The PDF mentions that issues in setTimeout() or setInterval() can lead to global exceptions.
If global exceptions are not handled properly, they can cause serious damage.
Risky idea:
const userCode = req.body.code;
// Dangerous if user-controlled code is passed
setTimeout(userCode, 1000);
Safer idea:
setTimeout(() => {
// Run only predefined safe logic
runScheduledTask();
}, 1000);
Important rule:
Timers should run predefined functions,
not user-provided code.
Insecure Deserialization
Deserialization means converting serialized data back into an object or usable data format.
The PDF explains that sometimes JSON data comes in and the application deserializes it without proper checking.
Risky flow:
User sends serialized data
↓
Server deserializes it directly
↓
Unexpected or malicious structure is processed
↓
Security issue may occur
Risky example:
const serializedData = req.body.data;
// Issue: insecure deserialization
const deserializedObject = deserialize(serializedData);
Safer approach:
const serializedData = req.body.data;
try {
const deserializedObject = JSON.parse(serializedData);
if (!isValidData(deserializedObject)) {
return res.status(400).send("Invalid data");
}
// Process the validated object
} catch (error) {
return res.status(500).send("Error while deserializing data");
}
function isValidData(data) {
// Add validation logic for expected structure
return data && typeof data === "object";
}
Important rule:
Do not deserialize and trust data immediately.
Parse it, validate it, then process it.
Other Injection and Parsing Risks
The PDF also shows examples around unsafe input being used in different contexts.
SQL or NoSQL Injection Style Risk
Unsafe input may be directly inserted into a query.
Risky pattern:
const userInput = '{"username":"admin","password":{"$ne":null}}';
const query = `SELECT * FROM users WHERE data = '${userInput}'`;
Problem:
User input is directly mixed with query logic.
Resource Exhaustion
Large input can create resource exhaustion, similar to a denial-of-service style issue.
Example:
const userInput = '{"data":"' + "A".repeat(1000000) + '"}';
const data = JSON.parse(userInput);
Problem:
Huge input can consume server memory or processing power.
Deserialization Vulnerability
Example pattern:
const userInput = '{"type":"Buffer","data":[72,101,108,108,111]}';
const buffer = JSON.parse(userInput);
const text = Buffer.from(buffer).toString();
Problem:
Parsed data should still be validated before being trusted.
Prevention Summary
To reduce SSJI risk:
Do not execute user input
↓
Avoid dangerous functions
↓
Validate input strictly
↓
Deserialize safely
↓
Handle global exceptions
↓
Limit input size
Basic Checklist
Use this checklist to prevent SSJI:
- Do not pass user input into
eval(). - Do not pass user input into
new Function(). - Do not execute user-provided JavaScript code directly.
- Avoid using string-based logic in
setTimeout()andsetInterval(). - Validate all request body input.
- Use whitelist-style validation for allowed characters, actions, or values.
- Parse JSON safely using
try/catch. - Validate deserialized data before processing it.
- Check input size to avoid resource exhaustion.
- Avoid directly mixing user input into query logic.
- Handle global exceptions properly.
- Treat user input as data, not code.
Interview Style Answer
Server-Side JavaScript Injection, or SSJI, is a vulnerability where user-provided input is executed as JavaScript on the server. This can happen when dangerous functions like eval(), Function(), setTimeout(), or setInterval() are used with untrusted input.
It is dangerous because the injected code runs in the context of the server and may interact with the file system or internal server resources. Common causes include inadequate input validation, direct execution of user-provided code, use of dangerous functions, and insecure deserialization.
To prevent SSJI, we should never execute user input as code, avoid dangerous functions with untrusted data, validate input strictly, use predefined allowed actions, safely parse and validate JSON, limit input size, and handle global exceptions properly.
One-Line Summary
SSJI happens when user input is treated as executable JavaScript on the server.
Final Mental Model
User Input + Server-Side Code Execution = SSJI Risk
Remember it like this:
Input should be data, not code.
Validate before use.
Never eval user input.
Never create functions from user input.
Deserialize safely.
Handle exceptions properly.