AOSSIE-Org / AOSSIE-Org/PictoPy

BUG:Wildcard CORS + credentials + no auth allows any webpage to read/delete the photo library

Abierto
#1,360 4 comentarios 0 reacciones 0 asignados Ver en GitHub
Lenguaje dominante
Python
Estrellas
283
Forks
679
Merge medio
7 d 2 h
PR fusionados (30 d)
3

Descripción

### Is there an existing issue for this?

- [x] I have searched the existing issues

### What happened?

### Summary
Both local FastAPI servers (`backend` on `:52123` and `sync-microservice` on `:52124`) are configured with:

```python
allow_origins=["*"]
allow_credentials=True
allow_methods=["*"]
allow_headers=["*"]
```

This combination means the browser will expose credentialed cross-origin responses to *any* origin. Since there's no authentication on top of it, any webpage the user has open in a normal browser tab — while PictoPy is running in the background — can talk directly to these servers.

### Affected files
- `backend/main.py:124-130`
- `sync-microservice/main.py:36-42`

### Impact
With the app simply running, a malicious or compromised webpage can, without any user interaction:
- Enumerate the entire photo library via `GET /images/`, including file paths, GPS/EXIF data, and timestamps
- Delete folders and cascade-delete images/faces/albums via `DELETE /folders/delete-folders`
- Delete ML models via `DELETE /models/facenet`
- Shut down the backend via `POST /shutdown`
- Leak absolute local folder paths (OS username, directory layout) via the sync-microservice's status endpoints
- Disrupt sync or kill the sync-microservice via unauthenticated POSTs (`/watcher/stop`, `/shutdown`)

This also compounds other findings — e.g. the unrestricted `folder_path` in `POST /folders/add-folder` becomes remotely triggerable once this is fixed to require auth, it's no longer exploitable from an arbitrary webpage.

### Steps to reproduce
1. Run PictoPy normally.
2. In a separate, unrelated browser tab, open a page containing:
```js
fetch('http://localhost:52123/images/').then(r => r.json()).then(console.log)
```
3. Observe the full image index (including GPS data) returned to a page with no relation to PictoPy.

### Proposed fix
1. **Restrict CORS to the actual app origin(s)** instead of `*`:
```python
allow_origins=["tauri://localhost", "http://localhost:1420"]
```
2. **Drop `allow_credentials=True`** unless cookies/sessions are actually required — this alone prevents cross-origin scripts from reading responses even if an origin match occurs.
3. **Add a local shared-secret token**, generated at process startup, written to a file only the Tauri process can read, and required on every request via a header (e.g. `X-PictoPy-Token`). This protects against DNS-rebinding-style attacks that could still spoof an allowed origin.
4. Apply the same fix to `sync-microservice/main.py`.

### Acceptance criteria
- [ ] `allow_origins` set to an explicit list, not `*`
- [ ] `allow_credentials` set to `False` unless a concrete need is documented
- [ ] All state-changing and data-reading endpoints require a valid local auth token
- [ ] Token is generated per-process-start and stored with restrictive file permissions (`0600`)
- [ ] Fix applied to both `backend` and `sync-microservice`
- [ ] Manual test: a `fetch()` from an arbitrary origin (not the app's own) is rejected with `401`

### Record

- [x] I agree to follow this project's Code of Conduct

Guía de contribución

Abrir la guía de contribución

Evaluación

Este issue todavía no se ha evaluado.

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.