Closed yihong1120 closed 1 month ago
The plan to solve the bug involves adding proper exception handling and logging mechanisms to ensure that sensitive information is not exposed through exceptions. This will be done by updating the code in the relevant files to catch exceptions, log them securely, and return generic error messages to the client.
The bug is caused by the lack of proper exception handling in the code, which can lead to sensitive information being exposed through exception messages. Specifically, the model_downloader.py
file includes detailed exception messages in the JSON responses, which can reveal sensitive information about the server or the request.
Here are the implementation details and code snippets to address the issue:
examples/YOLOv8_server_api/security.py
Add exception handling and logging to the update_secret_key
function to ensure that any potential issues are securely managed.
import logging
import secrets
from flask import Flask
def update_secret_key(app: Flask) -> None:
"""
Updates the JWT secret key in the application configuration.
This function generates a new, secure JWT secret key and updates the
Flask application's config with this token. It helps protect against
security breaches by regularly rotating the secret key.
Args:
app (Flask): The Flask app instance to update the JWT secret key.
"""
try:
# Securely generate a new, random JWT secret key using the secrets library
app.config['JWT_SECRET_KEY'] = secrets.token_urlsafe(16)
except Exception as e:
logging.error("Failed to update JWT secret key: %s", str(e))
# Optionally, re-raise the exception if it should be handled further up the stack
raise
examples/YOLOv8_server_api/auth.py
Ensure that detailed error information is logged securely for debugging purposes, but not exposed to the user.
import logging
from flask import Blueprint, request, jsonify, Response
from flask_jwt_extended import create_access_token
auth_blueprint = Blueprint('auth', __name__)
logger = logging.getLogger(__name__)
@auth_blueprint.route('/token', methods=['POST'])
def create_token() -> Response:
try:
username = request.json.get('username', None)
password = request.json.get('password', None)
user = user_cache.get(username)
if not user:
user = User.query.filter_by(username=username).first()
if user:
user_cache[username] = user
if not user or not user.check_password(password):
logger.warning(f'Failed login attempt for username: {username}')
response = jsonify({'msg': 'Wrong user name or passcode.'})
response.status_code = 401
return response
access_token = create_access_token(identity=username)
return jsonify(access_token=access_token)
except Exception as e:
logger.error(f'Error during authentication: {str(e)}')
response = jsonify({'msg': 'Internal server error.'})
response.status_code = 500
return response
examples/YOLOv8_server_api/app.py
Implement a global error handler and custom error pages to catch and handle exceptions gracefully without exposing sensitive information.
from flask import Flask, jsonify
app = Flask(__name__)
# Global error handler
@app.errorhandler(Exception)
def handle_exception(e):
# Log the exception details (ensure sensitive information is not logged)
app.logger.error(f"An error occurred: {str(e)}")
# Return a generic error message
response = {
"message": "An internal error occurred. Please try again later."
}
return jsonify(response), 500
# Custom error handler for 404 Not Found
@app.errorhandler(404)
def not_found_error(error):
response = {
"message": "Resource not found."
}
return jsonify(response), 404
# Custom error handler for 500 Internal Server Error
@app.errorhandler(500)
def internal_error(error):
response = {
"message": "An internal error occurred. Please try again later."
}
return jsonify(response), 500
examples/YOLOv8_server_api/model_downloader.py
Remove detailed error information from the JSON response and log the error details internally.
from __future__ import annotations
import datetime
from pathlib import Path
import requests
from flask import Blueprint, jsonify, send_from_directory, current_app as app
from flask_limiter import Limiter
from flask_limiter.util import get_remote_address
models_blueprint = Blueprint('models', __name__)
limiter = Limiter(key_func=get_remote_address)
@models_blueprint.route('/models/<model_name>', methods=['GET'])
@limiter.limit('10 per minute')
def download_model(model_name):
"""
Endpoint to download model files.
Args:
model_name (str): The name of the model file to download.
Returns:
Response: A Flask response object that downloads the model file.
"""
# Define the directory where the model files are stored
MODELS_DIRECTORY = 'models/pt/'
# Define the external URL for model files
MODEL_URL = f"http://changdar-server.mooo.com:28000/models/{model_name}"
# Ensure the model name is valid
allowed_models = ['best_yolov8l.pt', 'best_yolov8x.pt']
if model_name not in allowed_models:
return jsonify({'error': 'Model not found.'}), 404
try:
# Check last modified time via a HEAD request to the external server
response = requests.head(MODEL_URL)
if response.status_code == 200 and 'Last-Modified' in response.headers:
server_last_modified = datetime.datetime.strptime(
response.headers['Last-Modified'],
'%a, %d %b %Y %H:%M:%S GMT',
)
local_file_path = f"{MODELS_DIRECTORY}{model_name}"
# Check local file's last modified time
local_last_modified = datetime.datetime.fromtimestamp(
Path(local_file_path).stat().st_mtime,
)
if local_last_modified >= server_last_modified:
return jsonify({'message': 'Local model is up-to-date.'}), 304
# If not up-to-date, fetch the file and return it
return send_from_directory(
MODELS_DIRECTORY, model_name, as_attachment=True,
)
except FileNotFoundError:
return jsonify({'error': 'Model not found.'}), 404
except requests.RequestException:
# Log the exception details internally
app.logger.error('Failed to fetch model information', exc_info=True)
return (
jsonify(
{
'error': 'Failed to fetch model information.',
},
),
500,
)
By implementing the above changes, the application will handle exceptions securely, log necessary details internally, and return generic error messages to the client, thus preventing information exposure through exceptions.
Click here to create a Pull Request with the proposed solution
Files used for this task:
Tracking issue for: