Security fix: Implement Google Maps API photo proxy (Fix 3)
Completed HIGH severity security fix (CVSS 6.5) to prevent Google Maps API key exposure to frontend clients. Issue: API key was embedded in photo URLs sent to frontend, allowing potential abuse and quota exhaustion. Solution: Implemented backend proxy endpoint for photos. Backend Changes: - google-maps.client.ts: Changed photoUrl to photoReference, added fetchPhoto() - stations.types.ts: Updated type definition (photoUrl → photoReference) - stations.controller.ts: Added getStationPhoto() proxy method - stations.routes.ts: Added GET /api/stations/photo/:reference route - stations.service.ts: Updated to use photoReference - stations.repository.ts: Updated database queries and mappings - admin controllers/services: Updated for consistency - Created migration 003 to rename photo_url column Frontend Changes: - stations.types.ts: Updated type definition (photoUrl → photoReference) - photo-utils.ts: NEW - Helper to generate proxy URLs - StationCard.tsx: Use photoReference with helper function Tests & Docs: - Updated mock data to use photoReference - Updated test expectations for proxy URLs - Updated API.md and TESTING.md documentation Database Migration: - 003_rename_photo_url_to_photo_reference.sql: Renames column in station_cache Security Benefits: - API key never sent to frontend - All photo requests proxied through authenticated endpoint - Photos cached for 24 hours (Cache-Control header) - No client-side API key exposure Files modified: 16 files New files: 2 (photo-utils.ts, migration 003) Status: All 3 P0 security fixes now complete - Fix 1: crypto.randomBytes() ✓ - Fix 2: Magic byte validation ✓ - Fix 3: API key proxy ✓ Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -62,7 +62,7 @@ Search for gas stations near a location using Google Maps Places API.
|
||||
"latitude": 37.7750,
|
||||
"longitude": -122.4195,
|
||||
"rating": 4.2,
|
||||
"photoUrl": "https://maps.googleapis.com/maps/api/place/photo?...",
|
||||
"photoReference": "/api/stations/photo/{reference}",
|
||||
"distance": 150
|
||||
}
|
||||
],
|
||||
@@ -85,7 +85,7 @@ Search for gas stations near a location using Google Maps Places API.
|
||||
| stations[].latitude | number | Station latitude |
|
||||
| stations[].longitude | number | Station longitude |
|
||||
| stations[].rating | number | Google rating (0-5) |
|
||||
| stations[].photoUrl | string | Photo URL (nullable) |
|
||||
| stations[].photoReference | string | Photo URL (nullable) |
|
||||
| stations[].distance | number | Distance from search location (meters) |
|
||||
| searchLocation | object | Original search coordinates |
|
||||
| searchRadius | number | Actual search radius used |
|
||||
@@ -187,7 +187,7 @@ Save a station to user's favorites with optional metadata.
|
||||
"latitude": 37.7750,
|
||||
"longitude": -122.4195,
|
||||
"rating": 4.2,
|
||||
"photoUrl": "https://maps.googleapis.com/maps/api/place/photo?..."
|
||||
"photoReference": "/api/stations/photo/{reference}"
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -286,7 +286,7 @@ Retrieve all stations saved by the authenticated user.
|
||||
"latitude": 37.7750,
|
||||
"longitude": -122.4195,
|
||||
"rating": 4.2,
|
||||
"photoUrl": "https://maps.googleapis.com/maps/api/place/photo?..."
|
||||
"photoReference": "/api/stations/photo/{reference}"
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -355,7 +355,7 @@ Retrieve a specific saved station by Google Place ID.
|
||||
"latitude": 37.7750,
|
||||
"longitude": -122.4195,
|
||||
"rating": 4.2,
|
||||
"photoUrl": "https://maps.googleapis.com/maps/api/place/photo?..."
|
||||
"photoReference": "/api/stations/photo/{reference}"
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -437,7 +437,7 @@ Update metadata for a saved station.
|
||||
"latitude": 37.7750,
|
||||
"longitude": -122.4195,
|
||||
"rating": 4.2,
|
||||
"photoUrl": "https://maps.googleapis.com/maps/api/place/photo?..."
|
||||
"photoReference": "/api/stations/photo/{reference}"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
@@ -471,7 +471,7 @@ export const mockStations = [
|
||||
latitude: 37.7750,
|
||||
longitude: -122.4195,
|
||||
rating: 4.2,
|
||||
photoUrl: 'https://example.com/photo1.jpg',
|
||||
photoReference: 'mock-photo-reference-1',
|
||||
distance: 150
|
||||
},
|
||||
{
|
||||
@@ -481,7 +481,7 @@ export const mockStations = [
|
||||
latitude: 37.7755,
|
||||
longitude: -122.4190,
|
||||
rating: 4.0,
|
||||
photoUrl: null,
|
||||
photoReference: null,
|
||||
distance: 300
|
||||
}
|
||||
];
|
||||
|
||||
Reference in New Issue
Block a user