Skip to main content

Insecure exceptions - Empty or no catch

Need

Proper exception handling and error management

Context

  • Usage of Python 3.0+ for developing Python applications
  • Usage of Django for building web applications with Python

Description

Non compliant code

from django.http import HttpResponse
from .models import MyModel

def my_view(request):
try:
my_model = MyModel.objects.get(pk=1)
except:
pass

return HttpResponse(my_model)

The above code shows a Django view function my_view that tries to retrieve an instance of MyModel with a primary key of 1.

The vulnerability lies in the except: block where an exception is silently ignored with the pass statement. This is a clear example of an empty catch.

If the get method throws an exception (for example, if there is no MyModel instance with a primary key of 1), the except: block will catch it and do nothing. This means that the my_model variable will not be defined, and the function will still try to return it as a HttpResponse.

This will result in an UnboundLocalError being raised, which is not being handled anywhere. This can cause the application to become temporarily or permanently unavailable, depending on how the application is structured.

Furthermore, silently ignoring exceptions is a bad practice, because it makes debugging more difficult and can hide underlying issues that might be critical.

Steps

  • Identify the specific exceptions that the code might throw and catch those specific exceptions instead of using a generic catch-all exception handler.
  • Remove the 'pass' statement in the catch block and replace it with appropriate error handling code. This could include logging the error, returning an error response, or re-throwing the exception after some cleanup.
  • Ensure that the application can continue to function in a meaningful way even when an exception is caught. This could involve setting default values, retrying the operation, or informing the user about the error.
  • Consider using the 'finally' block to ensure that any necessary cleanup code is always executed, regardless of whether an exception was thrown or not.

Compliant code

from django.http import HttpResponse, Http404
from .models import MyModel
import logging

def my_view(request):
my_model = None
try:
my_model = MyModel.objects.get(pk=1)
except MyModel.DoesNotExist:
logging.error('MyModel with pk=1 does not exist')
raise Http404("No MyModel matches the given query.")
except Exception as e:
logging.error('An error occurred: ' + str(e))
return HttpResponse('An error occurred: ' + str(e), status=500)

return HttpResponse(my_model)

The updated code now handles exceptions in a more secure and robust manner.

Firstly, instead of using a generic catch-all exception handler, we are now catching specific exceptions that might be thrown by the MyModel.objects.get(pk=1) operation. This includes the MyModel.DoesNotExist exception, which is thrown when no MyModel instance with pk=1 exists, and a generic Exception, which will catch any other exceptions that might be thrown.

Secondly, we have removed the pass statement in the catch block and replaced it with appropriate error handling code. In the case of MyModel.DoesNotExist, we log the error and raise a Http404 exception, which will result in a 404 error being returned to the client. In the case of a generic Exception, we log the error and return a 500 error response to the client.

Thirdly, we ensure that the application can continue to function in a meaningful way even when an exception is caught. If an exception is thrown, an error response is returned to the client, informing them about the error.

Finally, although not shown in the code, you could consider using a finally block to ensure that any necessary cleanup code is always executed, regardless of whether an exception was thrown or not. This could include closing database connections, releasing resources, etc.

References