AcademiaBalance / app.py
Sami
Initial deployment
b57a95c
raw
history blame
7.65 kB
from flask import Flask, render_template, send_from_directory, Response
import os
import uuid
import time
from functools import wraps
app = Flask(__name__, static_folder='static')
# Configure session-based token security
app.config['SECRET_KEY'] = str(uuid.uuid4())
app.config['SESSION_TYPE'] = 'filesystem'
app.config['PERMANENT_SESSION_LIFETIME'] = 1800 # 30 minutes
# Store for valid tokens and their expiry times (in a real app, use a proper database)
VALID_TOKENS = {}
TOKEN_EXPIRY = 3600 # 1 hour
# Security headers for all responses
@app.after_request
def add_security_headers(response):
# Prevent content from being framed by other sites
response.headers['X-Frame-Options'] = 'DENY'
# Prevent browsers from performing MIME sniffing
response.headers['X-Content-Type-Options'] = 'nosniff'
# Enable XSS protection in browsers
response.headers['X-XSS-Protection'] = '1; mode=block'
# Content Security Policy to restrict resources
response.headers['Content-Security-Policy'] = "default-src 'self' https://cdn.tailwindcss.com https://cdnjs.cloudflare.com https://fonts.googleapis.com https://fonts.gstatic.com; img-src 'self' data:; style-src 'self' 'unsafe-inline' https://cdnjs.cloudflare.com https://fonts.googleapis.com https://cdn.tailwindcss.com; script-src 'self' 'unsafe-inline' https://cdn.tailwindcss.com https://cdnjs.cloudflare.com https://cdn.jsdelivr.net"
# Cache control - prevent caching
response.headers['Cache-Control'] = 'no-store, no-cache, must-revalidate, max-age=0'
response.headers['Pragma'] = 'no-cache'
response.headers['Expires'] = '0'
return response
# Load the HTML content
with open(os.path.join(os.path.dirname(os.path.dirname(__file__)), 'dist', 'index.html'), 'r') as f:
html_content = f.read()
# Add anti-inspection/download JavaScript to the HTML
def add_protection_scripts(html):
# Insert scripts right before the closing body tag
protection_scripts = """
<script>
// Disable right-click context menu
document.addEventListener('contextmenu', e => e.preventDefault());
// Disable keyboard shortcuts that could be used to save the page
document.addEventListener('keydown', function(e) {
// Ctrl/Cmd + S (Save)
if ((e.ctrlKey || e.metaKey) && e.keyCode === 83) {
e.preventDefault();
return false;
}
// Ctrl/Cmd + P (Print, which can be used to save as PDF)
if ((e.ctrlKey || e.metaKey) && e.keyCode === 80) {
e.preventDefault();
return false;
}
// Ctrl/Cmd + Shift + I or F12 (Developer Tools)
if (((e.ctrlKey || e.metaKey) && e.shiftKey && e.keyCode === 73) || e.keyCode === 123) {
e.preventDefault();
return false;
}
// Ctrl/Cmd + U (View Source)
if ((e.ctrlKey || e.metaKey) && e.keyCode === 85) {
e.preventDefault();
return false;
}
});
// Detect and prevent developer tools from opening with periodic check
(function() {
function detectDevTools() {
const widthThreshold = window.outerWidth - window.innerWidth > 160;
const heightThreshold = window.outerHeight - window.innerHeight > 160;
if (widthThreshold || heightThreshold) {
document.body.innerHTML = '<div style="text-align: center; padding: 50px;"><h1>Developer tools detected</h1><p>Please close developer tools to view this content.</p></div>';
}
}
// Check periodically
setInterval(detectDevTools, 1000);
})();
// Make content uncopyable
document.addEventListener('selectstart', e => e.preventDefault());
document.addEventListener('copy', e => e.preventDefault());
// Add a watermark with user info and timestamp
(function() {
const watermark = document.createElement('div');
watermark.style.position = 'fixed';
watermark.style.top = '0';
watermark.style.left = '0';
watermark.style.width = '100%';
watermark.style.height = '100%';
watermark.style.pointerEvents = 'none';
watermark.style.display = 'flex';
watermark.style.justifyContent = 'center';
watermark.style.alignItems = 'center';
watermark.style.zIndex = '10000';
const watermarkText = document.createElement('p');
watermarkText.style.transform = 'rotate(-45deg)';
watermarkText.style.fontSize = '20px';
watermarkText.style.color = 'rgba(0, 0, 0, 0.1)';
watermarkText.style.userSelect = 'none';
// Update watermark with current time every minute
function updateWatermark() {
const date = new Date();
watermarkText.textContent = 'Balance Academy - ' + date.toLocaleString();
}
updateWatermark();
setInterval(updateWatermark, 60000);
watermark.appendChild(watermarkText);
document.body.appendChild(watermark);
})();
// Additional anti-inspection code, tries to detect if devtools is open
(function() {
let devtools = function() {};
devtools.toString = function() {
document.body.innerHTML = '<div style="text-align: center; padding: 50px;"><h1>Developer tools detected</h1><p>Please close developer tools to view this content.</p></div>';
return 'Dev tools usage detected!';
};
console.log('%c', devtools);
})();
</script>
"""
return html.replace('</body>', protection_scripts + '</body>')
protected_html = add_protection_scripts(html_content)
# Create a simple token-based authentication system
def generate_token():
token = str(uuid.uuid4())
VALID_TOKENS[token] = time.time() + TOKEN_EXPIRY
return token
def is_valid_token(token):
if token in VALID_TOKENS:
if time.time() < VALID_TOKENS[token]:
return True
else:
# Token expired
del VALID_TOKENS[token]
return False
def clean_expired_tokens():
current_time = time.time()
expired = [token for token, expiry in VALID_TOKENS.items() if current_time > expiry]
for token in expired:
del VALID_TOKENS[token]
# Authentication decorator
def token_required(f):
@wraps(f)
def decorated_function(*args, **kwargs):
token = kwargs.get('token')
if not token or not is_valid_token(token):
return Response('Unauthorized access', 401)
return f(*args, **kwargs)
return decorated_function
# Routes
@app.route('/')
def index():
token = generate_token()
clean_expired_tokens()
return render_template('login.html', token=token)
@app.route('/view/<token>')
@token_required
def view_content(token):
return protected_html
@app.route('/static/<path:filename>')
def serve_static(filename):
return send_from_directory(app.static_folder, filename)
if __name__ == '__main__':
os.makedirs(os.path.join(os.path.dirname(__file__), 'static'), exist_ok=True)
os.makedirs(os.path.join(os.path.dirname(__file__), 'templates'), exist_ok=True)
app.run(host='0.0.0.0', port=7860, debug=False)