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.

Вернуться на верх