djangosecurityencryptionpostgisgeodjango

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!


Solution

  • 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.