Unsichere Drittanbieter-Ressource
Beschreibung
Unsichere Drittanbieter-Ressource tritt auf, wenn eine Webanwendung ausführbaren Code oder Ressourcen von einer Drittanbieter-Quelle einbindet, die böswillig modifiziert werden könnten. Dies umfasst JavaScript-Bibliotheken, die von CDNs geladen werden, externe Stylesheets, Schriftarten, Bilder und iframes. Wenn die Drittanbieter-Ressource kompromittiert, modifiziert oder über eine unsichere Verbindung bereitgestellt wird, können Angreifer bösartigen Code in Websites injizieren, die von diesen Ressourcen abhängen. Dies ist eine Form von Supply-Chain-Angriff, die auf Webanwendungen abzielt.
Risiko
Die Kompromittierung von Drittanbieter-Ressourcen hat massive Sicherheitsvorfälle verursacht. Die Magecart-Angriffe haben Tausende von E-Commerce-Seiten durch kompromittierte gemeinsame JavaScript-Bibliotheken betroffen. British Airways erlitt eine Datenschutzverletzung, die 380.000 Kunden durch kompromittierte Drittanbieter-Skripte betraf. CDN-Kompromittierungen können Millionen von Websites gleichzeitig betreffen. Selbst legitime CDNs können durch Cache-Poisoning korrumpierte Inhalte liefern. Man-in-the-Middle-Angriffe auf HTTP-Ressourcen ermöglichen Code-Injektion.
Lösung
Verwenden Sie Subresource Integrity (SRI), um zu überprüfen, dass abgerufene Ressourcen den erwarteten Hashes entsprechen. Laden Sie Ressourcen nur über HTTPS. Hosten Sie kritische JavaScript-Bibliotheken selbst, anstatt sich auf CDNs zu verlassen. Implementieren Sie Content Security Policy (CSP), um Ressourcenquellen einzuschränken. Überprüfen Sie regelmäßig Drittanbieter-Abhängigkeiten. Verwenden Sie npm audit, Snyk oder ähnliche Tools zum Abhängigkeits-Scanning. Minimieren Sie Drittanbieter-Abhängigkeiten. Überwachen Sie Änderungen an Drittanbieter-Ressourcen.
Häufige Auswirkungen
| Auswirkung | Details |
|---|---|
| Integrität | Bereich: Code-Injektion Kompromittierte Drittanbieter-Ressourcen können beliebiges JavaScript in den Browsern der Benutzer ausführen. |
| Vertraulichkeit | Bereich: Datendiebstahl Bösartige Skripte können Anmeldedaten, Zahlungsinformationen und Session-Token stehlen. |
| Verfügbarkeit | Bereich: Dienststörung Verunstaltete oder defekte Ressourcen können Websites unbenutzbar machen. |
Beispielcode und Lösung
Verwundbarer Code
<!-- VERWUNDBAR: Keine Integritätsprüfung bei CDN-Ressourcen -->
<script src="https://cdn.example.com/jquery-3.6.0.min.js"></script>
<script src="https://cdn.example.com/bootstrap.min.js"></script>
<!-- VERWUNDBAR: Laden über HTTP -->
<script src="http://cdn.example.com/library.js"></script>
<!-- VERWUNDBAR: Drittanbieter-Analytics ohne Integrität -->
<script src="https://analytics.thirdparty.com/tracker.js"></script>
<!-- VERWUNDBAR: Externe Stylesheets -->
<link rel="stylesheet" href="https://cdn.example.com/styles.css">
<!-- VERWUNDBAR: Drittanbieter-iframe ohne Sandbox -->
<iframe src="https://widget.thirdparty.com/embed"></iframe>
<!-- VERWUNDBAR: Dynamisches Laden von Skripten -->
<script>
var script = document.createElement('script');
script.src = 'https://cdn.example.com/dynamic.js';
document.head.appendChild(script);
</script>
# VERWUNDBAR: Server-seitige Einbindung von Drittanbieter-Ressource
import requests
@app.route('/proxy/script')
def proxy_script():
# Abrufen und Bereitstellen eines Drittanbieter-Skripts ohne Überprüfung
response = requests.get('https://cdn.example.com/library.js')
return response.text, 200, {'Content-Type': 'application/javascript'}
# VERWUNDBAR: Template bindet externe Ressourcen ein
@app.route('/page')
def render_page():
# Externe Ressourcen-URL aus Konfiguration - könnte kompromittiert sein
cdn_url = config.get('CDN_URL')
return render_template('page.html', cdn_url=cdn_url)
// VERWUNDBAR: Dynamisches Laden von Ressourcen in Node.js/Express
app.get('/widget', (req, res) => {
const widgetUrl = req.query.url; // Benutzer-kontrolliert!
res.send(`<script src="${widgetUrl}"></script>`);
});
// VERWUNDBAR: npm-Paket ohne Lock-Datei
// package.json erlaubt schwankende Versionen
{
"dependencies": {
"lodash": "^4.0.0", // Könnte kompromittierte Version erhalten
"express": "*" // Jede Version!
}
}
Sichere Lösung
<!-- SICHER: Subresource Integrity (SRI) -->
<script
src="https://cdn.jsdelivr.net/npm/[email protected]/dist/jquery.min.js"
integrity="sha384-vtXRMe3mGCbOeY7l30aIg8H9p3GdeSe4IFlP6G8JMa7o7lXvnz3GFKzPxzJdPfGK"
crossorigin="anonymous">
</script>
<script
src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.min.js"
integrity="sha384-ODmDIVzN+pFdexxHEHFBQH3/9EggsS7T6UFxMIUY4OHsW9A7EjdgLCw3K9cPRmFw"
crossorigin="anonymous">
</script>
<!-- SICHER: SRI für Stylesheets -->
<link
rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css"
integrity="sha384-gH2yIJqKdNHPEq0n4Mqa/HGKIhSkIHeL5AyhkYV8i59U5AR6csBvApHHNl/vI1Bx"
crossorigin="anonymous">
<!-- SICHER: Sandboxed iframe -->
<iframe
src="https://widget.trusted.com/embed"
sandbox="allow-scripts allow-same-origin"
loading="lazy">
</iframe>
<!-- SICHER: Content Security Policy -->
<meta http-equiv="Content-Security-Policy"
content="script-src 'self' https://cdn.jsdelivr.net;
style-src 'self' https://cdn.jsdelivr.net;
img-src 'self' data:;
frame-src https://widget.trusted.com;">
# SICHER: Server-seitige Ressourcenüberprüfung
import requests
import hashlib
KNOWN_HASHES = {
'jquery-3.6.0.min.js': 'sha384-vtXRMe3mGCbOeY7l30aIg8H9p3GdeSe4IFlP6G8JMa7o7lXvnz3GFKzPxzJdPfGK',
}
def verify_resource(url, content):
"""Überprüft, ob Ressourceninhalt mit bekanntem Hash übereinstimmt."""
filename = url.split('/')[-1]
expected_hash = KNOWN_HASHES.get(filename)
if not expected_hash:
raise ValueError(f"Unknown resource: {filename}")
# Hash parsen
algorithm, expected = expected_hash.split('-')
if algorithm == 'sha384':
actual = base64.b64encode(
hashlib.sha384(content).digest()
).decode()
if actual != expected:
raise ValueError(f"Resource integrity check failed for {filename}")
return True
@app.route('/proxy/script')
def proxy_script_safe():
url = 'https://cdn.jsdelivr.net/npm/[email protected]/dist/jquery.min.js'
response = requests.get(url)
# Integrität vor Bereitstellung überprüfen
verify_resource(url, response.content)
return response.text, 200, {'Content-Type': 'application/javascript'}
# SICHER: Kritische Ressourcen selbst hosten
@app.route('/static/js/<filename>')
def serve_local_script(filename):
# Aus lokaler verifizierter Kopie bereitstellen
allowed_files = {'jquery.min.js', 'bootstrap.min.js'}
if filename not in allowed_files:
abort(404)
return send_from_directory('static/js', filename)
# SICHER: CSP-Header
@app.after_request
def add_security_headers(response):
csp = (
"default-src 'self'; "
"script-src 'self' https://cdn.jsdelivr.net 'sha256-...'; "
"style-src 'self' https://cdn.jsdelivr.net; "
"img-src 'self' data:; "
"frame-ancestors 'self';"
)
response.headers['Content-Security-Policy'] = csp
return response
// SICHER: SRI-Hashes beim Build generieren
const crypto = require('crypto');
const fs = require('fs');
function generateSRI(filePath) {
const content = fs.readFileSync(filePath);
const hash = crypto.createHash('sha384').update(content).digest('base64');
return `sha384-${hash}`;
}
// Build-Skript zum Generieren von Integritäts-Hashes
const scripts = [
{ file: 'vendor/jquery.min.js', output: 'jquery' },
{ file: 'vendor/bootstrap.min.js', output: 'bootstrap' }
];
const integrityHashes = {};
scripts.forEach(script => {
integrityHashes[script.output] = generateSRI(script.file);
});
fs.writeFileSync('sri-hashes.json', JSON.stringify(integrityHashes, null, 2));
// SICHER: Gesperrte Abhängigkeiten
// package-lock.json oder yarn.lock stellt exakte Versionen sicher
{
"dependencies": {
"lodash": "4.17.21", // Exakte Version
"express": "4.18.2" // Exakte Version
}
}
// SICHER: Dynamisches Laden mit Integritätsprüfung
async function loadScriptWithSRI(url, expectedHash) {
const script = document.createElement('script');
script.src = url;
script.integrity = expectedHash;
script.crossOrigin = 'anonymous';
return new Promise((resolve, reject) => {
script.onload = resolve;
script.onerror = () => reject(new Error(`Failed to load ${url}`));
document.head.appendChild(script);
});
}
// Verwendung
await loadScriptWithSRI(
'https://cdn.jsdelivr.net/npm/[email protected]/lodash.min.js',
'sha384-...'
);
// SICHER: CSP-Konfiguration in Express
const helmet = require('helmet');
app.use(helmet.contentSecurityPolicy({
directives: {
defaultSrc: ["'self'"],
scriptSrc: [
"'self'",
"https://cdn.jsdelivr.net",
// Bestimmte Inline-Skripte per Hash erlauben
"'sha256-abc123...'"
],
styleSrc: ["'self'", "https://cdn.jsdelivr.net"],
imgSrc: ["'self'", "data:"],
frameSrc: ["'none'"],
upgradeInsecureRequests: []
}
}));
Ausgenutzt in der Praxis
British Airways Datenschutzverletzung (2018)
Angreifer kompromittierten eine Drittanbieter-JavaScript-Bibliothek auf der British-Airways-Website und injizierten Code, der Zahlungskartendaten von 380.000 Kunden über einen Zeitraum von zwei Wochen abgriff.
Magecart-Angriffe (2018-heute)
Mehrere E-Commerce-Seiten wurden durch gemeinsam genutzte JavaScript-Bibliotheken kompromittiert, wobei Angreifer Zahlungs-Skimming-Code injizierten, der Millionen von Kunden auf Tausenden von Websites betraf.
event-stream npm-Paket-Kompromittierung (2018)
Ein beliebtes npm-Paket (event-stream) wurde durch einen böswilligen Maintainer kompromittiert, der Code zum Stehlen von Kryptowährung aus bestimmten Anwendungen hinzufügte.
Tools zum Testen und Ausnutzen
-
SRI Hash Generator -- SRI-Hashes für Ressourcen generieren.
-
CSP Evaluator -- Content Security Policy analysieren.
-
npm audit -- Auf verwundbare Abhängigkeiten prüfen.
-
Snyk -- Abhängigkeits-Schwachstellen-Scanning.
CVE-Beispiele
-
CVE-2018-11776 -- Apache Struts Remote-Code-Ausführung über kompromittierte Bibliothek.
-
CVE-2021-44228 -- Log4j (Auswirkungen von Bibliothekskompromittierung).
-
CVE-2019-10744 -- lodash Prototype Pollution.
Referenzen
-
MITRE. "CWE-1035: Insecure Third Party Resource." https://cwe.mitre.org/data/definitions/1035.html
-
MDN. "Subresource Integrity." https://developer.mozilla.org/en-US/docs/Web/Security/Subresource_Integrity