4 Stimmen

Wie kann ich die Größe einer Net::HTTP-Anfrage begrenzen?

Ich erstelle einen API-Dienst, der es ermöglicht, dem API-Aufruf eine URL eines Bildes zu übergeben, und der Dienst lädt das Bild zur Verarbeitung herunter.

Wie kann ich sicherstellen, dass mir jemand NICHT die URL eines 5 MB großen Bildes gibt? Gibt es eine Möglichkeit, die Anfrage zu begrenzen?

Das ist das, was ich bis jetzt habe, was im Grunde alles erfasst.

  req = Net::HTTP::Get.new(url.path)
  res = Net::HTTP.start(url.host, url.port) { |http|
    http.request(req)
  }

Danke! Conrad

7voto

Bob Aman Punkte 32266

cwninja hat Ihnen leider eine Antwort gegeben, die nur bei zufälligen Angriffen funktioniert. Ein intelligenter Angreifer wird keine Mühe haben, diese Prüfung zu umgehen. Es gibt zwei Hauptgründe, warum seine Methode nicht verwendet werden sollte. Erstens garantiert nichts, dass die Informationen in einer HEAD-Antwort mit der entsprechenden GET-Antwort übereinstimmen. Ein korrekt arbeitender Server wird dies sicherlich tun, aber ein böswilliger Akteur muss sich nicht an die Spezifikation halten. Der Angreifer könnte einfach eine HEAD-Antwort senden, die eine Content-Length angibt, die unter dem von Ihnen festgelegten Schwellenwert liegt, und Ihnen dann in der GET-Antwort eine riesige Datei übermitteln. Aber das deckt nicht einmal die Möglichkeit ab, dass ein Server eine Antwort mit dem Header Transfer-Encoding: chunked zurückschickt. Eine Chunked-Antwort könnte unter Umständen niemals enden. Ein paar Leute, die Ihren Server auf nie endende Antworten hinweisen, könnten einen trivialen Angriff auf die Ressourcenerschöpfung durchführen, selbst wenn Ihr HTTP-Client eine Zeitüberschreitung erzwingt.

Um dies korrekt zu tun, müssen Sie eine HTTP-Bibliothek verwenden, die es Ihnen ermöglicht, die Bytes zu zählen, während sie empfangen werden, und abzubrechen, wenn die Schwelle überschritten wird. Ich würde wahrscheinlich empfehlen Kandare und nicht Net::HTTP. (Kann man das überhaupt mit Net::HTTP machen?) Wenn Sie die on_body und/oder on_progress Callbacks verwenden, können Sie die eingehenden Bytes zählen und mitten in der Antwort abbrechen, wenn Sie eine zu große Datei erhalten. Offensichtlich, da cwninja Wie bereits erwähnt, sollten Sie den Vorgang auch abbrechen, wenn Sie einen Content-Length-Header erhalten, der größer ist als Ihr Schwellenwert. Curb ist auch deutlich schneller als Net::HTTP .

2voto

cwninja Punkte 9062

Versuchen Sie zuerst, dies auszuführen:

Net::HTTP.start(url.host, url.port) { |http|
  response = http.request_head(url.path)
  raise "File too big." if response['content-length'].to_i > 5*1024*1024
}

Sie haben immer noch eine Wettlaufsituation (jemand könnte die Datei austauschen, nachdem Sie die HEAD Anfrage), aber im einfachen Fall wird der Server nach den Kopfzeilen gefragt, die er bei einer E-Mail-Anfrage zurücksenden würde. GET Anfrage.

2voto

Eine weitere Möglichkeit zur Begrenzung der Download-Größe (der vollständige Code sollte den Antwortstatus, die Ausnahmebehandlung usw. überprüfen. Es ist nur ein Beispiel)

Net::HTTP.start(uri.host, uri.port) do |http|
  request = Net::HTTP::Get.new uri.request_uri
  http.request request do |response|
# check response codes here
      body=''
      response.read_body do |chunk|
           body += chunk
           break if body.size > MY_SAFE_SIZE_LIMIT
      end
      break
  end
end

2voto

Jeff Ward Punkte 13425

Wenn ich die beiden anderen Antworten kombiniere, möchte ich 1) den Größen-Header überprüfen, 2) die Größe der Chunks beobachten und 3) Unterstützung von https und 4) aggressives Erzwingen einer Auszeit. Hier ist ein Hilfsmittel, das ich mir ausgedacht habe:

require "net/http"
require 'uri'

module FetchUtil
  # Fetch a URL, with a given max bytes, and a given timeout
  def self.fetch_url url, timeout_sec=5, max_bytes=5*1024*1024
    uri = URI.parse(url)

    t0 = Time.now.to_f
    body = ''
    Net::HTTP.start(uri.host, uri.port,
               :use_ssl => (uri.scheme == 'https'),
               :open_timeout => timeout_sec,
               :read_timeout => timeout_sec) { |http|

      # First make a HEAD request and check the content-length
      check_res = http.request_head(uri.path)
      raise "File too big" if check_res['content-length'].to_i > max_bytes

      # Then fetch in chunks and bail on either timeout or max_bytes
      # (Note: timeout won't work unless bytes are streaming in...)
      http.request_get(uri.path) do |res|
        res.read_body do |chunk|
          raise "Timeout error" if (Time.now().to_f-t0 > timeout_sec)
          raise "Filesize exceeded" if (body.length+chunk.length > max_bytes)
          body += chunk
        end
      end
    }
    return body
  end
end

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X