Skip to content

Commit 440582e

Browse files
Update server.py
1 parent 1c98d05 commit 440582e

File tree

1 file changed

+42
-44
lines changed

1 file changed

+42
-44
lines changed

server.py

+42-44
Original file line numberDiff line numberDiff line change
@@ -2,26 +2,25 @@
22
import logging
33
from datetime import datetime
44
from random import Random
5+
import requests
56
import base64
67

78
from flask import Flask, render_template, request, jsonify
8-
from flask_recaptcha import ReCaptcha
99
from flask_limiter import Limiter
1010
from flask_limiter.util import get_remote_address
1111

1212
from sendgrid import SendGridAPIClient
13-
from sendgrid.helpers.mail import (Mail, Attachment, FileContent, FileName, FileType, Disposition)
13+
from sendgrid.helpers.mail import Mail, Attachment, FileContent, FileName, FileType, Disposition
1414

1515
from dotenv import load_dotenv
1616

1717
load_dotenv()
1818

1919
class Config:
20-
MAX_CONTENT_LENGTH = 15 * 1024 * 1024 # 15 MB
20+
MAX_CONTENT_LENGTH = 15 * 1024 * 1024 # 15 MB
2121
EMAIL_DOMAIN = "@ethereum.org"
2222
DEFAULT_RECIPIENT_EMAIL = "[email protected]"
2323
NUMBER_OF_ATTACHMENTS = int(os.getenv('NUMBEROFATTACHMENTS', 10))
24-
DEBUG_MODE = os.getenv('DEBUG', 'False').lower() == 'true'
2524
SECRET_KEY = os.getenv('SECRET_KEY', 'you-should-set-a-secret-key')
2625

