How can I securely encrypt spatial fields (GeoDjango / PostGIS) in Django?
I’m working on a Django project with GeoDjango models that store user location data (e.g., PointField, LineStringField). Because location data is highly sensitive, I want to ensure it’s secured (?encrypted) at rest in the database.
The challenge is that most Django field encryption libraries (like django-cryptography) work well for standard CharField or TextField, but don’t appear to support spatial fields directly.
My requirements are:
I don’t need to run spatial queries in PostGIS (like ST_Contains, ST_Distance, etc., although it would be a bonus if I could maintain this functionality of GeoDjango) — I can handle geometry operations in Python (Shapely/GEOS) after decryption.
I do want the raw data encrypted in the database so that DB admins can’t see exact coordinates.
Ideally, I’d like to keep using Django’s model field API so that saving/retrieving encrypted geometries feels natural.
Has anyone implemented a secure way to encrypt GeoDjango fields?
Any examples or best practices would be greatly appreciated!
To my knowledge, there's no on-the-shelf solution for encryption-at-rest with PostGIS
. So most likely, you won't be able to preserve GIS-operations if you want encryption.
If that much is fine, then the rest of the question becomes easier - because now you can use two FloatField
instead and keep those encrypted. To preserve a mostly GIS-like API contract, our old OOP friends to the rescue - getters and setters.
from django.contrib.gis.geos import Point
class SomeModel:
lat = encrypt(models.FloatField())
lon = encrypt(models.FloatField())
@property
def location(self):
return Point(self.lon, self.lat)
@location.setter
def location(self, point: Point):
self.lon = point.x
self.lat = point.y
Aside from not being able to do GIS transformations/comparisons at the queryset level, consumers won't know the difference between this and a model where location
is a PointField
.
One caveat is that this doesn't work out of the box in the admin panel, you'd have to create a custom form to handle interfacing with the getter and setter.