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.
One-line idea: SSJI happens when user input is treated as executable JavaScript on the server.
Core Concepts
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.
Other Injection and Parsing Risks: 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.
Other Injection and Parsing Risks: 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.
Other Injection and Parsing Risks: 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.
Practical Examples
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
Summary
SSJI happens when user input is treated as executable JavaScript on the server.
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.