Authentifizierungsumgehung über alternativen Pfad oder Kanal

Beschreibung

Authentifizierungsumgehung über alternativen Pfad oder Kanal tritt auf, wenn ein System, das Authentifizierung erfordert, über einen alternativen Einstiegspunkt umgangen werden kann, der nicht dieselben Authentifizierungsanforderungen durchsetzt. Dies umfasst direkten Zugriff auf Backend-APIs, Verwendung von Legacy-Endpunkten, Ausnutzung von Mobile-App-APIs, Zugriff auf administrative Schnittstellen über verschiedene Ports oder Verwendung alternativer Protokolle, die Authentifizierungskontrollen umgehen.

Risiko

Alternative Pfad-Umgehungen umgehen Authentifizierungskontrollen vollständig und geben Angreifern denselben Zugang wie authentifizierten Benutzern. Dies ist besonders häufig in modernen Multi-Interface-Anwendungen, bei denen Web-, Mobile- und API-Schnittstellen inkonsistente Sicherheit haben können. Legacy-Endpunkte, die für Abwärtskompatibilität beibehalten werden, haben oft keine modernen Sicherheitskontrollen. Interne APIs, von denen angenommen wird, dass sie vom Internet nicht zugänglich sind, können exponiert sein. Das Risiko erstreckt sich auf jedes System mit mehreren Einstiegspunkten – das schwächste Glied bestimmt die Gesamtsicherheit.

Lösung

Wenden Sie konsistente Authentifizierung über alle Schnittstellen und Einstiegspunkte an. Implementieren Sie Authentifizierung auf der API/Service-Ebene statt nur auf der Präsentationsebene. Entfernen oder sichern Sie Legacy-Endpunkte ordnungsgemäß. Verwenden Sie API-Gateways, um konsistente Authentifizierungsrichtlinien durchzusetzen. Prüfen Sie alle netzwerkzugänglichen Schnittstellen auf Authentifizierungsanforderungen. Implementieren Sie Netzwerksegmentierung für nur interne Dienste. Wenden Sie Defense in Depth an – verlassen Sie sich nicht allein auf Netzwerkisolierung. Regelmäßige Sicherheitstests sollten die Enumeration alternativer Pfade einschließen.

Häufige Auswirkungen

AuswirkungDetails
ZugriffskontrolleBereich: Authentifizierungsumgehung

Angreifer greifen ohne Authentifizierung auf geschützte Ressourcen zu.
VertraulichkeitBereich: Datenoffenlegung

Sensible Daten über ungeschützte alternative Pfade zugänglich.
IntegritätBereich: Unbefugte Modifikation

Angreifer können Daten über umgangene Schnittstellen modifizieren.

Beispielcode + Korrigierter Code

Anfälliger Code

# ANFÄLLIG: Web-Interface gesichert, aber API nicht
from flask import Flask, request, session, jsonify

# Web-Interface - erfordert Authentifizierung
@app.route('/dashboard')
def dashboard():
    if 'user_id' not in session:
        return redirect('/login')
    return render_template('dashboard.html')

# API-Endpunkt - KEINE AUTHENTIFIZIERUNG!
@app.route('/api/v1/users')
def api_get_users():
    # Angreifer kann direkt zugreifen!
    return jsonify([u.to_dict() for u in User.query.all()])

@app.route('/api/v1/data')
def api_get_data():
    # Keine Auth-Prüfung auf API!
    return jsonify(get_sensitive_data())

# ANFÄLLIG: Legacy-Endpunkt noch erreichbar
@app.route('/legacy/admin')
def legacy_admin():
    # Alte Admin-Schnittstelle ohne moderne Auth!
    return render_template('legacy_admin.html')

# ANFÄLLIG: Interner Dienst ohne Auth exponiert
@app.route('/internal/metrics')
def internal_metrics():
    # Angenommen nur intern, aber tatsächlich erreichbar!
    return jsonify(get_system_metrics())

# ANFÄLLIG: Unterschiedliche Auth für verschiedene Ports
# Haupt-App auf Port 443 erfordert Auth
# Admin-Interface auf Port 8080 hat schwache Auth
// ANFÄLLIG: Web gesichert, REST API nicht
@Controller
public class WebController {

