Security¶
This guide covers security best practices when using gntplib.
Authentication¶
Always Use Strong Passwords¶
# ✗ Bad: Weak password
publisher = Publisher('App', events, password='123456')
# ✓ Good: Strong random password
import secrets
password = secrets.token_urlsafe(32)
publisher = Publisher('App', events, password=password)
Use SHA256 or SHA512¶
from gntplib.keys import SHA256, SHA512
# ✓ Recommended: SHA256 (good balance)
publisher = Publisher(
'App', events,
password='strong-password',
key_hashing=SHA256
)
# ✓ Also good: SHA512 (maximum security)
publisher = Publisher(
'App', events,
password='strong-password',
key_hashing=SHA512
)
# ✗ Avoid: MD5 (weak)
# ✗ Avoid: SHA1 (deprecated)
Encryption¶
Enable Encryption for Sensitive Data¶
from gntplib.keys import SHA256
from gntplib.ciphers import AES
publisher = Publisher(
'App', events,
password='strong-password',
key_hashing=SHA256,
encryption=AES # Enable encryption
)
When to Use Encryption¶
Use encryption when:
Notifications contain sensitive information
Communicating over untrusted networks
Compliance requirements mandate it
Defense in depth is desired
Encryption not needed when:
GNTP server is on localhost
Network is fully trusted
Notifications are not sensitive
Performance is critical
Password Management¶
Store Passwords Securely¶
import os
from gntplib import Publisher
# ✓ Good: Read from environment
password = os.environ.get('GNTP_PASSWORD')
if not password:
raise ValueError("GNTP_PASSWORD not set")
publisher = Publisher('App', events, password=password)
# ✓ Good: Read from secure file
import json
from pathlib import Path
config_file = Path.home() / '.config' / 'myapp' / 'gntp.json'
with open(config_file) as f:
config = json.load(f)
password = config['gntp_password']
# ✗ Bad: Hardcoded password
password = 'my-secret-password' # Don't do this!
# ✗ Bad: Committed to version control
# config.py
GNTP_PASSWORD = 'secret' # Don't commit this!
Use Key Management Systems¶
For production applications:
# Example with AWS Secrets Manager
import boto3
def get_gntp_password():
client = boto3.client('secretsmanager')
response = client.get_secret_value(SecretId='gntp-password')
return response['SecretString']
password = get_gntp_password()
publisher = Publisher('App', events, password=password)
Network Security¶
Use Firewall Rules¶
Restrict GNTP server access:
# Allow only from specific IPs
sudo ufw allow from 192.168.1.0/24 to any port 23053
# Or allow only localhost
sudo ufw allow from 127.0.0.1 to any port 23053
Use VPN or SSH Tunneling¶
For remote GNTP servers:
# SSH tunnel
ssh -L 23053:localhost:23053 user@remote-server
# Then connect to localhost
publisher = Publisher(
'App', events,
host='localhost',
port=23053,
password='password'
)
Input Validation¶
Validate User Input¶
from gntplib.exceptions import GNTPValidationError
def send_notification(title: str, message: str):
"""Send notification with validation."""
# Validate inputs
if not title or len(title) > 200:
raise ValueError("Invalid title")
if len(message) > 1000:
raise ValueError("Message too long")
# Sanitize if needed
title = title.strip()
message = message.strip()
try:
publisher.publish('event', title, message)
except GNTPValidationError as e:
raise ValueError(f"Invalid notification: {e}")
Prevent Injection Attacks¶
# ✓ Good: Parameterized
def notify_user(username: str, action: str):
"""Safe notification."""
title = f"User {username}"
message = f"performed action: {action}"
publisher.publish('user_event', title, message)
# ✗ Bad: String interpolation without validation
def notify_user_unsafe(username: str):
"""Unsafe: username not validated."""
# If username contains GNTP control characters,
# could potentially cause issues
publisher.publish('event', username, 'logged in')
Resource Security¶
Validate Resource Sources¶
from gntplib import Resource
from urllib.parse import urlparse
def load_icon_safely(url: str) -> Resource:
"""Load icon with URL validation."""
# Parse and validate URL
parsed = urlparse(url)
# Check scheme
if parsed.scheme not in ('http', 'https'):
raise ValueError("Only HTTP(S) URLs allowed")
# Check domain (whitelist)
allowed_domains = ['cdn.example.com', 'assets.myapp.com']
if parsed.netloc not in allowed_domains:
raise ValueError("Domain not allowed")
return Resource.from_url(url)
Limit Resource Size¶
MAX_ICON_SIZE = 1024 * 1024 # 1MB
def load_icon_with_size_check(filepath: str) -> Resource:
"""Load icon with size limit."""
import os
size = os.path.getsize(filepath)
if size > MAX_ICON_SIZE:
raise ValueError(f"Icon too large: {size} bytes")
return Resource.from_file(filepath)
Error Handling¶
Don’t Leak Sensitive Information¶
from gntplib.exceptions import GNTPError
import logging
# ✓ Good: Log securely
try:
publisher.publish('event', title, message)
except GNTPError as e:
# Log error details server-side
logging.error(f"GNTP error: {e}", exc_info=True)
# Return generic message to user
raise RuntimeError("Failed to send notification")
# ✗ Bad: Expose details to user
try:
publisher.publish('event', title, message)
except GNTPError as e:
# Don't expose internal details
print(f"Error: {e}")
print(f"Server: {publisher.host}")
print(f"Password: {publisher.password}") # Never do this!
Rate Limiting¶
Implement Rate Limiting¶
import time
from collections import deque
class RateLimitedPublisher:
"""Publisher with rate limiting."""
def __init__(self, publisher, max_per_minute=60):
self.publisher = publisher
self.max_per_minute = max_per_minute
self.times = deque()
def publish(self, *args, **kwargs):
"""Publish with rate limiting."""
now = time.time()
# Remove old entries
minute_ago = now - 60
while self.times and self.times[0] < minute_ago:
self.times.popleft()
# Check limit
if len(self.times) >= self.max_per_minute:
raise RuntimeError("Rate limit exceeded")
# Send notification
self.publisher.publish(*args, **kwargs)
self.times.append(now)
Security Checklist¶
Before Production¶
[ ] Strong passwords used
[ ] SHA256 or SHA512 hash algorithm
[ ] Encryption enabled for sensitive data
[ ] Passwords stored securely (not hardcoded)
[ ] Input validation implemented
[ ] Resource sources validated
[ ] Error messages don’t leak information
[ ] Rate limiting implemented
[ ] Network access restricted
[ ] Logs don’t contain sensitive data
[ ] Dependencies up to date
[ ] Security audit performed
Regular Maintenance¶
[ ] Update dependencies regularly
[ ] Review security logs
[ ] Rotate passwords periodically
[ ] Monitor for security advisories
[ ] Test backup authentication methods
[ ] Review access controls
Reporting Security Issues¶
If you discover a security vulnerability:
Do NOT open a public issue
Email cumulus13@gmail.com
Include:
Description of the vulnerability
Steps to reproduce
Potential impact
Suggested fix (if any)
Wait for confirmation before disclosing
We aim to respond within 48 hours and will work with you to address the issue promptly.