2726
def validate_env_vars(required_vars):
@@ -81,7 +80,7 @@ def create_email(to_email, identifier, text, all_attachments, reference=''):
8180
subject = f'Secure Form Submission {identifier}'
8281
if reference:
8382
subject = f'{reference} {subject}'
84-
83+
8584
message = Mail(
8685
from_email=FROMEMAIL,
8786
to_emails=to_email,
@@ -102,26 +101,27 @@ def create_email(to_email, identifier, text, all_attachments, reference=''):
102101
message.add_attachment(attachedFile)
103102
return message
104103

105-
106104
def validate_recaptcha(recaptcha_response):
107105
"""
108-
Validates the ReCaptcha response.
106+
Validates the ReCaptcha response using Google's API.
109107
"""
110-
try:
111-
if not recaptcha_response:
112-
logging.error('No ReCaptcha response provided.')
113-
raise ValueError('ReCaptcha verification failed: No response provided.')
108+
secret_key = os.getenv('RECAPTCHASECRETKEY')
109+
payload = {
110+
'secret': secret_key,
111+
'response': recaptcha_response
112+
}
113+
response = requests.post('https://www.google.com/recaptcha/api/siteverify', data=payload)
114+
result = response.json()
114115

115-
# Perform the verification
116-
if not recaptcha.verify(response=recaptcha_response):
117-
logging.error('ReCaptcha verification failed for response: %s', recaptcha_response)
118-
raise ValueError('ReCaptcha verification failed.')
116+
# Log the validation result
117+
logging.info(f"ReCaptcha validation response: {result}")
119118

120-
logging.info('ReCaptcha verification succeeded for response: %s', recaptcha_response)
121-
except Exception as e:
122-
logging.error('Error during ReCaptcha validation: %s', str(e))
123-
raise
119+
if not result.get('success'):
120+
raise ValueError('ReCaptcha verification failed.')
124121

122+
# Check action and score thresholds for additional security
123+
if result.get('score', 1.0) < 0.5:
124+
raise ValueError('ReCaptcha score is too low, indicating potential abuse.')
125125

126126
def send_email(message):
127127
"""
@@ -134,13 +134,10 @@ def send_email(message):
134134
if response.status_code not in [200, 201, 202]:
135135
logging.error('SendGrid failed with status code: %s, response body: %s', response.status_code, response.body)
136136
raise ValueError(f"Error: Failed to send email. Status code: {response.status_code}, body: {response.body}")
137-
else:
138-
logging.info('Email sent successfully. Status code: %s, response body: %s', response.status_code, response.body)
139137
except Exception as e:
140138
logging.error('Error sending email via SendGrid: %s', str(e))
141139
raise
142140

143-
144141
# Validate required environment variables
145142
required_env_vars = ['RECAPTCHASITEKEY', 'RECAPTCHASECRETKEY', 'SENDGRIDAPIKEY', 'SENDGRIDFROMEMAIL']
146143
validate_env_vars(required_env_vars)
@@ -152,40 +149,39 @@ def send_email(message):
152149

153150
app = Flask(__name__)
154151
app.config.from_object(Config)
155-
recaptcha = ReCaptcha(app)
156152

157153
# Initialize rate limiting
158154
limiter = Limiter(get_remote_address, app=app, default_limits=["200 per day", "50 per hour"])
159155

160156
# Configure logging
161157
log_file = os.environ.get('LOG_FILE', '')
162-
163158
if log_file:
164159
logging.basicConfig(filename=log_file, level=logging.INFO)
165160
else:
166161
logging.basicConfig(level=logging.INFO)
167162

168163
@app.route('/', methods=['GET'])
169164
def index():
170-
return render_template('index.html', notice='', hascaptcha=not Config.DEBUG_MODE, attachments_number=Config.NUMBER_OF_ATTACHMENTS, recaptcha_sitekey=RECAPTCHASITEKEY)
171-
165+
return render_template('index.html', notice='', hascaptcha=True, attachments_number=Config.NUMBER_OF_ATTACHMENTS, recaptcha_sitekey=RECAPTCHASITEKEY)
172166

173167
@app.route('/submit-encrypted-data', methods=['POST'])
174-
@limiter.limit("5 per minute")
168+
@limiter.limit("3 per minute")
175169
def submit():
176170
try:
177171
# Parse JSON data from request
178172
data = request.get_json()
179173

180-
# Validate ReCaptcha unless in debug mode
181-
if not Config.DEBUG_MODE:
182-
recaptcha_response = data.get('g-recaptcha-response', '')
183-
try:
184-
validate_recaptcha(recaptcha_response)
185-
except ValueError as e:
186-
return jsonify({'status': 'failure', 'message': str(e)}), 400
174+
# Validate ReCaptcha
175+
recaptcha_response = data.get('g-recaptcha-response', '')
176+
if not recaptcha_response:
177+
logging.warning(f"Missing ReCaptcha response. Potential bypass attempt detected from IP: {request.remote_addr}")
178+
return jsonify({'status': 'failure', 'message': 'Missing ReCaptcha token'}), 400
179+
180+
try:
181+
validate_recaptcha(recaptcha_response)
182+
except ValueError as e:
183+
return jsonify({'status': 'failure', 'message': str(e)}), 400
187184

188-
# Extract fields from JSON data
189185
message = data['message']
190186
recipient = data['recipient']
191187
reference = data.get('reference', '')
@@ -197,7 +193,6 @@ def submit():
197193
if not valid_recipient(recipient):
198194
raise ValueError('Error: Invalid recipient!')
199195

200-
# Get submission statistics
201196
date = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
202197
message_length = len(message)
203198
file_count = len(files)
@@ -212,24 +207,27 @@ def submit():
212207

213208
message = create_email(to_email, identifier, message, files, reference)
214209

215-
if Config.DEBUG_MODE:
216-
print(f"Attempt to send email to {to_email}")
217-
print(message.get())
218-
send_email(message)
219-
else:
220-
send_email(message)
210+
send_email(message)
221211

222212
notice = f'Thank you! The relevant team was notified of your submission. You could use the following identifier to refer to it in correspondence: <b>{identifier}</b>'
223213

224-
# Return success response
225214
return jsonify({'status': 'success', 'message': notice})
226215

227216
except Exception as e:
228-
# Log error message and return failure response
229217
error_message = "An unexpected error occurred. Please try again later."
230218
logging.error(f"Internal error: {str(e)}")
231219
return jsonify({'status': 'failure', 'message': error_message})
232220

221+
@app.errorhandler(429)
222+
def rate_limit_exceeded(e):
223+
"""
224+
Handles requests that exceed the rate limit.
225+
"""
226+
return jsonify({
227+
'status': 'failure',
228+
'message': 'Rate limit exceeded. You can only submit once per minute. Please try again later.'
229+
}), 429
230+
233231

234232
@app.errorhandler(413)
235233
def error413(e):

0 commit comments

Comments
 (0)