Django: SQL Injection-proof Manager.py
У меня есть файл manager.py, который использует параметр.extra() для выполнения необработанных запросов SQL. Например:
class MyManager(models.Manager):
def order_null_last(self, field):
return super(DecisionManager, self).get_query_set()\
.extra(select={'has_field': "CASE WHEN " + field + " IS NULL THEN 1 ELSE 0 END"}).order_by('has_field', field)
Мне было предложено, что этот вид запроса может быть уязвим для атаки путем инъекции.
Переменная 'field' анализируется, чтобы убедиться, что она принадлежит группе допустимых значений, но эта проверка выполняется в представлении. Например:
class ModelList(ListView):
model = MyModel
def get(self, request, *args, **kwargs):
self.set_sorting(request)
def set_sorting(self, request):
self.sort_field = request.GET.get('sort', '-id')
if not self.sort_field in self.sort_options:
self.sort_field = 'id'
Так что, если кто-то написал новое представление с помощью вышеприведенного менеджера, но забыл отфильтровать параметры, то эксплойт будет возможен.
Итак, есть ли способ проверить параметр по полям моделей в менеджере, не получая циклический импорт? То есть менеджеру необходимо импортировать модель, чтобы получить список разрешенных полей, но модели необходимо импортировать менеджер.
2 ответа
Использование select_params во избежание SQL-инъекций:
class MyManager(models.Manager):
def order_null_last(self, field):
return super(DecisionManager, self).get_query_set()\
.extra(select={'has_field': "CASE WHEN %s IS NULL THEN 1 ELSE 0 END"}, select_params=(field,)).order_by('has_field', field)
Менеджер моделей имеет доступ к моделям и, следовательно, к полям через self.model.
Так что я могу написать это:
Класс MyManager(models.Manager):
def order_null_last(self, field):
if field in [modelfield.name for modelfield in self.model._meta.fields]:
return super(DecisionManager, self).get_query_set()\
.extra(select={'has_field': "CASE WHEN " + field + " IS NULL THEN 1 ELSE 0 END"}).order_by('has_field', field)
else:
return super(DecisionManager, self).get_query_set()