Django HIPAA Compliance Guide: Step-by-Step Best Practices and Developer Checklist

Product Pricing
Ready to get started? Book a demo with our team
Talk to an expert

Django HIPAA Compliance Guide: Step-by-Step Best Practices and Developer Checklist

Kevin Henry

HIPAA

July 16, 2025

5 minutes read
Share this article
Django HIPAA Compliance Guide: Step-by-Step Best Practices and Developer Checklist

This Django HIPAA compliance guide distills technical safeguards into clear, developer-ready steps you can implement today. You will move from encryption and transport security to access control, auditability, resilient sessions, backups, and ongoing risk management—ending with a concise developer checklist for each area.

The focus is practical: implementable settings, code patterns, and operational guardrails that align with HIPAA’s technical requirements while fitting real-world Django projects.

Implementing Encryption at Rest

Encrypt every place protected health information (PHI) resides: databases, file storage, caches, logs, and backups. Combine infrastructure encryption with application-layer controls for fields that demand stronger isolation and key rotation.

Storage layers to cover

  • Volume/disk: enable managed volume encryption (for example, AES-256 encryption) for database and media disks.
  • Database: prefer native transparent data encryption (TDE) where available; otherwise, encrypt sensitive columns at the application layer.
  • Files and object storage: store media on encrypted buckets/volumes; avoid placing PHI in world-readable paths or URLs.
  • Caches and message queues: enable at-rest encryption and strict access policies; never store long-lived PHI in ephemeral caches.

Application-layer field encryption

For high-sensitivity fields, encrypt before writing to the database. Use authenticated encryption and keep keys outside the codebase with envelope encryption and rotation.

# example: AES-256-GCM for a sensitive field
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
import os, base64

def encrypt_value(plaintext: str, key_32_bytes: bytes) -> str:
    aes = AESGCM(key_32_bytes)  # 32 bytes => AES-256
    nonce = os.urandom(12)
    ct = aes.encrypt(nonce, plaintext.encode(), None)
    return base64.b64encode(nonce + ct).decode()

def decrypt_value(token: str, key_32_bytes: bytes) -> str:
    raw = base64.b64decode(token)
    nonce, ct = raw[:12], raw[12:]
    return AESGCM(key_32_bytes).decrypt(nonce, ct, None).decode()

Key management essentials

  • Use a KMS/HSM for data keys; never hardcode secrets. Rotate keys and store key IDs alongside ciphertext.
  • Separate duties: restrict who can view keys vs. who can deploy code. Audit every key operation.
  • Document key rotation and recovery, including emergency access procedures.

Developer checklist

  • Enable disk/volume and database encryption; verify with test restores.
  • Encrypt selected fields at the application layer using authenticated AES-256 encryption modes.
  • Externalize and rotate keys; log and review key usage.
  • Ensure caches, queues, and logs with PHI are encrypted at rest.

Enforcing Encryption in Transit

Protect data on every hop—browser to load balancer, load balancer to app, and app to data stores—using modern TLS. Enforce TLS 1.2+ and prefer TLS 1.3 where supported.

Ready to simplify HIPAA compliance?

Join thousands of organizations that trust Accountable to manage their compliance needs.

Django security settings

# settings.py
SECURE_SSL_REDIRECT = True
SECURE_HSTS_SECONDS = 31536000
SECURE_HSTS_INCLUDE_SUBDOMAINS = True
SECURE_HSTS_PRELOAD = True
SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SECURE = True
SECURE_REFERRER_POLICY = "same-origin"
SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https")

Web server and proxy

# example (nginx)
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;

Backend connections

  • Databases: require TLS on connections (e.g., sslmode=require for PostgreSQL).
  • Caches/message brokers: use TLS endpoints and authenticated clients.
  • Internal services: mutually authenticate where feasible to prevent impersonation.

Developer checklist

  • Force HTTPS with HSTS and redirect rules; disable plain HTTP.
  • Allow only TLS 1.2+; renew and monitor certificates automatically.
  • Require TLS on database, cache, and queue connections.

Establishing Access Controls