    // Web-Interface - Spring Security konfiguriert
    @GetMapping("/dashboard")
    public String dashboard() {
        // Durch Spring Security Konfiguration gesichert
        return "dashboard";
    }
}

@RestController
@RequestMapping("/api/v1")
public class ApiController {

    // API umgeht Web-Sicherheit!
    // Spring Security nur für Web-Pfade konfiguriert

    @GetMapping("/users")
    public List<User> getUsers() {
        // Keine Authentifizierung!
        return userService.getAllUsers();
    }

    @PostMapping("/admin/action")
    public ResponseEntity<String> adminAction(@RequestBody ActionRequest request) {
        // Admin-Aktion ohne Auth!
        return ResponseEntity.ok(performAction(request));
    }
}

// ANFÄLLIG: Spring Security Fehlkonfiguration
@Configuration
@EnableWebSecurity
public class VulnerableSecurityConfig {

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
            .authorizeHttpRequests(auth -> auth
                .requestMatchers("/dashboard/**").authenticated()
                // /api/** nicht konfiguriert - Standard erlaubt alles!
            );

        return http.build();
    }
}

// ANFÄLLIG: Actuator-Endpunkte exponiert
// application.properties
// management.endpoints.web.exposure.include=*
// Keine Authentifizierung auf Actuator!
// ANFÄLLIG: Express mit inkonsistenter Authentifizierung
const express = require('express');
const app = express();

// Web-Routen - Authentifizierungs-Middleware angewandt
app.use('/web', requireAuth);

app.get('/web/dashboard', (req, res) => {
    res.render('dashboard');
});

// API-Routen - KEINE Authentifizierungs-Middleware!
app.get('/api/users', (req, res) => {
    // Ohne Auth zugänglich!
    User.find({}).then(users => res.json(users));
});

app.get('/api/admin/config', (req, res) => {
    // Admin-Konfiguration exponiert!
    res.json(getConfig());
});

// ANFÄLLIG: GraphQL umgeht REST-Auth
const graphqlSchema = `
    type Query {
        users: [User]
        adminData: AdminData
    }
`;

// GraphQL-Endpunkt hat keine Auth!
app.use('/graphql', graphqlHTTP({
    schema: schema,
    graphiql: true  // Exponiert auch GraphiQL-Interface!
}));

// ANFÄLLIG: WebSocket ohne Auth
const WebSocket = require('ws');
const wss = new WebSocket.Server({ port: 8080 });

wss.on('connection', (ws) => {
    // Keine Authentifizierung auf WebSocket!
    ws.on('message', (message) => {
        handleMessage(JSON.parse(message));
    });
});

// ANFÄLLIG: Mobile API mit schwächerer Auth
app.post('/mobile/login', (req, res) => {
    // Mobile API akzeptiert schwächere Anmeldedaten
    if (req.body.device_id) {  // Nur Geräte-ID!
        res.json({ token: generateToken() });
    }
});

Korrigierter Code

# SICHER: Konsistente Authentifizierung über alle Pfade
from flask import Flask, request, session, jsonify
from functools import wraps

app = Flask(__name__)

# Zentraler Authentifizierungs-Decorator
def require_auth(f):
    @wraps(f)
    def decorated(*args, **kwargs):
        # Session für Web-Anfragen prüfen
        if 'user_id' in session:
            return f(*args, **kwargs)

        # API-Token für API-Anfragen prüfen
        auth_header = request.headers.get('Authorization')
        if auth_header and auth_header.startswith('Bearer '):
            token = auth_header.split(' ')[1]
            user = verify_api_token(token)
            if user:
                request.current_user = user
                return f(*args, **kwargs)

        return jsonify({'error': 'Authentifizierung erforderlich'}), 401

    return decorated

# Standardmäßig auf ALLE Routen anwenden
@app.before_request
def check_authentication():
    # Whitelist öffentlicher Endpunkte
    public_endpoints = {'login', 'register', 'health', 'static'}

    if request.endpoint and request.endpoint not in public_endpoints:
        # Authentifizierung prüfen
        if not is_authenticated():
            return jsonify({'error': 'Authentifizierung erforderlich'}), 401

# SICHER: Sowohl Web als auch API erfordern Authentifizierung
@app.route('/dashboard')
@require_auth
def dashboard():
    return render_template('dashboard.html')

