Sami commited on
Commit
b57a95c
·
0 Parent(s):

Initial deployment

Browse files
Files changed (8) hide show
  1. .gitattributes +32 -0
  2. .gitignore +11 -0
  3. Dockerfile +16 -0
  4. README.md +75 -0
  5. README_HF.md +20 -0
  6. app.py +196 -0
  7. requirements.txt +4 -0
  8. templates/login.html +112 -0
.gitattributes ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ *.7z filter=lfs diff=lfs merge=lfs -text
2
+ *.arrow filter=lfs diff=lfs merge=lfs -text
3
+ *.bin filter=lfs diff=lfs merge=lfs -text
4
+ *.bz2 filter=lfs diff=lfs merge=lfs -text
5
+ *.ckpt filter=lfs diff=lfs merge=lfs -text
6
+ *.ftz filter=lfs diff=lfs merge=lfs -text
7
+ *.gz filter=lfs diff=lfs merge=lfs -text
8
+ *.h5 filter=lfs diff=lfs merge=lfs -text
9
+ *.joblib filter=lfs diff=lfs merge=lfs -text
10
+ *.lfs.* filter=lfs diff=lfs merge=lfs -text
11
+ *.mlmodel filter=lfs diff=lfs merge=lfs -text
12
+ *.model filter=lfs diff=lfs merge=lfs -text
13
+ *.msgpack filter=lfs diff=lfs merge=lfs -text
14
+ *.npy filter=lfs diff=lfs merge=lfs -text
15
+ *.npz filter=lfs diff=lfs merge=lfs -text
16
+ *.onnx filter=lfs diff=lfs merge=lfs -text
17
+ *.ot filter=lfs diff=lfs merge=lfs -text
18
+ *.parquet filter=lfs diff=lfs merge=lfs -text
19
+ *.pb filter=lfs diff=lfs merge=lfs -text
20
+ *.pickle filter=lfs diff=lfs merge=lfs -text
21
+ *.pkl filter=lfs diff=lfs merge=lfs -text
22
+ *.pt filter=lfs diff=lfs merge=lfs -text
23
+ *.pth filter=lfs diff=lfs merge=lfs -text
24
+ *.rar filter=lfs diff=lfs merge=lfs -text
25
+ *.safetensors filter=lfs diff=lfs merge=lfs -text
26
+ *.tar.* filter=lfs diff=lfs merge=lfs -text
27
+ *.tflite filter=lfs diff=lfs merge=lfs -text
28
+ *.tgz filter=lfs diff=lfs merge=lfs -text
29
+ *.wasm filter=lfs diff=lfs merge=lfs -text
30
+ *.xz filter=lfs diff=lfs merge=lfs -text
31
+ *.zip filter=lfs diff=lfs merge=lfs -text
32
+ *.zst filter=lfs diff=lfs merge=lfs -text
.gitignore ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ __pycache__/
2
+ *.py[cod]
3
+ *$py.class
4
+ .DS_Store
5
+ .env
6
+ .venv
7
+ venv/
8
+ ENV/
9
+ create_space.py
10
+ deploy.sh
11
+ setup_hf_space.py
Dockerfile ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM python:3.9-slim
2
+
3
+ WORKDIR /app
4
+
5
+ COPY . .
6
+
7
+ RUN pip install --no-cache-dir -r requirements.txt
8
+
9
+ # Create necessary directories
10
+ RUN mkdir -p static templates
11
+
12
+ # Make port 7860 available (default for HF Spaces)
13
+ EXPOSE 7860
14
+
15
+ # Command to run the application
16
+ CMD ["python", "app.py"]
README.md ADDED
@@ -0,0 +1,75 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Secure Balance Academy Viewer
2
+
3
+ This is a secure implementation of the Balance Academy web application that prevents users from downloading, copying, or inspecting the HTML content.
4
+
5
+ ## Features
6
+
7
+ - Authentication system with token-based access
8
+ - Prevention of right-click context menu
9
+ - Detection and prevention of developer tools
10
+ - Content made uncopyable
11
+ - Dynamic watermarking with timestamp
12
+ - Security headers for additional protection
13
+
14
+ ## Usage
15
+
16
+ Simply click the "Access Secure Content" button to view the protected content. Your session will automatically expire after 1 hour.
17
+
18
+ ## Security Notice
19
+
20
+ The protections implemented in this application are designed to discourage casual copying and downloading but cannot prevent determined technical users from accessing the content. For the most sensitive content, consider additional server-side protection measures
21
+
22
+ ## Setup Instructions
23
+
24
+ ### Local Development
25
+
26
+ 1. Install the required dependencies:
27
+ ```
28
+ pip install -r requirements.txt
29
+ ```
30
+
31
+ 2. Run the Flask application:
32
+ ```
33
+ python app.py
34
+ ```
35
+
36
+ 3. Open your browser and navigate to `http://localhost:7860`
37
+
38
+ ### Deployment on Hugging Face Spaces
39
+
40
+ 1. Create a new Space on Hugging Face with the "Gradio" template
41
+ 2. Upload all the files in this directory to your Space
42
+ 3. Add the following to your `requirements.txt`:
43
+ ```
44
+ flask==2.3.3
45
+ Werkzeug==2.3.7
46
+ gunicorn==21.2.0
47
+ Flask-Session==0.5.0
48
+ ```
49
+ 4. Add the following to your `app.py` at the top:
50
+ ```python
51
+ # This file will be used by Hugging Face Spaces
52
+ ```
53
+ 5. Commit and push your changes
54
+
55
+ ## How It Works
56
+
57
+ The application serves the HTML content with added JavaScript protections that:
58
+
59
+ 1. Disable right-clicking to prevent context menu access
60
+ 2. Intercept keyboard shortcuts that could be used to save or inspect the page
61
+ 3. Detect when developer tools are opened and replace page content with a warning
62
+ 4. Make text selection and copying difficult
63
+ 5. Add a dynamic watermark that updates with the current time
64
+
65
+ Additionally, the server adds security headers to every response to prevent certain attacks and restricts how the content can be loaded or framed.
66
+
67
+ ## Limitations
68
+
69
+ While these protections make it more difficult for casual users to copy or download the content, they are not foolproof against determined technical users. No client-side protection can be 100% effective against someone with technical knowledge, as the browser must ultimately receive and render the content.
70
+
71
+ For the most sensitive content, consider server-side rendering of partial content or delivering content as images rather than HTML.
72
+
73
+ ## License
74
+
75
+ This project is licensed under the terms of the LICENSE file included in the repository.
README_HF.md ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Secure Balance Academy Viewer
2
+
3
+ This is a secure implementation of the Balance Academy web application that prevents users from downloading, copying, or inspecting the HTML content.
4
+
5
+ ## Features
6
+
7
+ - Authentication system with token-based access
8
+ - Prevention of right-click context menu
9
+ - Detection and prevention of developer tools
10
+ - Content made uncopyable
11
+ - Dynamic watermarking with timestamp
12
+ - Security headers for additional protection
13
+
14
+ ## Usage
15
+
16
+ Simply click the "Access Secure Content" button to view the protected content. Your session will automatically expire after 1 hour.
17
+
18
+ ## Security Notice
19
+
20
+ The protections implemented in this application are designed to discourage casual copying and downloading but cannot prevent determined technical users from accessing the content. For the most sensitive content, consider additional server-side protection measures.
app.py ADDED
@@ -0,0 +1,196 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from flask import Flask, render_template, send_from_directory, Response
2
+ import os
3
+ import uuid
4
+ import time
5
+ from functools import wraps
6
+
7
+ app = Flask(__name__, static_folder='static')
8
+
9
+ # Configure session-based token security
10
+ app.config['SECRET_KEY'] = str(uuid.uuid4())
11
+ app.config['SESSION_TYPE'] = 'filesystem'
12
+ app.config['PERMANENT_SESSION_LIFETIME'] = 1800 # 30 minutes
13
+
14
+ # Store for valid tokens and their expiry times (in a real app, use a proper database)
15
+ VALID_TOKENS = {}
16
+ TOKEN_EXPIRY = 3600 # 1 hour
17
+
18
+ # Security headers for all responses
19
+ @app.after_request
20
+ def add_security_headers(response):
21
+ # Prevent content from being framed by other sites
22
+ response.headers['X-Frame-Options'] = 'DENY'
23
+
24
+ # Prevent browsers from performing MIME sniffing
25
+ response.headers['X-Content-Type-Options'] = 'nosniff'
26
+
27
+ # Enable XSS protection in browsers
28
+ response.headers['X-XSS-Protection'] = '1; mode=block'
29
+
30
+ # Content Security Policy to restrict resources
31
+ 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"
32
+
33
+ # Cache control - prevent caching
34
+ response.headers['Cache-Control'] = 'no-store, no-cache, must-revalidate, max-age=0'
35
+ response.headers['Pragma'] = 'no-cache'
36
+ response.headers['Expires'] = '0'
37
+
38
+ return response
39
+
40
+ # Load the HTML content
41
+ with open(os.path.join(os.path.dirname(os.path.dirname(__file__)), 'dist', 'index.html'), 'r') as f:
42
+ html_content = f.read()
43
+
44
+ # Add anti-inspection/download JavaScript to the HTML
45
+ def add_protection_scripts(html):
46
+ # Insert scripts right before the closing body tag
47
+ protection_scripts = """
48
+ <script>
49
+ // Disable right-click context menu
50
+ document.addEventListener('contextmenu', e => e.preventDefault());
51
+
52
+ // Disable keyboard shortcuts that could be used to save the page
53
+ document.addEventListener('keydown', function(e) {
54
+ // Ctrl/Cmd + S (Save)
55
+ if ((e.ctrlKey || e.metaKey) && e.keyCode === 83) {
56
+ e.preventDefault();
57
+ return false;
58
+ }
59
+
60
+ // Ctrl/Cmd + P (Print, which can be used to save as PDF)
61
+ if ((e.ctrlKey || e.metaKey) && e.keyCode === 80) {
62
+ e.preventDefault();
63
+ return false;
64
+ }
65
+
66
+ // Ctrl/Cmd + Shift + I or F12 (Developer Tools)
67
+ if (((e.ctrlKey || e.metaKey) && e.shiftKey && e.keyCode === 73) || e.keyCode === 123) {
68
+ e.preventDefault();
69
+ return false;
70
+ }
71
+
72
+ // Ctrl/Cmd + U (View Source)
73
+ if ((e.ctrlKey || e.metaKey) && e.keyCode === 85) {
74
+ e.preventDefault();
75
+ return false;
76
+ }
77
+ });
78
+
79
+ // Detect and prevent developer tools from opening with periodic check
80
+ (function() {
81
+ function detectDevTools() {
82
+ const widthThreshold = window.outerWidth - window.innerWidth > 160;
83
+ const heightThreshold = window.outerHeight - window.innerHeight > 160;
84
+
85
+ if (widthThreshold || heightThreshold) {
86
+ 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>';
87
+ }
88
+ }
89
+
90
+ // Check periodically
91
+ setInterval(detectDevTools, 1000);
92
+ })();
93
+
94
+ // Make content uncopyable
95
+ document.addEventListener('selectstart', e => e.preventDefault());
96
+ document.addEventListener('copy', e => e.preventDefault());
97
+
98
+ // Add a watermark with user info and timestamp
99
+ (function() {
100
+ const watermark = document.createElement('div');
101
+ watermark.style.position = 'fixed';
102
+ watermark.style.top = '0';
103
+ watermark.style.left = '0';
104
+ watermark.style.width = '100%';
105
+ watermark.style.height = '100%';
106
+ watermark.style.pointerEvents = 'none';
107
+ watermark.style.display = 'flex';
108
+ watermark.style.justifyContent = 'center';
109
+ watermark.style.alignItems = 'center';
110
+ watermark.style.zIndex = '10000';
111
+
112
+ const watermarkText = document.createElement('p');
113
+ watermarkText.style.transform = 'rotate(-45deg)';
114
+ watermarkText.style.fontSize = '20px';
115
+ watermarkText.style.color = 'rgba(0, 0, 0, 0.1)';
116
+ watermarkText.style.userSelect = 'none';
117
+
118
+ // Update watermark with current time every minute
119
+ function updateWatermark() {
120
+ const date = new Date();
121
+ watermarkText.textContent = 'Balance Academy - ' + date.toLocaleString();
122
+ }
123
+
124
+ updateWatermark();
125
+ setInterval(updateWatermark, 60000);
126
+
127
+ watermark.appendChild(watermarkText);
128
+ document.body.appendChild(watermark);
129
+ })();
130
+
131
+ // Additional anti-inspection code, tries to detect if devtools is open
132
+ (function() {
133
+ let devtools = function() {};
134
+ devtools.toString = function() {
135
+ 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>';
136
+ return 'Dev tools usage detected!';
137
+ };
138
+ console.log('%c', devtools);
139
+ })();
140
+ </script>
141
+ """
142
+ return html.replace('</body>', protection_scripts + '</body>')
143
+
144
+ protected_html = add_protection_scripts(html_content)
145
+
146
+ # Create a simple token-based authentication system
147
+ def generate_token():
148
+ token = str(uuid.uuid4())
149
+ VALID_TOKENS[token] = time.time() + TOKEN_EXPIRY
150
+ return token
151
+
152
+ def is_valid_token(token):
153
+ if token in VALID_TOKENS:
154
+ if time.time() < VALID_TOKENS[token]:
155
+ return True
156
+ else:
157
+ # Token expired
158
+ del VALID_TOKENS[token]
159
+ return False
160
+
161
+ def clean_expired_tokens():
162
+ current_time = time.time()
163
+ expired = [token for token, expiry in VALID_TOKENS.items() if current_time > expiry]
164
+ for token in expired:
165
+ del VALID_TOKENS[token]
166
+
167
+ # Authentication decorator
168
+ def token_required(f):
169
+ @wraps(f)
170
+ def decorated_function(*args, **kwargs):
171
+ token = kwargs.get('token')
172
+ if not token or not is_valid_token(token):
173
+ return Response('Unauthorized access', 401)
174
+ return f(*args, **kwargs)
175
+ return decorated_function
176
+
177
+ # Routes
178
+ @app.route('/')
179
+ def index():
180
+ token = generate_token()
181
+ clean_expired_tokens()
182
+ return render_template('login.html', token=token)
183
+
184
+ @app.route('/view/<token>')
185
+ @token_required
186
+ def view_content(token):
187
+ return protected_html
188
+
189
+ @app.route('/static/<path:filename>')
190
+ def serve_static(filename):
191
+ return send_from_directory(app.static_folder, filename)
192
+
193
+ if __name__ == '__main__':
194
+ os.makedirs(os.path.join(os.path.dirname(__file__), 'static'), exist_ok=True)
195
+ os.makedirs(os.path.join(os.path.dirname(__file__), 'templates'), exist_ok=True)
196
+ app.run(host='0.0.0.0', port=7860, debug=False)
requirements.txt ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ flask==2.3.3
2
+ Werkzeug==2.3.7
3
+ gunicorn==21.2.0
4
+ Flask-Session==0.5.0
templates/login.html ADDED
@@ -0,0 +1,112 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Secure Access - Balance Academy</title>
7
+ <link href="https://fonts.googleapis.com/css2?family=Nunito:wght@300;400;500;600;700&display=swap" rel="stylesheet">
8
+ <style>
9
+ body {
10
+ font-family: 'Nunito', sans-serif;
11
+ background-color: #f3f4f6;
12
+ display: flex;
13
+ justify-content: center;
14
+ align-items: center;
15
+ height: 100vh;
16
+ margin: 0;
17
+ padding: 0;
18
+ }
19
+
20
+ .login-container {
21
+ background-color: white;
22
+ border-radius: 8px;
23
+ box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
24
+ padding: 2rem;
25
+ width: 100%;
26
+ max-width: 400px;
27
+ text-align: center;
28
+ }
29
+
30
+ .login-logo {
31
+ font-size: 1.5rem;
32
+ font-weight: 700;
33
+ color: #0D6EFD;
34
+ margin-bottom: 1.5rem;
35
+ }
36
+
37
+ .login-title {
38
+ font-size: 1.25rem;
39
+ margin-bottom: 1rem;
40
+ color: #4b5563;
41
+ }
42
+
43
+ .login-description {
44
+ color: #6b7280;
45
+ margin-bottom: 1.5rem;
46
+ font-size: 0.875rem;
47
+ }
48
+
49
+ .login-button {
50
+ background-color: #0D6EFD;
51
+ color: white;
52
+ border: none;
53
+ border-radius: 4px;
54
+ padding: 0.75rem 1.5rem;
55
+ font-weight: 600;
56
+ cursor: pointer;
57
+ transition: background-color 0.2s;
58
+ width: 100%;
59
+ font-size: 1rem;
60
+ }
61
+
62
+ .login-button:hover {
63
+ background-color: #0b5ed7;
64
+ }
65
+
66
+ .terms {
67
+ margin-top: 1.5rem;
68
+ font-size: 0.75rem;
69
+ color: #6b7280;
70
+ }
71
+
72
+ .disclaimer {
73
+ margin-top: 1rem;
74
+ font-size: 0.75rem;
75
+ color: #ef4444;
76
+ }
77
+ </style>
78
+ </head>
79
+ <body>
80
+ <div class="login-container">
81
+ <div class="login-logo">Balance Academy</div>
82
+ <h1 class="login-title">Secure Access Portal</h1>
83
+ <p class="login-description">
84
+ This content is protected and requires secure access. By continuing, you agree not to download, copy, or distribute this content.
85
+ </p>
86
+ <a href="/view/{{ token }}">
87
+ <button class="login-button">Access Secure Content</button>
88
+ </a>
89
+ <p class="terms">
90
+ By accessing this content, you agree to our terms of use and privacy policy.
91
+ Your access is being logged and monitored.
92
+ </p>
93
+ <p class="disclaimer">
94
+ WARNING: Attempts to bypass security measures or extract content are prohibited and may result in legal action.
95
+ </p>
96
+ </div>
97
+
98
+ <script>
99
+ // Disable right-click
100
+ document.addEventListener('contextmenu', e => e.preventDefault());
101
+
102
+ // Disable keyboard shortcuts
103
+ document.addEventListener('keydown', function(e) {
104
+ if ((e.ctrlKey || e.metaKey) &&
105
+ (e.keyCode === 83 || e.keyCode === 80 || e.keyCode === 85)) {
106
+ e.preventDefault();
107
+ return false;
108
+ }
109
+ });
110
+ </script>
111
+ </body>
112
+ </html>