Unzureichende Autorisierung von Index mit sensiblen Informationen
Beschreibung
Unzureichende Autorisierung von Index mit sensiblen Informationen tritt auf, wenn eine Anwendung Suchindizes, Datenbankindizes oder Caching-Strukturen erstellt oder exponiert, die sensible Daten ohne ordnungsgemäße Zugriffskontrollen enthalten. Diese Indizes aggregieren oft Daten aus mehreren Quellen und exponieren potenziell Informationen, die einzelne Datenquellen schützen würden. Suchergebnisse können Datenexistenz oder -inhalt an unbefugte Benutzer offenlegen.
Risiko
Suchindizes können sensible Daten exponieren, die zugrunde liegende Systeme schützen. Autocomplete-Features können Informationen über Daten anderer Benutzer leaken. Indexstrukturen offenbaren Datenmuster und Beziehungen. Cache-Indizes können veraltete sensible Daten an unbefugte Benutzer zurückgeben. Volltextsuche über verschlüsselte Daten kann Klartext offenlegen. Aggregierte Indizes umgehen Row-Level-Security der Quelldaten.
Lösung
Wenden Sie dieselben Zugriffskontrollen auf Indizes wie auf Quelldaten an. Implementieren Sie Autorisierung zur Index-Abfragezeit. Erwägen Sie separate Indizes pro Autorisierungsebene. Bereinigen Sie sensible Daten vor der Indizierung. Implementieren Sie ordnungsgemäße Index-Level-Zugriffskontrollen. Überprüfen Sie Autocomplete-/Vorschlagsfeatures auf Datenlecks. Wenden Sie Field-Level-Security auf Suchergebnisse an.
Häufige Auswirkungen
| Auswirkung | Details |
|---|---|
| Vertraulichkeit | Bereich: Informationsoffenlegung Sensible Daten durch Such-/Index-Abfragen exponiert. |
| Datenschutz | Bereich: Personenbezogene Daten-Exposure In Indizes aggregierte PII werden zugänglich. |
| Autorisierung | Bereich: Bypass Indexzugriff umgeht Quelldaten-Zugriffskontrollen. |
Beispielcode + Lösungscode
Verwundbarer Code
// VERWUNDBAR: Suchindex ohne Zugriffskontrollen
@Service
public class VulnerableSearchService {
@Autowired
private SearchIndex globalIndex;
public List<Document> search(String query) {
// Kein Benutzerkontext - jeder kann alles suchen!
return globalIndex.search(query);
}
// Index enthält alle Dokumente unabhängig von Berechtigungen
public void indexDocument(Document doc) {
globalIndex.add(doc); // Keine Zugriffsebene gespeichert
}
}
// VERWUNDBAR: Autocomplete exponiert alle Benutzerdaten
@RestController
public class VulnerableAutocompleteController {
@Autowired
private UserRepository userRepository;
@GetMapping("/api/autocomplete/users")
public List<String> autocompleteUsers(@RequestParam String prefix) {
// Gibt ALLE Benutzer zurück, die mit Präfix übereinstimmen!
// Angreifer kann Benutzernamen enumerieren
return userRepository.findUsernamesByPrefix(prefix);
}
}
// VERWUNDBAR: Cache-Index ohne Autorisierung
@Service
public class VulnerableCacheService {
private Map<String, Object> cache = new ConcurrentHashMap<>();
public void cacheData(String key, Object data) {
// Keine Benutzerzuordnung
cache.put(key, data);
}
public Object getData(String key) {
// Jeder kann alle gecachten Daten abrufen!
return cache.get(key);
}
}
# VERWUNDBAR: Elasticsearch ohne Field-Level-Security
from elasticsearch import Elasticsearch
es = Elasticsearch()
def search_vulnerable(query):
# Gibt alle übereinstimmenden Dokumente einschließlich sensibler Felder zurück
return es.search(
index="documents",
body={
"query": {
"match": {"content": query}
}
}
)
# Index enthält sensible Daten ohne Zugriffskontrollen
def index_document_vulnerable(doc):
es.index(
index="documents",
body={
"title": doc.title,
"content": doc.content,
"ssn": doc.ssn, # Sensibel!
"salary": doc.salary, # Sensibel!
"owner": doc.owner
}
)
# VERWUNDBAR: Autocomplete auf sensiblen Daten
@app.route('/api/search/suggest')
def suggest_vulnerable():
prefix = request.args.get('q', '')
# Schlägt basierend auf ALLEN indizierten Inhalten vor
results = es.search(
index="documents",
body={
"suggest": {
"content-suggest": {
"prefix": prefix,
"completion": {
"field": "content_suggest"
}
}
}
}
)
return jsonify(results)
// VERWUNDBAR: Client-seitiger Suchindex
const searchIndex = [];
// Alle Daten client-seitig indizieren
function buildIndexVulnerable(allDocuments) {
allDocuments.forEach(doc => {
// Alle Dokumente indiziert unabhängig von Benutzerberechtigungen
searchIndex.push({
id: doc.id,
title: doc.title,
content: doc.content,
owner: doc.owner,
sensitiveData: doc.sensitiveData // Exponiert!
});
});
}
// VERWUNDBAR: Suche gibt alle Treffer zurück
function searchVulnerable(query) {
return searchIndex.filter(doc =>
doc.content.toLowerCase().includes(query.toLowerCase())
);
}
Lösungscode
// SICHER: Suche mit Autorisierungskontext
@Service
public class SafeSearchService {
@Autowired
private SearchIndex index;
@Autowired
private AuthorizationService authService;
public List<Document> search(String query, User currentUser) {
// Filter für Benutzerberechtigungen erstellen
SearchFilter filter = buildPermissionFilter(currentUser);
// Suche mit Autorisierungsfilter
return index.search(query, filter);
}
private SearchFilter buildPermissionFilter(User user) {
SearchFilter filter = new SearchFilter();
// Benutzer kann eigene Dokumente sehen
filter.addCondition("owner", user.getId());
// Mit Benutzer geteilte Dokumente hinzufügen
List<Long> sharedDocIds = getSharedDocumentIds(user);
filter.addCondition("id", "IN", sharedDocIds);
// Admin kann alles sehen
if (user.isAdmin()) {
filter.clearConditions();
}
return filter;
}
// Index mit Zugriffsebenen-Metadaten
public void indexDocument(Document doc) {
IndexEntry entry = new IndexEntry();
entry.setContent(doc.getContent());
entry.setOwnerId(doc.getOwner().getId());
entry.setAccessLevel(doc.getAccessLevel());
entry.setSharedWith(doc.getSharedUserIds());
index.add(entry);
}
}
// SICHER: Autocomplete mit Autorisierung
@RestController
public class SafeAutocompleteController {
@Autowired
private UserService userService;
@GetMapping("/api/autocomplete/users")
public List<String> autocompleteUsers(
@RequestParam String prefix,
@AuthenticationPrincipal User currentUser) {
// Nur Benutzer zurückgeben, die dieser Benutzer sehen kann
return userService.findVisibleUsernamesByPrefix(
prefix, currentUser
);
}
}
// SICHER: Field-Level-Security in Suchergebnissen
@Service
public class SafeFieldFilterService {
public List<DocumentDTO> filterSensitiveFields(
List<Document> documents, User user) {
return documents.stream()
.map(doc -> {
DocumentDTO dto = new DocumentDTO();
dto.setId(doc.getId());
dto.setTitle(doc.getTitle());
// Sensible Felder nur einschließen wenn autorisiert
if (canViewSensitiveData(user, doc)) {
dto.setSalary(doc.getSalary());
dto.setSsn(doc.getSsn());
}
return dto;
})
.collect(Collectors.toList());
}
}
# SICHER: Elasticsearch mit Document-Level-Security
from elasticsearch import Elasticsearch
es = Elasticsearch()
def search_safe(query, current_user):
# Autorisierungsfilter erstellen
auth_filter = build_auth_filter(current_user)
return es.search(
index="documents",
body={
"query": {
"bool": {
"must": {
"match": {"content": query}
},
"filter": auth_filter
}
},
"_source": {
# Sensible Felder aus Ergebnissen ausschließen
"excludes": ["ssn", "raw_password"]
}
}
)
def build_auth_filter(user):
if user.is_admin:
return {"match_all": {}}
return {
"bool": {
"should": [
{"term": {"owner": user.id}},
{"terms": {"shared_with": [user.id]}},
{"term": {"public": True}}
]
}
}
# SICHER: Separate Indizes nach Sicherheitsebene
def index_document_safe(doc, security_level):
# Verschiedene Indizes für verschiedene Zugriffsebenen
index_name = f"documents_{security_level}"
# Sensible Felder nicht indizieren
body = {
"title": doc.title,
"content": doc.content,
"owner": doc.owner,
"access_level": security_level
# SSN und Gehalt NICHT indiziert
}
es.index(index=index_name, body=body)
# SICHER: Autocomplete mit Autorisierung
@app.route('/api/search/suggest')
@require_auth
def suggest_safe():
prefix = request.args.get('q', '')
user = get_current_user()
# Vorschläge nach Autorisierung filtern
auth_filter = build_auth_filter(user)
results = es.search(
index="documents",
body={
"query": {
"bool": {
"filter": auth_filter
}
},
"suggest": {
"content-suggest": {
"prefix": prefix,
"completion": {
"field": "content_suggest",
"contexts": {
"access_level": user.access_levels
}
}
}
}
}
)
return jsonify(filter_sensitive_suggestions(results, user))
// SICHER: Server-seitige Suche mit Autorisierung
async function searchSafe(query) {
// Server wendet Autorisierung an
const response = await fetch('/api/search', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${getToken()}`
},
body: JSON.stringify({ query })
});
// Ergebnisse bereits vom Server gefiltert
return response.json();
}
// SICHER: Server-seitiges Autocomplete
async function autocompleteSafe(prefix) {
const response = await fetch(
`/api/autocomplete?q=${encodeURIComponent(prefix)}`,
{
headers: {
'Authorization': `Bearer ${getToken()}`
}
}
);
// Server gibt nur autorisierte Vorschläge zurück
return response.json();
}
Ausgenutzt in der Praxis
Such-basierte Daten-Exposure
Unternehmens-Suchsysteme exponierten Dokumente, auf die Benutzer keinen Zugriff haben sollten.
Autocomplete-Datenlecks
Autocomplete-Features offenbarten Benutzernamen, E-Mail-Adressen und andere sensible Daten.
Cache-Poisoning/-Lecks
Geteilte Caches gaben sensible Daten anderer Benutzer zurück.
Tools zum Testen/Ausnutzen
- Suchabfrage-Manipulationstools
- Autocomplete-Enumerationsskripte
- Cache-Analysetools
- Elasticsearch Security-Audit-Tools
CVE-Beispiele
- CVEs durch Suchindex-Daten-Exposure
- Autocomplete-Enumerationsschwachstellen
Referenzen
- MITRE. "CWE-612: Improper Authorization of Index Containing Sensitive Information." https://cwe.mitre.org/data/definitions/612.html
- Elasticsearch Security Documentation.