@app.route('/api/v1/users')
@require_auth
def api_get_users():
    return jsonify([u.to_dict() for u in User.query.all()])

@app.route('/api/v1/data')
@require_auth
def api_get_data():
    return jsonify(get_sensitive_data())

# SICHER: Legacy-Endpunkte entfernen oder sichern
# @app.route('/legacy/admin') - ENTFERNT

# SICHER: Interne Dienste nicht exponiert oder ordnungsgemäß gesichert
# Interne Metriken auf separatem internen Port bereitgestellt
# Oder mit derselben Authentifizierung gesichert
@app.route('/internal/metrics')
@require_auth
@require_role('admin')
def internal_metrics():
    return jsonify(get_system_metrics())

# SICHER: API-Gateway-Muster
class APIGateway:
    """Zentrale Authentifizierung für alle Dienste."""

    def __init__(self):
        self.routes = {}

    def register(self, path, handler, auth_required=True):
        self.routes[path] = {
            'handler': handler,
            'auth_required': auth_required
        }

    def handle_request(self, path, request):
        route = self.routes.get(path)
        if not route:
            return {'error': 'Nicht gefunden'}, 404

        if route['auth_required']:
            if not self.authenticate(request):
                return {'error': 'Authentifizierung erforderlich'}, 401

        return route['handler'](request)
// SICHER: Konsistente Spring Security für alle Pfade
@Configuration
@EnableWebSecurity
public class SecureSecurityConfig {

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
            // Auf ALLE Pfade anwenden
            .authorizeHttpRequests(auth -> auth
                // Öffentliche Endpunkte explizit whitelisten
                .requestMatchers("/login", "/register", "/health").permitAll()
                // Alles andere erfordert Authentifizierung
                .anyRequest().authenticated()
            )
            // REST API Authentifizierung
            .oauth2ResourceServer(oauth2 -> oauth2.jwt())
            // Sitzungsauthentifizierung für Web
            .formLogin(form -> form.loginPage("/login"));

        return http.build();
    }
}

// SICHER: Alle Controller erfordern standardmäßig Authentifizierung
@RestController
@RequestMapping("/api/v1")
public class SecureApiController {

    @GetMapping("/users")
    @PreAuthorize("isAuthenticated()")
    public List<User> getUsers() {
        return userService.getAllUsers();
    }

    @PostMapping("/admin/action")
    @PreAuthorize("hasRole('ADMIN')")
    public ResponseEntity<String> adminAction(@RequestBody ActionRequest request) {
        return ResponseEntity.ok(performAction(request));
    }
}

// SICHER: Sichere Actuator-Endpunkte
// application.properties
// management.endpoints.web.exposure.include=health,info
// management.endpoint.health.show-details=when_authorized
// management.endpoints.web.base-path=/actuator
// spring.security.user.name=admin
// spring.security.user.password=${ACTUATOR_PASSWORD}

@Configuration
public class ActuatorSecurityConfig {

    @Bean
    public SecurityFilterChain actuatorFilterChain(HttpSecurity http) throws Exception {
        http
            .securityMatcher(EndpointRequest.toAnyEndpoint())
            .authorizeHttpRequests(auth -> auth
                .requestMatchers(EndpointRequest.to("health", "info")).permitAll()
                .anyRequest().hasRole("ACTUATOR_ADMIN")
            )
            .httpBasic(Customizer.withDefaults());

        return http.build();
    }
}

// SICHER: WebSocket mit Authentifizierung
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {

    @Override
    public void configureClientInboundChannel(ChannelRegistration registration) {
        registration.interceptors(new AuthChannelInterceptor());
    }
}

public class AuthChannelInterceptor implements ChannelInterceptor {

    @Override
    public Message<?> preSend(Message<?> message, MessageChannel channel) {
        StompHeaderAccessor accessor = StompHeaderAccessor.wrap(message);

        if (StompCommand.CONNECT.equals(accessor.getCommand())) {
            String token = accessor.getFirstNativeHeader("Authorization");
            if (!validateToken(token)) {
                throw new AccessDeniedException("Ungültiges Token");
            }
        }

        return message;
    }
}
// SICHER: Konsistente Authentifizierung über alle Pfade
const express = require('express');
const app = express();

