CVE-2024-24827 (published 2024-03-15T20:15Z)

Discourse is an open source platform for community discussion. Without a rate limit on the POST /uploads endpoint, it makes it easier for an attacker to carry out a DoS attack on the server since creating an upload can be a resource intensive process. Do note that the impact varies from site to site as various site settings like `max_image_size_kb`, `max_attachment_size_kb` and `max_image_megapixels` will determine the amount of resources used when creating an upload. The issue is patched in the latest stable, beta and tests-passed version of Discourse. Users are advised to upgrade. Users unable to upgrade should reduce `max_image_size_kb`, `max_attachment_size_kb` and `max_image_megapixels` as smaller uploads require less resources to process. Alternatively, `client_max_body_size` can be reduced in Nginx to prevent large uploads from reaching the server.


Weakness information:
cwe_id: NVD-CWE-noinfo
cwe_name: Insufficient Information
description: There is insufficient information about the issue to classify it; details are unkown or unspecified.
extended_description: Insufficient Information

Fix information:
repo_url: https://github.com/discourse/discourse
commit_details: repo_url: https://github.com/discourse/discourse
author: Alan Guo Xiang Tan
author_date: 2024-02-07 12:55:36+08:00
msg: SECURITY: Add rate limits for uploads
repository: repo_url: https://github.com/discourse/discourse
repo_name: discourse/discourse
description: A platform for community discussion. Free, open, simple.
repo_language: Ruby
owner: discourse
file_changes: filename: uploads_controller.rb
change_type: ModificationType.MODIFY
diff: @@ -25,6 +25,13 @@ class UploadsController < ApplicationController
     # capture current user for block later on
     me = current_user
 
+    RateLimiter.new(
+      current_user,
+      "uploads-per-minute",
+      SiteSetting.max_uploads_per_minute,
+      1.minute.to_i,
+    ).performed!
+
     params.permit(:type, :upload_type)
     raise Discourse::InvalidParameters if params[:type].blank? && params[:upload_type].blank?
     # 50 characters ought to be enough for the upload type

programming_language: Ruby
filename: site_settings.yml
change_type: ModificationType.MODIFY
diff: @@ -2228,6 +2228,9 @@ rate_limits:
   max_complete_multipart_per_minute:
     default: 10
     hidden: true
+  max_uploads_per_minute:
+    default: 10
+    hidden: true
 
 developer:
   force_hostname:

programming_language: CoffeeScript
filename: uploads_controller_spec.rb
change_type: ModificationType.MODIFY
diff: @@ -19,6 +19,32 @@ RSpec.describe UploadsController do
       let(:fake_jpg) { Rack::Test::UploadedFile.new(file_from_fixtures("fake.jpg")) }
       let(:text_file) { Rack::Test::UploadedFile.new(File.new("#{Rails.root}/LICENSE.txt")) }
 
+      context "when rate limited" do
+        before { RateLimiter.enable }
+
+        use_redis_snapshotting
+
+        it "should return 429 response code when maximum number of uploads per minute has been exceeded for a user" do
+          SiteSetting.max_uploads_per_minute = 1
+
+          post "/uploads.json",
+               params: {
+                 file: Rack::Test::UploadedFile.new(logo_file),
+                 type: "avatar",
+               }
+
+          expect(response.status).to eq(200)
+
+          post "/uploads.json",
+               params: {
+                 file: Rack::Test::UploadedFile.new(logo_file),
+                 type: "avatar",
+               }
+
+          expect(response.status).to eq(429)
+        end
+      end
+
       it "expects a type or upload_type" do
         post "/uploads.json", params: { file: logo }
         expect(response.status).to eq(400)

programming_language: Ruby