Offenlegung einer WSDL-Datei mit sensiblen Informationen
Beschreibung
Die Offenlegung einer WSDL-Datei mit sensiblen Informationen tritt auf, wenn Web Services Description Language (WSDL)-Dateien öffentlich zugänglich sind und Informationen enthalten, die Angreifern helfen. WSDL-Dateien beschreiben Webservice-Schnittstellen, Methoden, Parameter und Endpunkte. Wenn sie exponiert werden, offenbaren sie die Angriffsfläche, interne Methodennamen, Datenstrukturen, versteckte administrative Funktionen und manchmal Anmeldedaten oder interne URLs.
Risiko
Angreifer entdecken alle verfügbaren Webservice-Methoden. Interne administrative Funktionen werden offenbart. Datenstrukturen legen Datenbank-Schema-Informationen offen. Versteckte Debug- oder Testmethoden werden zugänglich. Interne URLs und Server-Topologie werden offengelegt. Parametertypen helfen bei der Erstellung von Injection-Angriffen. Anmeldedaten oder API-Schlüssel können in WSDLs eingebettet sein.
Lösung
Deaktivieren Sie WSDL-Auto-Generierung in der Produktion. Beschränken Sie den Zugriff auf WSDL-Endpunkte. Entfernen Sie sensible Methoden aus öffentlichen WSDLs. Erstellen Sie separate WSDLs für internen/externen Gebrauch. Implementieren Sie Authentifizierung für WSDL-Zugriff. Überprüfen Sie WSDLs vor der Bereitstellung. Entfernen Sie Kommentare mit sensiblen Informationen.
Häufige Konsequenzen
| Auswirkung | Details |
|---|---|
| Vertraulichkeit | Umfang: Informationsoffenlegung Service-Struktur und Methoden werden offenbart. |
| Zugriffskontrolle | Umfang: Angriffsfläche Versteckte Methoden werden für Angreifer zugänglich. |
| Sicherheit | Umfang: Aufklärung Detaillierte Informationen unterstützen weitere Angriffe. |
Beispielcode + Lösungscode
Anfälliger Code
<!-- ANFÄLLIG: WSDL legt sensible Informationen offen -->
<?xml version="1.0" encoding="UTF-8"?>
<wsdl:definitions xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:tns="http://internal.company.com/services"
name="UserManagementService"
targetNamespace="http://internal.company.com/services">
<!-- ANFÄLLIG: Legt internen Endpunkt offen -->
<wsdl:service name="UserManagementService">
<wsdl:port name="UserManagementPort" binding="tns:UserManagementBinding">
<!-- Interne URL exponiert! -->
<soap:address location="http://internal-server.company.local:8080/ws/users"/>
</wsdl:port>
</wsdl:service>
<!-- ANFÄLLIG: Legt Admin-Methoden offen -->
<wsdl:portType name="UserManagementPortType">
<wsdl:operation name="getUser">
<wsdl:input message="tns:getUserRequest"/>
<wsdl:output message="tns:getUserResponse"/>
</wsdl:operation>
<!-- Admin-Funktionen exponiert! -->
<wsdl:operation name="deleteAllUsers">
<wsdl:input message="tns:deleteAllUsersRequest"/>
<wsdl:output message="tns:deleteAllUsersResponse"/>
</wsdl:operation>
<wsdl:operation name="createAdminUser">
<wsdl:input message="tns:createAdminUserRequest"/>
<wsdl:output message="tns:createAdminUserResponse"/>
</wsdl:operation>
<!-- Debug-Methode exponiert! -->
<wsdl:operation name="debugDumpDatabase">
<wsdl:input message="tns:debugRequest"/>
<wsdl:output message="tns:debugResponse"/>
</wsdl:operation>
</wsdl:portType>
<!-- ANFÄLLIG: Legt Datenstruktur-Details offen -->
<wsdl:types>
<xs:schema>
<xs:complexType name="User">
<!-- Offenbart Datenbank-Spaltennamen -->
<xs:element name="user_id" type="xs:int"/>
<xs:element name="password_hash" type="xs:string"/> <!-- Offenbart dass dieses Feld existiert -->
<xs:element name="social_security_number" type="xs:string"/> <!-- PII-Feld exponiert -->
<xs:element name="credit_card_number" type="xs:string"/> <!-- Zahlungsdaten exponiert -->
<xs:element name="internal_role_code" type="xs:int"/> <!-- Internes Enum exponiert -->
</xs:complexType>
</xs:schema>
</wsdl:types>
<!-- ANFÄLLIG: Kommentare mit sensiblen Infos -->
<!-- Standard-Admin-Passwort: admin123 -->
<!-- Datenbank: mysql://root:[email protected]/users -->
<!-- API-Schlüssel für Tests: sk_test_1234567890abcdef -->
</wsdl:definitions>
// ANFÄLLIG: Auto-generierte WSDL-Exponierung
@WebService(
name = "UserService",
serviceName = "UserManagementService",
targetNamespace = "http://internal.company.com/services"
)
public class VulnerableUserService {
// Öffentliche Methode - OK
@WebMethod
public User getUser(int userId) {
return userRepository.findById(userId);
}
// ANFÄLLIG: Admin-Methode in WSDL exponiert
@WebMethod
public void deleteAllUsers(String adminPassword) {
// Diese Methode erscheint in öffentlicher WSDL!
if (adminPassword.equals("secret123")) {
userRepository.deleteAll();
}
}
// ANFÄLLIG: Debug-Methode exponiert
@WebMethod
public String debugDatabaseDump() {
// Sichtbar in WSDL!
return databaseService.dumpAllTables();
}
// ANFÄLLIG: Interne Methode exponiert
@WebMethod
public void internalProcessPayroll() {
// Sollte nur intern sein!
payrollService.process();
}
}
// ANFÄLLIG: JAX-WS Konfiguration exponiert WSDL
// web.xml erlaubt nicht-authentifizierten Zugriff auf WSDL
/*
<servlet>
<servlet-name>UserService</servlet-name>
<servlet-class>com.sun.xml.ws.transport.http.servlet.WSServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>UserService</servlet-name>
<url-pattern>/services/users</url-pattern>
</servlet-mapping>
<!-- Keine Sicherheitsbeschränkung - WSDL zugänglich bei /services/users?wsdl -->
*/
// ANFÄLLIG: .NET WCF-Service mit exponierter WSDL
[ServiceContract]
public interface IUserService
{
[OperationContract]
User GetUser(int userId);
// ANFÄLLIG: Admin-Operationen im öffentlichen Vertrag
[OperationContract]
void DeleteAllUsers(string adminKey);
[OperationContract]
string GetDatabaseConnectionString();
[OperationContract]
void ExecuteArbitrarySQL(string sql); // Sehr gefährlich!
}
// ANFÄLLIG: WCF-Konfiguration aktiviert Metadaten
/*
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior>
<!-- ANFÄLLIG: Exponiert WSDL bei ?wsdl -->
<serviceMetadata httpGetEnabled="true"/>
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
*/
# ANFÄLLIG: Python SOAP-Service mit exponierten Methoden
from spyne import Application, rpc, ServiceBase, Unicode, Integer
from spyne.protocol.soap import Soap11
from spyne.server.wsgi import WsgiApplication
class VulnerableUserService(ServiceBase):
@rpc(Integer, _returns=Unicode)
def get_user(ctx, user_id):
return get_user_from_db(user_id)
# ANFÄLLIG: Admin-Methode im öffentlichen Service
@rpc(Unicode, _returns=Unicode)
def delete_all_data(ctx, admin_password):
if admin_password == "secret":
db.delete_all()
return "Alle Daten gelöscht"
# ANFÄLLIG: Debug-Methode exponiert
@rpc(_returns=Unicode)
def debug_dump_config(ctx):
# Gibt sensible Konfiguration zurück
return str(app.config)
# ANFÄLLIG: Interne Methode exponiert
@rpc(Unicode, Unicode, _returns=Unicode)
def internal_execute_sql(ctx, query, password):
# Beliebige SQL-Ausführung!
return db.execute(query)
# WSDL automatisch generiert und exponiert
app = Application([VulnerableUserService],
tns='http://internal.company.com/services',
in_protocol=Soap11(),
out_protocol=Soap11())
# ANFÄLLIG: WSDL verfügbar unter /services?wsdl
Korrigierter Code
<!-- SICHER: Eingeschränkte WSDL für externe Nutzung -->
<?xml version="1.0" encoding="UTF-8"?>
<wsdl:definitions xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:tns="http://api.company.com/services"
name="UserService"
targetNamespace="http://api.company.com/services">
<!-- SICHER: Nur öffentlicher Endpunkt exponiert -->
<wsdl:service name="UserService">
<wsdl:port name="UserPort" binding="tns:UserBinding">
<soap:address location="https://api.company.com/v1/users"/>
</wsdl:port>
</wsdl:service>
<!-- SICHER: Nur öffentliche Methoden -->
<wsdl:portType name="UserPortType">
<wsdl:operation name="getPublicProfile">
<wsdl:input message="tns:getProfileRequest"/>
<wsdl:output message="tns:getProfileResponse"/>
</wsdl:operation>
<wsdl:operation name="updateProfile">
<wsdl:input message="tns:updateProfileRequest"/>
<wsdl:output message="tns:updateProfileResponse"/>
</wsdl:operation>
<!-- Keine Admin/Debug-Methoden -->
</wsdl:portType>
<!-- SICHER: Begrenzte Datenexponierung -->
<wsdl:types>
<xs:schema>
<xs:complexType name="PublicProfile">
<!-- Nur öffentliche Felder -->
<xs:element name="displayName" type="xs:string"/>
<xs:element name="bio" type="xs:string"/>
<xs:element name="avatarUrl" type="xs:string"/>
<!-- Keine internen Felder, keine PII -->
</xs:complexType>
</xs:schema>
</wsdl:types>
<!-- SICHER: Keine sensiblen Kommentare -->
</wsdl:definitions>
// SICHER: Separate öffentliche und interne Services
@WebService(name = "PublicUserService")
public class PublicUserService {
// Nur öffentliche Methoden exponiert
@WebMethod
public PublicProfile getProfile(String userId) {
User user = userRepository.findById(userId);
return user.toPublicProfile(); // Filtere sensible Daten
}
@WebMethod
public boolean updateProfile(String userId, PublicProfile profile) {
// Validiere und aktualisiere nur erlaubte Felder
return userService.updatePublicProfile(userId, profile);
}
}
// SICHER: Interner Service auf separatem Endpunkt
@WebService(name = "InternalAdminService")
@Authenticated // Erfordert Authentifizierung
@RolesAllowed("ADMIN")
public class InternalAdminService {
@WebMethod
public void deleteUser(String adminToken, String userId) {
if (!authService.validateAdminToken(adminToken)) {
throw new SecurityException("Ungültiges Admin-Token");
}
userRepository.delete(userId);
}
}
// SICHER: Auto-WSDL in Produktion deaktivieren
/*
<!-- web.xml für Produktion -->
<context-param>
<param-name>com.sun.xml.ws.transport.http.HttpAdapter.publishWsdl</param-name>
<param-value>false</param-value> <!-- WSDL-Generierung deaktivieren -->
</context-param>
<!-- Oder Authentifizierung für WSDL erforderlich -->
<security-constraint>
<web-resource-collection>
<web-resource-name>WSDL Access</web-resource-name>
<url-pattern>/services/*</url-pattern>
</web-resource-collection>
<auth-constraint>
<role-name>developer</role-name>
</auth-constraint>
</security-constraint>
*/
// SICHER: .NET WCF mit eingeschränkten Metadaten
[ServiceContract]
public interface IPublicUserService
{
// Nur öffentliche Operationen
[OperationContract]
PublicProfile GetProfile(string userId);
[OperationContract]
bool UpdateProfile(string userId, PublicProfile profile);
}
// SICHER: Separater interner Service
[ServiceContract]
internal interface IInternalAdminService
{
[OperationContract]
void DeleteUser(string adminToken, string userId);
}
// SICHER: WCF-Konfiguration - Metadaten in Produktion deaktivieren
/*
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior>
<!-- SICHER: Öffentliche Metadaten in Produktion deaktivieren -->
<serviceMetadata httpGetEnabled="false" httpsGetEnabled="false"/>
<!-- Oder auf bestimmten Endpunkt mit Auth beschränken -->
<serviceMetadata>
<serviceMetadataEndpoint
binding="wsHttpBinding"
bindingConfiguration="secureMetadataBinding"/>
</serviceMetadata>
</behavior>
</serviceBehaviors>
</behaviors>
<bindings>
<wsHttpBinding>
<binding name="secureMetadataBinding">
<security mode="Transport">
<transport clientCredentialType="Certificate"/>
</security>
</binding>
</wsHttpBinding>
</bindings>
</system.serviceModel>
*/
# SICHER: Python mit eingeschränkter WSDL
from spyne import Application, rpc, ServiceBase, Unicode
from spyne.protocol.soap import Soap11
from spyne.server.wsgi import WsgiApplication
# SICHER: Öffentlicher Service nur mit erlaubten Methoden
class PublicUserService(ServiceBase):
@rpc(Unicode, _returns=Unicode)
def get_public_profile(ctx, user_id):
user = get_user_from_db(user_id)
return user.to_public_json() # Nur öffentliche Felder
@rpc(Unicode, Unicode, _returns=Unicode)
def update_profile(ctx, user_id, profile_json):
return update_user_profile(user_id, profile_json)
# Keine Admin/Debug-Methoden im öffentlichen Service
# SICHER: Separater interner Admin-Service
class InternalAdminService(ServiceBase):
@rpc(Unicode, Unicode, _returns=Unicode)
def delete_user(ctx, admin_token, user_id):
if not validate_admin_token(admin_token):
raise PermissionError("Ungültiges Admin-Token")
return delete_user(user_id)
# SICHER: Nur öffentlichen Service extern exponieren
public_app = Application(
[PublicUserService],
tns='https://api.company.com/public',
in_protocol=Soap11(),
out_protocol=Soap11()
)
# SICHER: Benutzerdefinierter WSDL-Handler mit Authentifizierung
class SecureWSDLMiddleware:
def __init__(self, app):
self.app = app
def __call__(self, environ, start_response):
# Prüfe ob WSDL angefordert wird
if environ.get('QUERY_STRING') == 'wsdl':
# Erfordere Authentifizierung für WSDL-Zugriff
if not self.is_authenticated(environ):
start_response('401 Unauthorized', [])
return [b'Authentifizierung für WSDL-Zugriff erforderlich']
return self.app(environ, start_response)
def is_authenticated(self, environ):
# Implementiere Authentifizierungsprüfung
auth_header = environ.get('HTTP_AUTHORIZATION')
return validate_auth(auth_header)
wsgi_app = SecureWSDLMiddleware(WsgiApplication(public_app))
Ausgenutzt in der Praxis
API-Entdeckung
WSDLs werden verwendet um versteckte administrative APIs zu entdecken.
SQL-Injection
Methodensignaturen aus WSDLs unterstützten Injection-Angriffe.
Datenleck
Interne Datenstrukturen durch WSDLs offenbart.
Tools zum Testen/Ausnutzen
-
SoapUI - SOAP-Tests und -Entdeckung.
-
WS-Attacker - Webservice-Angriffe.
-
Nmap NSE-Skripte für WSDL-Entdeckung.
CVE-Beispiele
-
CVEs von exponierten internen Webservices.
-
Informationsoffenlegung über WSDL.
Referenzen
-
MITRE. "CWE-651: Exposure of WSDL File Containing Sensitive Information." https://cwe.mitre.org/data/definitions/651.html
-
OWASP. "Web Services Security Cheat Sheet."