Improve security

This commit is contained in:
2025-09-21 17:48:36 +03:00
Unverified
parent 5e0d225b24
commit 991ef5dd0c
8 changed files with 20 additions and 45 deletions
+7 -35
View File
@@ -4,35 +4,18 @@ Generate VAPID keys for push notifications
Run this script to generate new VAPID keys for your application Run this script to generate new VAPID keys for your application
""" """
from pywebpush import WebPushException import sys
import base64 import base64
import json from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric import ec
from cryptography.hazmat.backends import default_backend
def generate_vapid_keys(): def generate_vapid_keys():
"""Generate VAPID keys for push notifications""" """Generate VAPID keys for push notifications"""
try: try:
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric import ec
from cryptography.hazmat.backends import default_backend
# Generate private key
private_key = ec.generate_private_key(ec.SECP256R1(), default_backend()) private_key = ec.generate_private_key(ec.SECP256R1(), default_backend())
# Get public key
public_key = private_key.public_key() public_key = private_key.public_key()
# Serialize keys
private_pem = private_key.private_bytes(
encoding=serialization.Encoding.PEM,
format=serialization.PrivateFormat.PKCS8,
encryption_algorithm=serialization.NoEncryption()
)
public_pem = public_key.public_bytes(
encoding=serialization.Encoding.PEM,
format=serialization.PublicFormat.SubjectPublicKeyInfo
)
# Convert to base64 for web push # Convert to base64 for web push
private_key_b64 = base64.urlsafe_b64encode( private_key_b64 = base64.urlsafe_b64encode(
private_key.private_bytes( private_key.private_bytes(
@@ -50,23 +33,12 @@ def generate_vapid_keys():
public_key_b64 = base64.urlsafe_b64encode(public_key_raw).decode('utf-8').rstrip('=') public_key_b64 = base64.urlsafe_b64encode(public_key_raw).decode('utf-8').rstrip('=')
print("VAPID Keys Generated:") print(f"VAPID_PRIVATE_KEY=\"{private_key_b64}\"")
print("=" * 50) print(f"VAPID_PUBLIC_KEY=\"{public_key_b64}\"")
print(f"Private Key: {private_key_b64}")
print(f"Public Key: {public_key_b64}")
print("=" * 50)
print("\nAdd these to your environment variables:")
print(f"VAPID_PRIVATE_KEY={private_key_b64}")
print(f"VAPID_PUBLIC_KEY={public_key_b64}")
return private_key_b64, public_key_b64 return private_key_b64, public_key_b64
except ImportError:
print("Error: cryptography library not found.")
print("Install it with: pip install cryptography")
return None, None
except Exception as e: except Exception as e:
print(f"Error generating VAPID keys: {e}") print(f"Error generating VAPID keys: {e}", file=sys.stderr)
return None, None return None, None
if __name__ == "__main__": if __name__ == "__main__":
+7 -4
View File
@@ -10,11 +10,14 @@ logger = logging.getLogger("uvicorn.error")
class PushNotificationService: class PushNotificationService:
def __init__(self): def __init__(self):
# VAPID keys - load from environment variables self.vapid_private_key = os.getenv("VAPID_PRIVATE_KEY")
self.vapid_private_key = os.getenv("VAPID_PRIVATE_KEY", "MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQghg2CSKiq0KsXXXImE75Z8UAphGBjkpYjUE87zPBmGqKhRANCAATxbNBGMhNl6gLmPL0PAf2YIJCVYX_TZrSqkj7SCqsu5VNMhnDOan6Qc9hEkcTZgvwj286C24SnxfH5CghVMCI6") self.vapid_public_key = os.getenv("VAPID_PUBLIC_KEY")
self.vapid_public_key = os.getenv("VAPID_PUBLIC_KEY", "BPFs0EYyE2XqAuY8vQ8B_ZggkJVhf9NmtKqSPtIKqy7lU0yGcM5qfpBz2ESRxNmC_CPbzoLbhKfF8fkKCFUwIjo")
if (not self.vapid_public_key) or (not self.vapid_private_key):
raise ValueError("VAPID public or private key is None")
self.vapid_claims = { self.vapid_claims = {
"sub": "mailto:admin@fromchat.com", "sub": "mailto:support@fromchat.ru",
"aud": "https://fcm.googleapis.com" "aud": "https://fcm.googleapis.com"
} }
-1
View File
@@ -1 +0,0 @@
JWT_SECRET="jwt-secret-change-in-production"
@@ -2,7 +2,7 @@ import { useState, useEffect } from "react";
import { PRODUCT_NAME, API_BASE_URL } from "../../../core/config"; import { PRODUCT_NAME, API_BASE_URL } from "../../../core/config";
import type { DialogProps } from "../../../core/types"; import type { DialogProps } from "../../../core/types";
import { MaterialDialog } from "../core/Dialog"; import { MaterialDialog } from "../core/Dialog";
import { initialize, isSupported, startElectronReceiver, stopElectronReceiver, subscribe, unsubscribe } from "../../../utils/notifications"; import { initialize, isSupported, startElectronReceiver, stopElectronReceiver, subscribe, unsubscribe } from "../../../utils/push-notifications";
import { isElectron } from "../../../electron/electron"; import { isElectron } from "../../../electron/electron";
import { useAppState } from "../../state"; import { useAppState } from "../../state";
import type { Switch } from "mdui/components/switch"; import type { Switch } from "mdui/components/switch";
+1 -1
View File
@@ -8,7 +8,7 @@ import { useRef } from "react";
import type { TextField } from "mdui/components/text-field"; import type { TextField } from "mdui/components/text-field";
import { useAppState } from "../state"; import { useAppState } from "../state";
import { MaterialTextField } from "../components/core/TextField"; import { MaterialTextField } from "../components/core/TextField";
import { initialize, isSupported, startElectronReceiver, subscribe } from "../../utils/notifications"; import { initialize, isSupported, startElectronReceiver, subscribe } from "../../utils/push-notifications";
import { isElectron } from "../../electron/electron"; import { isElectron } from "../../electron/electron";
export default function LoginScreen() { export default function LoginScreen() {
+1 -1
View File
@@ -7,7 +7,7 @@ import { DMPanel, type DMPanelData } from "./panels/DMPanel";
import { getAuthHeaders } from "../auth/api"; import { getAuthHeaders } from "../auth/api";
import { restoreKeys } from "../auth/crypto"; import { restoreKeys } from "../auth/crypto";
import { API_BASE_URL } from "../core/config"; import { API_BASE_URL } from "../core/config";
import { initialize, subscribe, startElectronReceiver, isSupported } from "../utils/notifications"; import { initialize, subscribe, startElectronReceiver, isSupported } from "../utils/push-notifications";
import { isElectron } from "../electron/electron"; import { isElectron } from "../electron/electron";
type Page = "login" | "register" | "chat" type Page = "login" | "register" | "chat"
+3 -2
View File
@@ -27,8 +27,9 @@
"preview": "cd deployment && docker compose up --build --watch", "preview": "cd deployment && docker compose up --build --watch",
"preview:clean": "cd deployment && docker compose down -v", "preview:clean": "cd deployment && docker compose down -v",
"clean": "npm run backend:clean && npm run frontend:clean && npm run preview:clean", "clean": "npm run backend:clean && npm run frontend:clean && npm run preview:clean",
"install": "npm run backend:dependencies && cp deployment/.env.example deployment/.env", "install": "npm run backend:dependencies && npm run generate:env",
"prepare": "husky" "prepare": "husky",
"generate:env": "echo \"JWT_SECRET=\\\"$(openssl rand -base64 32)\\\"\" > deployment/.env && ./.venv/bin/python3 backend/generate_vapid_keys.py 1>>deployment/.env"
}, },
"files": [ "files": [
"frontend/build/electron" "frontend/build/electron"