Weakness reference
CWE-97

Improper Neutralization of Server-Side Includes (SSI) Injection

Server-Side Includes SSI is a server feature that processes special directives embedded in HTML files before sending them to the browser. If a web application…

01Summary

Server-Side Includes (SSI) is a server feature that processes special directives embedded in HTML files before sending them to the browser. If a web application accepts user input and includes it in a file processed as SSI without proper filtering, an attacker can inject malicious SSI directives to execute arbitrary commands, read sensitive files, or manipulate the server's behavior. This weakness is particularly dangerous because it runs with the privileges of the web server process.

02How It Happens

SSI directives use a specific syntax (typically <!--#command arg="value" -->) that the server interprets at request time. When user-supplied data—from form fields, URL parameters, file uploads, or database content—is embedded into a file that the server will parse as SSI, an attacker can craft input containing SSI directives. The server then executes these directives as if they were legitimate parts of the page. The vulnerability occurs when input validation is absent or incomplete, allowing SSI syntax to pass through to the parser unchanged.

03Real-World Impact

Successful SSI injection can lead to arbitrary command execution on the server, allowing an attacker to read sensitive files (configuration files, private keys, source code), modify or delete content, or pivot to further compromise the system. In shared hosting environments, SSI injection may allow access to other users' files. The impact is often as severe as remote code execution, since SSI directives can invoke system commands directly.

04Vulnerable & Fixed Patterns

Vulnerable pattern
from flask import Flask, request

app = Flask(__name__)

@app.route('/generate_page', methods=['POST'])
def generate_page():
    user_name = request.form.get('name', '')
    
    # Vulnerable: user input directly embedded in SSI-processed content
    page_content = f"""
    <html>
    <body>
    <h1>Welcome <!--#{user_name}--></h1>
    </body>
    </html>
    """
    
    with open('output.shtml', 'w') as f:
        f.write(page_content)
    
    return "Page generated"

Why it's vulnerable:
The user_name parameter is directly interpolated into the HTML without sanitization. If an attacker submits name=exec cmd="id", the resulting SSI directive will be executed by the server.

Fixed pattern
from flask import Flask, request
import html

app = Flask(__name__)

@app.route('/generate_page', methods=['POST'])
def generate_page():
    user_name = request.form.get('name', '')
    
    # Fixed: HTML-escape user input to neutralize SSI syntax
    safe_name = html.escape(user_name)
    
    page_content = f"""
    <html>
    <body>
    <h1>Welcome {safe_name}</h1>
    </body>
    </html>
    """
    
    with open('output.html', 'w') as f:
        f.write(page_content)
    
    return "Page generated"
Vulnerable pattern
<?php
$user_comment = $_POST['comment'] ?? '';

// Vulnerable: user input written directly to SSI-processed file
$page = "<html><body>";
$page .= "<p>Comment: <!--#" . $user_comment . "--></p>";
$page .= "</body></html>";

file_put_contents('comment.shtml', $page);
echo "Comment saved";
?>

Why it's vulnerable:
The $user_comment variable is concatenated directly into the page content without escaping. An attacker can inject SSI directives like exec cmd="whoami" that will be parsed and executed.

Fixed pattern
<?php
$user_comment = $_POST['comment'] ?? '';

// Fixed: HTML-escape user input to prevent SSI interpretation
$safe_comment = htmlspecialchars($user_comment, ENT_QUOTES, 'UTF-8');

$page = "<html><body>";
$page .= "<p>Comment: " . $safe_comment . "</p>";
$page .= "</body></html>";

file_put_contents('comment.html', $page);
echo "Comment saved";
?>

05Prevention Checklist

Disable SSI if not needed.
If your application does not require Server-Side Includes, disable the feature in your web server configuration (e.g., remove Includes from Apache Options directives).
Validate and escape all user input.
Use HTML entity encoding (e.g., htmlspecialchars() in PHP, html.escape() in Python) on any user-supplied data before writing it to files that may be processed as SSI.
Use allowlists for dynamic content.
If you must accept user input for page generation, restrict it to a known-safe character set (alphanumeric, spaces, basic punctuation) and reject anything else.
Serve dynamic content as .html, not .shtml.
Reserve .shtml extensions for static or trusted content only; generate user-facing pages as .html files that are not parsed for SSI directives.
Apply principle of least privilege.
Run the web server process with minimal necessary permissions so that even if SSI injection occurs, the damage is limited.
Monitor file writes and server logs.
Watch for unexpected .shtml file creation or SSI directive patterns in access logs, which may indicate an active attack.

06Signs You May Already Be Affected

Check your web server logs for requests containing SSI syntax patterns (e.g., <!--#exec, <!--#include, <!--#config). Review recently modified .shtml files for unexpected content or directives. If you find unfamiliar SSI commands in your pages or evidence of command execution in server logs, investigate immediately for unauthorized access or data exfiltration.