Skip to content

Security: TeloraVapi/TeloraBackend

Security

SECURITY.md

Security Implementation

Token Encryption

This application implements secure encryption for storing sensitive access tokens (particularly Shopify access tokens) in the database.

How It Works

  1. Encryption Algorithm: Uses Fernet symmetric encryption from the cryptography library
  2. Key Derivation: PBKDF2-HMAC-SHA256 with 100,000 iterations
  3. Base64 Encoding: Encrypted tokens are base64 encoded for safe storage

Configuration

Add the following to your .env file:

# Required - used for JWT and fallback encryption
SECRET_KEY=your-very-secure-secret-key-here

# Optional - dedicated encryption key (recommended for production)
ENCRYPTION_KEY=your-dedicated-encryption-key-for-tokens

Important:

  • In production, use a strong, randomly generated ENCRYPTION_KEY
  • If ENCRYPTION_KEY is not set, the system will fall back to using SECRET_KEY
  • Never commit these keys to version control

Usage

Automatic Encryption in Shopify Service

# The ShopifyService automatically handles encryption
shopify_service = ShopifyService()

# Encrypt token before storing
encrypted_token = shopify_service.encrypt_token(access_token)

# Decrypt token when needed for API calls
decrypted_token = shopify_service.decrypt_token(encrypted_token)

# Get decrypted token from database
decrypted_token = shopify_service.get_decrypted_token(db, shop_domain)

Direct Usage

from app.core.security import encrypt_token, decrypt_token

# Encrypt a token
encrypted = encrypt_token("shpat_1234567890abcdef...")

# Decrypt a token  
original = decrypt_token(encrypted)

Making API Calls with Encrypted Tokens

# Use stored encrypted tokens for API calls
shop_info = await shopify_service.get_shop_info_with_stored_token(db, shop_domain)

# Generic API call with stored token
response = await shopify_service.make_api_call(
    db=db,
    shop_domain="myshop.myshopify.com", 
    endpoint="products.json",
    method="GET"
)

Database Schema

The access_token field in the shopify_stores table uses the Text type to accommodate encrypted tokens, which are longer than plain tokens:

  • Plain token: ~38 characters
  • Encrypted token: ~188 characters (base64 encoded)

Migration Notes

For existing installations with plain text tokens:

  1. Backup your database first
  2. Create a migration script to encrypt existing tokens
  3. Test thoroughly in a staging environment

Example migration pseudocode:

# Migrate existing plain text tokens
for store in db.query(ShopifyStore).all():
    if not is_encrypted(store.access_token):  # Your validation logic
        store.access_token = encrypt_token(store.access_token)
        db.commit()

Security Benefits

  1. Data at Rest Protection: Tokens are encrypted in the database
  2. Key Rotation: Can implement key rotation by re-encrypting with new keys
  3. Breach Mitigation: Even if database is compromised, tokens are protected
  4. Compliance: Helps meet security standards for sensitive data storage

Performance Considerations

  • Encryption overhead: Minimal impact on application performance
  • Token length: Encrypted tokens are ~5x longer than plain text
  • Memory usage: Slightly higher due to encryption operations

Best Practices

  1. Use a dedicated ENCRYPTION_KEY in production
  2. Rotate encryption keys periodically
  3. Monitor token usage and implement proper logging
  4. Regular security audits of encryption implementation
  5. Backup strategies should account for encrypted data

Troubleshooting

Common Issues

  1. ImportError with cryptography: Install required dependencies

    pip install cryptography
  2. Key derivation errors: Ensure SECRET_KEY or ENCRYPTION_KEY is set

  3. Decryption failures: Usually indicates:

    • Wrong encryption key
    • Corrupted encrypted data
    • Token was not properly encrypted

Logging

The application logs encryption/decryption operations. Check logs for:

  • Failed decryption attempts
  • Key derivation issues
  • Token validation errors

There aren’t any published security advisories