Pentest 48.1 MEDIUM + 50.1 MEDIUM: customerEmailLabel-Strip + SSRF strict
48.1 (XSS in customerEmailLabel):
- Neuer sanitizeCustomerEmailLabel-Helper (stripHtml + trim +
60-Zeichen-Cap)
- Eingesetzt in createProviderConfig + updateProviderConfig
(Write-Pfad) und getProviderPublicSettings (Read-Defensive)
- Damit landet kein <script>/<img onerror>/<svg onload> mehr roh
in der DB, das Längen-Limit ist serverseitig erzwungen, und
Alt-Daten kommen über /public-settings ebenfalls gestrippt raus.
50.1 (SSRF, unvollständige Blockliste bei test-connection):
- safeResolveHost + assertAllowedHost akzeptieren jetzt
{ strict: boolean }. strict=true → isPrivateOrBlockedHost
(sperrt 127/8, 10/8, 172.16/12, 192.168/16, ::1, fc00::/7
unabhängig von SSRF_BLOCK_PRIVATE_IPS).
- test-connection und test-mail-access nutzen strict=true per
Default. Opt-out via env SSRF_ALLOW_INTERNAL_TESTING=true
für On-Prem mit internem Plesk.
- Defense-in-Depth: assertAllowedHost wird jetzt auch VOR der
DNS-Resolution auf den Hostname selbst angewendet, damit
Block-Hostnames (z.B. "metadata.google.internal", "localhost")
nicht via custom-DNS umgangen werden können.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -123,10 +123,15 @@ export async function testConnection(req: Request, res: Response): Promise<void>
|
||||
// SSRF-Guard inkl. DNS-Rebinding: testData.apiUrl-Hostname zu IP auflösen
|
||||
// und prüfen. Wenn DNS auf eine geblockte IP zeigt, abbrechen – ohne dass
|
||||
// ein zweiter Lookup zur Connection-Zeit eine andere IP liefern könnte.
|
||||
// Pentest 50.1: strict=true – test-connection darf NIE auf private IPs,
|
||||
// Loopback oder Cloud-Metadata zeigen, unabhängig von
|
||||
// SSRF_BLOCK_PRIVATE_IPS. On-Prem mit echtem internen Plesk kann das
|
||||
// per SSRF_ALLOW_INTERNAL_TESTING=true opt-outen (Default: blockiert).
|
||||
const allowInternalTesting = (process.env.SSRF_ALLOW_INTERNAL_TESTING || '').toLowerCase() === 'true';
|
||||
if (testData?.apiUrl) {
|
||||
try {
|
||||
const url = new URL(testData.apiUrl);
|
||||
await safeResolveHost(url.hostname, 'apiUrl-Host');
|
||||
await safeResolveHost(url.hostname, 'apiUrl-Host', { strict: !allowInternalTesting });
|
||||
} catch (err) {
|
||||
if (err instanceof Error && (err.message.includes('geblockte') || err.message.includes('DNS'))) {
|
||||
const ctx = contextFromRequest(req);
|
||||
@@ -247,12 +252,14 @@ export async function testMailAccess(req: Request, res: Response): Promise<void>
|
||||
// geblockte IPs prüfen. Connection läuft danach gegen die IP, der
|
||||
// ursprüngliche Hostname wird als TLS-servername gesetzt – damit kann
|
||||
// ein zweiter DNS-Lookup keine andere IP unterschieben.
|
||||
// Pentest 50.1 analog testConnection: strict, opt-out via env.
|
||||
const allowInternalTesting = (process.env.SSRF_ALLOW_INTERNAL_TESTING || '').toLowerCase() === 'true';
|
||||
let smtpResolved: { ip: string; servername: string };
|
||||
let imapResolved: { ip: string; servername: string };
|
||||
try {
|
||||
[smtpResolved, imapResolved] = await Promise.all([
|
||||
safeResolveHost(smtpServer, 'SMTP-Server'),
|
||||
safeResolveHost(imapServer, 'IMAP-Server'),
|
||||
safeResolveHost(smtpServer, 'SMTP-Server', { strict: !allowInternalTesting }),
|
||||
safeResolveHost(imapServer, 'IMAP-Server', { strict: !allowInternalTesting }),
|
||||
]);
|
||||
} catch (err) {
|
||||
const ctx = contextFromRequest(req);
|
||||
|
||||
Reference in New Issue
Block a user