Implement role-based access control (RBAC) aligned to least privilege. Map roles to Django groups and permissions, and add object-level checks where PHI isolation is required.

Roles, groups, and permissions

# create roles and map to permissions
from django.contrib.auth.models import Group, Permission

clinician = Group.objects.create(name="Clinician")
view_perm = Permission.objects.get(codename="view_record")
edit_perm = Permission.objects.get(codename="change_record")
clinician.permissions.add(view_perm, edit_perm)

Gate views with decorators or mixins and filter querysets to a user’s scope.

@login_required
@permission_required("app.view_record", raise_exception=True)
def record_detail(request, pk):
    qs = Record.objects.filter(organization=request.user.organization)
    obj = get_object_or_404(qs, pk=pk)
    ...

Strengthening identity

  • Require MFA via your identity provider; enforce step-up reauthentication for high-risk actions.
  • Harden Django admin: IP restrict, MFA via SSO, and limit who is_staff; keep PHI out of the admin if possible.
  • Segregate duties between clinical users, support, and administrators.

Developer checklist

  • Define RBAC roles and map to groups/permissions; document access matrices.
  • Apply object-level filtering and per-request permission checks.
  • Enforce MFA and harden administrative surfaces.

Enabling Audit Logging

Log read and write access to PHI with enough context to answer who did what, when, from where, and why. Store logs in append-only, durable storage and implement tamper-evident logging.

What to capture

  • User and subject identifiers, event type (read/create/update/delete), resource, outcome, timestamp, client IP/agent, and justification where applicable.
  • Never log raw PHI unless required; prefer stable IDs or redacted values.

Django logging configuration

# settings.py (excerpt)
LOGGING = {
  "version": 1,
  "disable_existing_loggers": False,
  "formatters": {
    "json": {"format": "{{\"ts\":\"%(asctime)s\",\"lvl\":\"%(levelname)s\",\"logger\":\"%(name)s\","
                       "\"user\":\"%(user)s\",\"ip\":\"%(clientip)s\",\"msg\":\"%(message)s\"}}"}
  },
  "handlers": {
    "audit": {"class": "logging.handlers.WatchedFileHandler",
              "filename": "/var/log/django/audit.log", "formatter": "json"}
  },
  "loggers": {"audit": {"handlers": ["audit"], "level": "INFO", "propagate": False}}
}

Tamper-evident chaining

Hash-chain entries with a secret to detect alteration. Periodically anchor the head hash in a separate system.

import hmac, hashlib, json, time, os

class Chain:
    def __init__(self, secret: bytes):
        self.prev = b"\x00"*32
        self.secret = secret

    def record(self, event: dict) -> dict:
        payload = json.dumps({"ts": int(time.time()), **event}, separators=(",",":")).encode()
        digest = hmac.new(self.secret, self.prev + payload, hashlib.sha256).hexdigest()
        self.prev = bytes.fromhex(digest)
        return {"h": digest, **event}

Developer checklist

  • Log all PHI access with minimal necessary detail; avoid raw PHI.
  • Ship logs off-host to append-only storage and implement tamper-evident logging.
  • Define retention, review schedules, and alerting for anomalous patterns.

Securing User Sessions

Use server-side sessions, hardened cookies, and explicit session timeout policies. Rotate identifiers on login and privilege changes to prevent fixation.

Session engine and cookies

# settings.py
SESSION_ENGINE = "django.contrib.sessions.backends.cached_db"  # server-side
SESSION_COOKIE_SECURE = True
SESSION_COOKIE_HTTPONLY = True
SESSION_COOKIE_SAMESITE = "Lax"   # use "Strict" if it doesn't break your flows
CSRF_COOKIE_SECURE = True

Timeouts and lifecycle

# example: 30-minute idle timeout and absolute 8-hour limit
SESSION_COOKIE_AGE = 1800           # seconds
SESSION_EXPIRE_AT_BROWSER_CLOSE = True
Share this article

Ready to simplify HIPAA compliance?

Join thousands of organizations that trust Accountable to manage their compliance needs.

Related Articles