I. Що таке django-mdeditor
?
django-mdeditor
— це популярний відкритий проект для Django, який надає зручний редактор markdown для вебсайтів, підтримуючи завантаження медіафайлів та живий перегляд. Цей редактор можна інтегрувати як безпосередньо в шаблони, так і в адмін-панель Django через клас MDTextField
.
II. Проблеми з безпекою
За замовчуванням, додаток не обробляє завантаження файлів у нейтральний спосіб. Він використовує клас UploadView
для обробки завантажень, і поведінка цього виду налаштовується в settings.py
через MDEDITOR_CONFIGS
. Проблема полягає в тому, що цей вид не реалізує контроль доступу за замовчуванням.
class UploadView(generic.View):
""" Завантаження зображень """
@method_decorator(csrf_exempt)
def dispatch(self, *args, **kwargs):
return super(UploadView, self).dispatch(*args, **kwargs)
def post(self, request, *args, **kwargs):
upload_image = request.FILES.get("editormd-image-file", None)
media_root = settings.MEDIA_ROOT
# перевірка на відсутність зображення
if not upload_image:
return JsonResponse({
'success': 0,
'message': "未获取到要上传的图片",
'url': ""
})
# перевірка формату зображення
file_name_list = upload_image.name.split('.')
file_extension = file_name_list.pop(-1)
file_name = '.'.join(file_name_list)
if file_extension not in MDEDITOR_CONFIGS['upload_image_formats']:
return JsonResponse({
'success': 0,
'message': "上传图片格式错误,允许上传图片格式为:%s" % ','.join(
MDEDITOR_CONFIGS['upload_image_formats']),
'url': ""
})
# перевірка папки для зображень
file_path = os.path.join(media_root, MDEDITOR_CONFIGS['image_folder'])
if not os.path.exists(file_path):
try:
os.makedirs(file_path)
except Exception as err:
return JsonResponse({
'success': 0,
'message': "上传失败:%s" % str(err),
'url': ""
})
# збереження зображення
file_full_name = '%s_%s.%s' % (file_name,
'{0:%Y%m%d%H%M%S%f}'.format(datetime.datetime.now()),
file_extension)
with open(os.path.join(file_path, file_full_name), 'wb+') as file:
for chunk in upload_image.chunks():
file.write(chunk)
return JsonResponse({'success': 1,
'message': "上传成功!",
'url': os.path.join(settings.MEDIA_URL,
MDEDITOR_CONFIGS['image_folder'],
file_full_name)})
Це може призвести до ситуацій, коли для використання редактора може знадобитися аутентифікація (наприклад, через адмін-панель Django), але завантаження файлів не вимагає цього.
III. Наслідки
Найпростішим способом використання цієї вразливості є атака типу Denial of Service (DoS) через файлову систему, що впливає на доступність.
Крім того, це може бути використано для підвищення прихованості під час атаки. Наприклад, якщо уразливий сайт https://jobs.ecorp.com
, неавтентифікований атакувальник може завантажити і отримати зловмисні корисні навантаження, розміщені на цьому домені, що допоможе уникнути виявлення через фільтрацію DNS.
IV. Запропоновані виправлення
Тимчасове виправлення
Негайним виправленням буде декорування виду завантаження через login_required
для забезпечення аутентифікації. Наприклад, замість того, щоб слідувати README.md проекту:
urlpatterns = [
...
url(r'mdeditor/', include('mdeditor.urls')),
...
]
Ви можете зареєструвати вид і обгорнути його декоратором login_required
:
urlpatterns = [
...
path('mdeditor/uploads/', login_required(UploadView.as_view()), name='uploads'),
...
]
Постійне виправлення
Постійним рішенням може бути використання цього форка, який я створив для цього додатку, і відповідна зміна MDEDITOR_CONFIGS
. Проблема в тому, що завантаження все ще не обробляється в нейтральний спосіб.
Альтернативно, за рахунок порушення сумісності з додатками, що залежать від цього пакету, більш надійним рішенням було б створити ще один форк, який повністю видалить UploadView
і використовуватиме HTTP-коди повернення для зворотного зв'язку замість того, щоб читати JSON-відповіді на фронтенді.
Це, по суті, залишить завдання обробки завантажень на стороні серверу для розробників, надаючи їм повний контроль над безпекою.
Посилання
Перекладено з: Common misconfig may lead to Improper Access Control on django-mdeditor’s upload path