Код-ревью Middle Безопасность веб-приложения спрашивают часто обновлено
Код-ревью: поиск, редирект и импорт настроек
Вопрос
Найдите уязвимости в эндпоинтах Django.
import pickle
import requests
from django.db import connection
from django.http import HttpResponse, JsonResponse
from django.shortcuts import redirect
from django.utils.safestring import mark_safe
def search(request):
q = request.GET.get('q', '')
with connection.cursor() as cur:
cur.execute(f"SELECT id, title FROM posts WHERE title LIKE '%{q}%'")
rows = cur.fetchall()
html = '<h1>Результаты для ' + q + '</h1>'
return HttpResponse(mark_safe(html))
def go(request):
return redirect(request.GET['next'])
def import_settings(request):
data = pickle.loads(request.body)
return JsonResponse({'ok': True, 'keys': list(data)})
def preview(request):
r = requests.get(request.GET['url'])
return HttpResponse(r.content[:500])
Коротко
Исправления: Для превью ссылок: только http(s), резолв и запрет внутренних адресов, проверка редиректов, таймаут и лимит размера, а лучше отдельный изолированный сервис. Плюс все эти эндпоинты должны требовать вход, если они не публичные.
Что должен найти ревьюер
- Безопасностьстрока 12SQL-инъекция: ввод вставлен в SQL f-строкой; параметр
%sсо значениемf"%{q}%" - Безопасностьстрока 14Отражённый XSS: ввод склеен в HTML и помечен безопасным; использовать шаблон с автоэкранированием или
escape - Безопасностьстрока 19Открытый редирект на любой адрес; проверять
url_has_allowed_host_and_scheme - Безопасностьстрока 23Десериализация pickle из запроса: выполнение произвольного кода; принимать JSON
- Безопасностьстрока 28SSRF: сервер ходит по любому URL, включая внутренние адреса, без таймаута и лимита размера
Разбор
Исправления:
def search(request):
q = request.GET.get('q', '')
posts = Post.objects.filter(title__icontains=q).values('id', 'title')[:50]
return render(request, 'search.html', {'q': q, 'posts': posts})
def go(request):
nxt = request.GET.get('next', '/')
if not url_has_allowed_host_and_scheme(nxt, allowed_hosts={request.get_host()}, require_https=True):
nxt = '/'
return redirect(nxt)
def import_settings(request):
data = json.loads(request.body)
...
Для превью ссылок: только http(s), резолв и запрет внутренних адресов, проверка редиректов, таймаут и лимит размера, а лучше отдельный изолированный сервис. Плюс все эти эндпоинты должны требовать вход, если они не публичные.
Ответьте сами, а не прочитайте
В тренажёре вопрос попадёт в повторения и вернётся, когда начнёт забываться. Плюс билеты с зачётом: 20 вопросов, не больше 2 ошибок.
Предыдущий вопрос
pickle от пользователя
Следующий вопрос
Безопасность Python-бэкенда: что вы проверяете