// Zentrale Authentifizierungs-Middleware
const requireAuth = async (req, res, next) => {
    // Session prüfen
    if (req.session?.userId) {
        req.user = await User.findById(req.session.userId);
        return next();
    }

    // Bearer-Token prüfen
    const authHeader = req.headers.authorization;
    if (authHeader?.startsWith('Bearer ')) {
        const token = authHeader.slice(7);
        try {
            const decoded = jwt.verify(token, process.env.JWT_SECRET);
            req.user = await User.findById(decoded.userId);
            return next();
        } catch (err) {
            // Ungültiges Token
        }
    }

    return res.status(401).json({ error: 'Authentifizierung erforderlich' });
};

// Authentifizierung auf ALLE Routen außer Whitelist anwenden
const publicPaths = ['/login', '/register', '/health'];

app.use((req, res, next) => {
    if (publicPaths.includes(req.path)) {
        return next();
    }
    return requireAuth(req, res, next);
});

// SICHER: Alle Routen erfordern jetzt Authentifizierung
app.get('/web/dashboard', (req, res) => {
    res.render('dashboard');
});

app.get('/api/users', (req, res) => {
    User.find({}).then(users => res.json(users));
});

app.get('/api/admin/config', requireRole('admin'), (req, res) => {
    res.json(getConfig());
});

// SICHER: GraphQL mit Authentifizierung
const { ApolloServer } = require('apollo-server-express');

const server = new ApolloServer({
    schema,
    context: async ({ req }) => {
        // Benutzer aus Anfrage extrahieren (bereits durch Middleware authentifiziert)
        if (!req.user) {
            throw new AuthenticationError('Nicht authentifiziert');
        }
        return { user: req.user };
    },
    introspection: process.env.NODE_ENV !== 'production'  // In Produktion deaktivieren
});

// SICHER: WebSocket mit Authentifizierung
const WebSocket = require('ws');

const wss = new WebSocket.Server({
    port: 8080,
    verifyClient: async (info, callback) => {
        // Token aus Query-String oder Headers verifizieren
        const token = new URL(info.req.url, 'ws://localhost').searchParams.get('token');

        if (!token) {
            callback(false, 401, 'Authentifizierung erforderlich');
            return;
        }

        try {
            const decoded = jwt.verify(token, process.env.JWT_SECRET);
            info.req.user = await User.findById(decoded.userId);
            callback(true);
        } catch (err) {
            callback(false, 401, 'Ungültiges Token');
        }
    }
});

// SICHER: Mobile API mit denselben Authentifizierungsstandards
app.post('/mobile/login', async (req, res) => {
    const { username, password } = req.body;

    // Dieselbe Authentifizierung wie Web!
    const user = await authenticate(username, password);
    if (!user) {
        return res.status(401).json({ error: 'Ungültige Anmeldedaten' });
    }

    const token = jwt.sign({ userId: user._id }, process.env.JWT_SECRET, {
        expiresIn: '15m'
    });

    res.json({ token });
});

Ausgenutzt in der Praxis

API-Endpunkt-Umgehungen

Zahlreiche Breaches traten auf, als Angreifer unauthentifizierte API-Endpunkte hinter authentifizierten Web-Interfaces entdeckten.

Spring Boot Actuator-Exposition

Exponierte Actuator-Endpunkte haben Angreifern ermöglicht, auf Umgebungsvariablen, Heap-Dumps und andere sensible Informationen zuzugreifen.

Mobile API-Exploits

Mobile Apps mit schwächerer API-Authentifizierung wurden ausgenutzt, um Web-Sicherheitskontrollen zu umgehen.


Tools zum Testen/Ausnutzen

  • Burp Suite — Alternative Pfade und Parameter testen.

  • ffuf — Nach alternativen Endpunkten fuzzen.

  • Nuclei — Templates für exponierte Dienste.

  • API-Dokumentationsanalyse auf Inkonsistenzen.


CVE-Beispiele


Referenzen

  1. MITRE. "CWE-288: Authentication Bypass Using an Alternate Path or Channel." https://cwe.mitre.org/data/definitions/288.html

  2. OWASP. "Testing for Bypassing Authentication Schema." https://owasp.org/www-project-web-security-testing-guide/