DiamondDogHamm: Problem mit downloads großer Dateien (header)

Hallo Leute,
ich hab da folgendes Problem und zwar biete ich Dateien zum download an. Das ganze funktioniertmit kleinerem Dateien ohne Probleme, nur bei großen Dateien bricht der Download immer ab. Weiß da vll jemand wie ich das Problem beheben kann?

Errorlog:
[Sat Sep 03 08:37:28 2011] [warn] [client 83.216.237.188] (104)Connection reset by peer: mod_fcgid: error reading data from FastCGI server
[Sat Sep 03 08:37:28 2011] [warn] [client 83.216.237.188] (104)Connection reset by peer: mod_fcgid: ap_pass_brigade failed in handle_request_ipc function

Code:

  
    ini_set('memory_limit','1024M');  
	  
    	  
    header('Pragma: public');  
    header('Expires: 0');  
    header('Cache-Control: must-revalidate, post-check=0, pre-check=0');  
    header('Content-Transfer-Encoding: binary');  
    header('Content-Disposition: attachment; filename="'.$file.'"');  
  
    header('Content-Type: application/zip');  
    header('Content-Length: '. filesize($pfad.$file));  
  
    //readfile("http://".$_SERVER['HTTP_HOST']."/".$pfad.$file);  
	  
    $file = fopen($pfad.$file, "r");  
  
    while(!feof($file) and (connection_status()==0)) {  
      print(fread($file, 1024*8));  
      flush();  
    }  
    fclose($file);  
  
    die();  

Danke schon mal an alle für eure Hilfe.

Mfg Dog

  1. Hi!

    [...] nur bei großen Dateien bricht der Download immer ab. Weiß da vll jemand wie ich das Problem beheben kann?

    Da kommt zuerst die Analyse des Problems. Wenn du die Ursache kennst, kannst du es beheben. Anderenfalls kannst du nur ungezielt etwas unternehmen.

    Errorlog:

    Vom Apachen. Und das von PHP?

    [Sat Sep 03 08:37:28 2011] [warn] [client 83.216.237.188] (104)Connection reset by peer: mod_fcgid: error reading data from FastCGI server
    [Sat Sep 03 08:37:28 2011] [warn] [client 83.216.237.188] (104)Connection reset by peer: mod_fcgid: ap_pass_brigade failed in handle_request_ipc function

    Was erzählte eine Suchmaschine, als du sie nach diesen Meldungen befragtest?

    ini_set('memory_limit','1024M');

    Wozu brauchst du 1G Speicher?

    //readfile("http://".$_SERVER['HTTP_HOST']."/".$pfad.$file);

    Was gefiel dir denn an readfile() nicht?

    Lo!

    1. Vom Apachen. Und das von PHP?

      Das von PHP wäre das:
      [Sat Sep 03 08:28:47 2011] [warn] [client 83.216.237.188] mod_fcgid: stderr: ALERT - script tried to increase memory_limit to 1073741824 bytes which is above the allowed value (attacker '**.***.***.***', file '/var/www/web2561/html/diamonddog/download2.php', line 47)
      Was aderes hab ich im errorlog nicht gefunden und dies bezieht sich auf das:

        
      ini_set('memory_limit','1024M');  
      
      

      Was erzählte eine Suchmaschine, als du sie nach diesen Meldungen befragtest?

      Das das ein Serverproblem sein soll, so wie ich das gelesens habe.

      Wozu brauchst du 1G Speicher?

      Hatte irgendwo gelesen das große Files mehr Speicher benötigen würden.

      Was gefiel dir denn an readfile() nicht?

      Habe erst mit readfile gearbeitet aber dort mit dem selben Ergbniss, hatte dann einwenig rumgesucht und bin auf ein Script gestoßen und hatte dies dann mal zum testen genommen, leider mit dem selben Ergeniss.

      1. Hallo,

        Vom Apachen. Und das von PHP?
        Das von PHP wäre das:
        [Sat Sep 03 08:28:47 2011] [warn] [client 83.216.237.188] mod_fcgid: stderr: ALERT - script tried to increase memory_limit to 1073741824 bytes which is above the allowed value (attacker '**.***.***.***', file '/var/www/web2561/html/diamonddog/download2.php', line 47)

        so, dir geht also der Speicher aus. Das ist aber mit dem Code, den du gezeigt hast, nicht zu erklären. In der Schleife liest du ja immer nur Blöcke von 8kB in einen internen Puffer und gibst sie sofort wieder aus, ohne sie in einer Variablen zu speichern (etwas anderes macht readfile() übrigens auch nicht, es verwendet AFAIR sogar dieselbe Blockgröße).
        Was also steht in deinem Script in Zeile 47?

        Was aderes hab ich im errorlog nicht gefunden und dies bezieht sich auf das:
        ini_set('memory_limit','1024M');

        Nein. Damit legst du nur die Obergrenze für den nutzbaren Arbeitsspeicher fest. Die Überschreitung dieser Grenze findet woanders statt.

        Was erzählte eine Suchmaschine, als du sie nach diesen Meldungen befragtest?
        Das das ein Serverproblem sein soll, so wie ich das gelesens habe.

        Eher weniger, würde ich annehmen.

        Wozu brauchst du 1G Speicher?
        Hatte irgendwo gelesen das große Files mehr Speicher benötigen würden.

        Wenn man sie komplett in den Arbeitsspeicher laden will, ja. Aber das willst du doch wohl nicht, oder?

        Was gefiel dir denn an readfile() nicht?
        Habe erst mit readfile gearbeitet aber dort mit dem selben Ergbniss, hatte dann einwenig rumgesucht und bin auf ein Script gestoßen und hatte dies dann mal zum testen genommen, leider mit dem selben Ergeniss.

        Übrigens, deine s-Taste prellt anscheinend. Jedenfalls wenn sie direkt nach i betätigt wird.

        Ciao,
         Martin

        --
        Zivilisation bedeutet, dass die Eskimos warme Wohnungen bekommen und dann arbeiten müssen, damit sie sich einen Kühlschrank leisten können.
        Selfcode: fo:) ch:{ rl:| br:< n4:( ie:| mo:| va:) de:] zu:) fl:{ ss:) ls:µ js:(
        1. Ich poste euch mal den gesamten Code damit ihr auch seht was in Zeile 47 steht:

            
          <?php  
          require_once dirname(__FILE__) . "/db_config.php";  
            
          $db_tabell = 'downloads_user';  // db table  
          $db_tabel2 = 'downloads';  // db table  
            
          $md5 = $_GET['id'];  
            
          $db = mysql_connect($db_host,$db_user,$db_pass);  
          mysql_select_db($db_name);  
            
          $result = mysql_query("SELECT * FROM ".$db_tabell." WHERE  mail = '".$md5."'");  
            
          $check = mysql_query("SELECT * FROM ".$db_tabell);  
          while($check_row = mysql_fetch_array($check)){  
           if($check_row['date'] < time()) mysql_query("DELETE FROM ".$db_tabell." WHERE `mail`='".$check_row['mail']."'");  
          }  
            
          $found = mysql_query("SELECT count(*) FROM ".$db_tabell." WHERE  mail = '".$md5."'");  
          if(mysql_result($found,0)==0){  
            die("Der aufgerufene Eintrag existiert leider nicht, oder die Gültigkeit ist abgelaufen!");  
          }  
            
            
            
          while($row = mysql_fetch_array($result)){  
            $date = $row['date'];  
            $download_id = $row['download_id'];  
          }  
            
          if($date < time()){  
            mysql_query("DELETE FROM ".$db_tabell." WHERE `mail`='".$md5."'");  
            
            echo "Der Downloadlink ist leider abgelaufen!";	  
          }else{  
            mysql_query("UPDATE ".$db_tabell2." SET downloads=downloads+1 WHERE id = '".$download_id."'");  
            $result = mysql_query("SELECT * FROM ".$db_tabel2." WHERE  id = '".$download_id."'");  
          	  
            while($row = mysql_fetch_array($result)){  
              $file = $row['rarname'];  
            }  
            
            $pfad = "downloads/files/";  
            
            //Download starten  
            if(file_exists($pfad.$file)){  
              ini_set('memory_limit','1024M');  
          	  
              	  
              header('Pragma: public');  
              header('Expires: 0');  
              header('Cache-Control: must-revalidate, post-check=0, pre-check=0');  
              header('Content-Transfer-Encoding: binary');  
              header('Content-Disposition: attachment; filename="'.$file.'"');  
            
              header('Content-Type: application/zip');  
              header('Content-Length: '. filesize($pfad.$file));  
            
              readfile("http://".$_SERVER['HTTP_HOST']."/".$pfad.$file);  
          	  
              $file = fopen($pfad.$file, "r");  
            
              while(!feof($file) and (connection_status()==0)) {  
                print(fread($file, 1024*8));  
                flush();  
              }  
              fclose($file);  
            
              die();  
            }else{  
              die("Die Datei ($file) konnte nicht gefunden werden!");  
            }  
            
          }  
            
          mysql_close($db);  
          ?>  
          
          
          1. Hallo,

            Ich poste euch mal den gesamten Code damit ihr auch seht was in Zeile 47 steht:

            sehr gut, danke. Das Sahnehäubchen wäre jetzt gewesen, wenn du Zeile 47 auch noch markiert hättest. ;-)
            Aber wenn mein Editor richtig zählen kann, ist es tatsächlich diese:

            ini_set('memory_limit','1024M');

            Das hattest du ja vorher schon gesagt. Und ich muss mich korrigieren: Selbstverständlich ist das die Anweisung, die den Fehler auslöst - man sollte den Wortlaut der Meldung auch lesen - sie besagt ja nicht, wie ich behauptet hatte, dass die Speicheranforderung fehlgeschlagen sei, sondern das Setzen des Limits. Du hast versucht, das Memory Limit höher zu setzen, als dir erlaubt ist.
            Asche auf mein Haupt. :-(

            Leider kann ich aus der Meldung nicht erkennen, ob das Script dort abgebrochen wird, oder ob es weiterläuft und die Zeile einfach nur ignoriert wird. Aber da du sagst, der Download startet auf jeden Fall, muss letzteres zutreffen.

            Dann kann ich mir höchstens noch vorstellen, dass dein Script abgebrochen wird, weil es das Zeitlimit überschreitet.

            Ciao,
             Martin

            --
            Der Afrika-Forscher wird gefragt: "Stimmt es, dass man nicht von Löwen angefallen wird, wenn man eine Fackel trägt?" - "Kommt drauf an. Man muss die Fackel sehr schnell tragen."
            Selfcode: fo:) ch:{ rl:| br:< n4:( ie:| mo:| va:) de:] zu:) fl:{ ss:) ls:µ js:(
            1. Hi!

              ini_set('memory_limit','1024M');
              Selbstverständlich ist das die Anweisung, die den Fehler auslöst - man sollte den Wortlaut der Meldung auch lesen -

              Na bloß gut, dass es mir nicht allein so ging.

              Dann kann ich mir höchstens noch vorstellen, dass dein Script abgebrochen wird, weil es das Zeitlimit überschreitet.

              Sollte eigentlich nicht vorkommen, wenn es nicht zu knapp eingestellt ist. PHP misst nicht die Zeit, die andere verbrauchen, also die Wartezeit, bis der Ausgabepuffer sich leert, sollte nicht auf PHPs Kappe gehen. Aber vielleicht ist der Fall auch wieder, dass das Script von außen abgebrochen wird.

              Lo!

              1. Sollte eigentlich nicht vorkommen, wenn es nicht zu knapp eingestellt ist. PHP misst nicht die Zeit, die andere verbrauchen, also die Wartezeit, bis der Ausgabepuffer sich leert, sollte nicht auf PHPs Kappe gehen. Aber vielleicht ist der Fall auch wieder, dass das Script von außen abgebrochen wird.

                Wie meinst du das, mit von außen abgebrochen?

                1. Hi!

                  Aber vielleicht ist der Fall auch wieder, dass das Script von außen abgebrochen wird.
                  Wie meinst du das, mit von außen abgebrochen?

                  Bei 1&1 beispielsweise werden PHP-Instanzen von einem externen Prozess überwacht, der diese dann killt, wenn sie zu lange Rechenzeit verbraucht. PHPs max_execution_time misst intern und beendet sich selbst. Die Speicherüberprüfung, die dein ini_set() verhindert, ist ebenfalls eine solche äußere Überwachung.

                  Lo!

                  1. Bei 1&1 beispielsweise werden PHP-Instanzen von einem externen Prozess überwacht, der diese dann killt, wenn sie zu lange Rechenzeit verbraucht. PHPs max_execution_time misst intern und beendet sich selbst. Die Speicherüberprüfung, die dein ini_set() verhindert, ist ebenfalls eine solche äußere Überwachung.

                    Ok. Ich glaub das hab ich verstanden. :)

                    1. Weiß denn vll noch jemand ne Lösung wie ich es schaffe, das meine großen Downloads auch beim Empfeänger ankommen?

                      Oder gibt es vll noch eine andere Möglichkeit Dateien zum download anzubieten, ohne das diese direkt aufgerufen werden können?

                      1. Hi!

                        Weiß denn vll noch jemand ne Lösung wie ich es schaffe, das meine großen Downloads auch beim Empfeänger ankommen?

                        readfile() ist eigentlich die Lösung. Aber wenn du Zeitabbrüche bekommst, die nicht von einer PHP-Fehlermeldung begleitet sind, kannst du mit PHP nicht viel machen. Denn dann wird der Unterbrecher außerhalb sitzen.

                        Lo!

                        1. readfile() ist eigentlich die Lösung. Aber wenn du Zeitabbrüche bekommst, die nicht von einer PHP-Fehlermeldung begleitet sind, kannst du mit PHP nicht viel machen. Denn dann wird der Unterbrecher außerhalb sitzen.

                          Ok also sollte ich da mal meinen Webspaceanbieter anschreibe, wen ich dich so recht verstehe.

                          Komisch ist auch, das so der download startet:

                            
                          readfile("http://".$_SERVER["HTTP_HOST"]."/".$pfad.$file);  
                          
                          

                          aber so nicht:

                            
                          readfile($pfad.$file);  
                          
                          

                          und so komischerweise auch nicht:

                            
                          readfile($_SERVER["DOCUMENT_ROOT"]."/".$pfad.$file);  
                          
                          

                          also irgendwas scheind da doch dann nich ganz ok zu sein oder? Aber der Pfad stimmt zu 100% in alle fällen hab in mir mal per echo ausgeben lassen.

                          1. Hi!

                            Ok also sollte ich da mal meinen Webspaceanbieter anschreibe, wen ich dich so recht verstehe.

                            Wäre eine Möglichkeit, sich über seine Maßnahmen gegen zu lang laufende Scripts zu erkundigen. Achte dabei auf den Unterschied zwischen der generellen Zeit und der effektiv vom Script verwendeten CPU-Zeit (also 5 Sekunden bei 50% CPU-Auslastung sind nach Milchmädchenrechnung 2,5 Sekunden CPU-Zeit).

                            Komisch ist auch, das so der download startet:
                            readfile("http://".$_SERVER["HTTP_HOST"]."/".$pfad.$file);

                            Na, das willst du nicht, weil du damit einen Request an deinen eigenen Server stellst. Zudem sollte doch die Datei gar nicht einfach so per Request erreichbar sein. Warum liegt sie dann überhaupt innerhalb des DocumentRoots?

                            aber so nicht:
                            readfile($pfad.$file);
                            und so komischerweise auch nicht:
                            readfile($_SERVER["DOCUMENT_ROOT"]."/".$pfad.$file);

                            Dann müsste es eigentlich eine Fehlermeldung zu lesen geben. Den Erfolg kannst du auch am Rückgabewert der Funktion erkennen. Sieh ihn dir mit var_dump() an, ein einfaches echo reicht nicht, weil damit ein false unsichtbar ist.

                            Lo!

                            1. Wäre eine Möglichkeit, sich über seine Maßnahmen gegen zu lang laufende Scripts zu erkundigen. Achte dabei auf den Unterschied zwischen der generellen Zeit und der effektiv vom Script verwendeten CPU-Zeit (also 5 Sekunden bei 50% CPU-Auslastung sind nach Milchmädchenrechnung 2,5 Sekunden CPU-Zeit).

                              Das Script läuft doch aber beim gestartetn Download nicht mehr oder? Also ist doch die Laufzeit so wie ich das sehe egal oder?
                              Antwort meines Webspaceanbieters:
                              "wenn der Webserver nach ca. 300 s dicht macht wäre das aber normal. Das ist eine allgemeine Einstellung auch bei vielen anderen Servern. Am besten Sie benutzen dafür das FTP Protokoll, indem Sie das im Downloadlink bereit stellen."

                              Komisch ist auch, das so der download startet:
                              readfile("http://".$_SERVER["HTTP_HOST"]."/".$pfad.$file);

                              Na, das willst du nicht, weil du damit einen Request an deinen eigenen Server stellst. Zudem sollte doch die Datei gar nicht einfach so per Request erreichbar sein. Warum liegt sie dann überhaupt innerhalb des DocumentRoots?

                              Weil ich noch kein Zeit hatte Sie nach außerhalb zu verlagern, wenn die Scripte nicht laufen hat das ja dann auch keinen Zweck ;)

                              Dann müsste es eigentlich eine Fehlermeldung zu lesen geben. Den Erfolg kannst du auch am Rückgabewert der Funktion erkennen. Sieh ihn dir mit var_dump() an, ein einfaches echo reicht nicht, weil damit ein false unsichtbar ist.

                              Komischerweise funktioniert es jetzt mit DOCUMENT_ROOT aber, auch da gehn nur kleine Dateien die großen brechen immer wieder ab.

                              Gibt es nicht noch eine Möglichkeit zu unterbinden das die Links direkt eingebunden werden können?

                              Eventuelle Lösung:
                              Den Ordnernamen in dem die Files liegen, alle 24 Stunden ändern lassen. Wäre nur doof wenn der Ordner um 24 Uhr geändert wird und jemand um 23:59
                              Uhr nen Link anfordert der dann in 1 min abgelaufen ist.

                              1. Hallo,

                                Wäre eine Möglichkeit, sich über seine Maßnahmen gegen zu lang laufende Scripts zu erkundigen. Achte dabei auf den Unterschied zwischen der generellen Zeit und der effektiv vom Script verwendeten CPU-Zeit (also 5 Sekunden bei 50% CPU-Auslastung sind nach Milchmädchenrechnung 2,5 Sekunden CPU-Zeit).
                                Das Script läuft doch aber beim gestartetn Download nicht mehr oder?

                                doch, sicher - das Script reicht doch die Daten durch, und zwar bis zum letzten Byte. Ob das in *einem* Aufruf mit readfile() erfolgt, oder häppchenweise mit fread()/echo, ändert daran nichts. Beide Varianten bewirken, dass das Script erst endet, wenn der letzte Block an den Client gesendet wurde.

                                Also ist doch die Laufzeit so wie ich das sehe egal oder?

                                Oder.

                                "wenn der Webserver nach ca. 300 s dicht macht wäre das aber normal. Das ist eine allgemeine Einstellung auch bei vielen anderen Servern. Am besten Sie benutzen dafür das FTP Protokoll, indem Sie das im Downloadlink bereit stellen."

                                Klingt nach einer vernünftigen Alternative.

                                Eventuelle Lösung:
                                Den Ordnernamen in dem die Files liegen, alle 24 Stunden ändern lassen. Wäre nur doof wenn der Ordner um 24 Uhr geändert wird und jemand um 23:59 Uhr nen Link anfordert der dann in 1 min abgelaufen ist.

                                Warum willst du den direkten Zugriff denn so krampfhaft vermeiden?

                                So long,
                                 Martin

                                --
                                Auch mit eckigen Radios kann man Rundfunk hören.
                                Selfcode: fo:) ch:{ rl:| br:< n4:( ie:| mo:| va:) de:] zu:) fl:{ ss:) ls:µ js:(
                                1. doch, sicher - das Script reicht doch die Daten durch, und zwar bis zum letzten Byte. Ob das in *einem* Aufruf mit readfile() erfolgt, oder häppchenweise mit fread()/echo, ändert daran nichts. Beide Varianten bewirken, dass das Script erst endet, wenn der letzte Block an den Client gesendet wurde.

                                  Dann scheint ja irgendwo das Script zu denken das der Block zu ende ist, wenn er immer abbricht.

                                  Klingt nach einer vernünftigen Alternative.

                                  Heist auf deutsch, alles per ftp oder direkt downloaden zu lassen oder?

                                  Warum willst du den direkten Zugriff denn so krampfhaft vermeiden?

                                  Weil sich beim direktverlinken die User sich nicht anmelden müssen. ;)

                                  1. Hallo,

                                    doch, sicher - das Script reicht doch die Daten durch, und zwar bis zum letzten Byte. Ob das in *einem* Aufruf mit readfile() erfolgt, oder häppchenweise mit fread()/echo, ändert daran nichts. Beide Varianten bewirken, dass das Script erst endet, wenn der letzte Block an den Client gesendet wurde.
                                    Dann scheint ja irgendwo das Script zu denken das der Block zu ende ist, wenn er immer abbricht.

                                    nein, das haben wir doch schon mehrfach erklärt: Offensichtlich wird das Script "von außen", also durch den Webserver selbst oder andere überwachende Prozesse beendet.

                                    Klingt nach einer vernünftigen Alternative.
                                    Heist auf deutsch, alles per ftp oder direkt downloaden zu lassen oder?

                                    Was heißt "oder"? FTP *ist* doch direkt. - Und ja, das würde ich mir aus Nutzersicht wünschen.

                                    Warum willst du den direkten Zugriff denn so krampfhaft vermeiden?
                                    Weil sich beim direktverlinken die User sich nicht anmelden müssen. ;)

                                    Dagegen hilft HTTP-AUTH. Und die Möglichkeit des Direkt-Verlinkens bleibt trotzdem bestehen.

                                    So long,
                                     Martin

                                    --
                                    Wer mit dem Finger droht, sollte ihn am Abzug haben, und nicht in der Nase.
                                    Selfcode: fo:) ch:{ rl:| br:< n4:( ie:| mo:| va:) de:] zu:) fl:{ ss:) ls:µ js:(
                                    1. Hey,

                                      nein, das haben wir doch schon mehrfach erklärt: Offensichtlich wird das Script "von außen", also durch den Webserver selbst oder andere überwachende Prozesse beendet.

                                      Hm.. ok also blaibt das Thema header download dann wohl auf der Strecke.

                                      Was heißt "oder"? FTP *ist* doch direkt. - Und ja, das würde ich mir aus Nutzersicht wünschen.

                                      Ja nur das man den Ordner der files ja dynamisch machen könnte, so das ein direkt verlinken nur 24 Stunden oder so möglich wäre.

                                      Dagegen hilft HTTP-AUTH. Und die Möglichkeit des Direkt-Verlinkens bleibt trotzdem bestehen.

                                      Deswegen ja das Dynamische Verzeichniss. Wäre die einzigste Möglichkeit der Direktverlinkung noch entgegen zu wirken meiner Meinung nach.

                                      1. Hi,

                                        nein, das haben wir doch schon mehrfach erklärt: Offensichtlich wird das Script "von außen", also durch den Webserver selbst oder andere überwachende Prozesse beendet.
                                        Hm.. ok also blaibt das Thema header download dann wohl auf der Strecke.

                                        was meinst du mit "header download"?

                                        Dagegen hilft HTTP-AUTH. Und die Möglichkeit des Direkt-Verlinkens bleibt trotzdem bestehen.
                                        Deswegen ja das Dynamische Verzeichniss. Wäre die einzigste Möglichkeit der Direktverlinkung noch entgegen zu wirken meiner Meinung nach.

                                        Warum? Wenn du für die Downloads HTTP Authentication forderst, hat sich das doch erledigt. Dann sind die Ressourcen direkt zugänglich, aber nur für "angemeldete" Nutzer.

                                        Und deine s-Taste prellt anscheinend immer noch, wenn sie unmittelbar nach einem i gedrückt wird.

                                        Ciao,
                                         Martin

                                        --
                                        Husten kann böse Folgen haben.
                                        Besonders im Kleiderschrank.
                                        Selfcode: fo:) ch:{ rl:| br:< n4:( ie:| mo:| va:) de:] zu:) fl:{ ss:) ls:µ js:(
                              2. Hi!

                                Das Script läuft doch aber beim gestartetn Download nicht mehr oder? Also ist doch die Laufzeit so wie ich das sehe egal oder?

                                Doch, readfile() läuft bis das letzte Byte der Datei im Ausgabepuffer gelandet ist. Anschließend läuft das Script mit dem Rest des Codes weiter.

                                "wenn der Webserver nach ca. 300 s dicht macht wäre das aber normal. Das ist eine allgemeine Einstellung auch bei vielen anderen Servern. Am besten Sie benutzen dafür das FTP Protokoll, indem Sie das im Downloadlink bereit stellen."

                                Na, ich weiß nicht. Der schiebt das auf den Webserver, anscheinend ohne dass er konkret nachgesehen hat, ob das auch so eingestellt ist. Ich hab aber schon Dateien gezogen, die waren "größer als 5 Minuten".

                                Warum liegt sie dann überhaupt innerhalb des DocumentRoots?
                                Weil ich noch kein Zeit hatte Sie nach außerhalb zu verlagern, wenn die Scripte nicht laufen hat das ja dann auch keinen Zweck ;)

                                Das ist doch kein Aufwand und deswegen eigentlich von vorn herein so realisierbar. Oder soll das Script eine Änderung zum bestehenden System sein und die Datei muss bis zur Fertigstellung an alter Stelle verfügbar sein?

                                Gibt es nicht noch eine Möglichkeit zu unterbinden das die Links direkt eingebunden werden können?

                                Du meinst, Dateien liegen im downloadbaren Bereich, sollen aber nicht direkt sondern nur unter einer bestimmten Voraussetzung geladen werden dürfen, von denen ich annehme, dass das ein vorheriger Klick auf einen Link einer Webseite sein soll? Nein, das geht aus Prinzip nicht, weil HTTP kein "von" kennt. Jeder Request steht für sich allein. Es gibt zwar den Referrer, den man (in dem Fall vielleicht mit mod_rewrite) prüfen kann, aber der ist als unzuverlässig anzusehen, weil ihn der Client beliebig ändern kann.

                                Eventuelle Lösung:
                                Den Ordnernamen in dem die Files liegen, alle 24 Stunden ändern lassen.

                                Was ist überhaupt das eigentliche Ziel? Eine Zugangsbeschränkung nur mit Mitteln des Webservers wäre mit HTTP-Authentication realisierbar.

                                Lo!

                      2. Moin!

                        Weiß denn vll noch jemand ne Lösung wie ich es schaffe, das meine großen Downloads auch beim Empfeänger ankommen?

                        Oder gibt es vll noch eine andere Möglichkeit Dateien zum download anzubieten, ohne das diese direkt aufgerufen werden können?

                        Was heißt denn "direkt aufgerufen"?

                        Im Moment bietest du eine URL an, die (wenn alles funktionieren würde) dem Besucher die Datei liefert. Das ist für mich "direkt aufgerufen". Es würde genauso gut funktionieren, wenn du die Datei einfach ohne Skript direkt verlinken würdest. Und die Tatsache, dass du in deinem mißlungenen Versuch mit readfile() und einer HTTP-URL versuchtest zu arbeiten deutet eigentlich schon darauf hin, dass das SO auch funktionieren sollte.

                        - Sven Rautenberg

            2. Dann kann ich mir höchstens noch vorstellen, dass dein Script abgebrochen wird, weil es das Zeitlimit überschreitet.

              Also dagegen hab ich gerade auch versucht was zu machen:

                
              set_time_limit(0);  
              
              

              Lösche ich die Zeile mit:

                
              ini_set('memory_limit','1024M');  
              
              

              dann taucht zumindest der memory_limit Fehler nicht mehr auf. So wie es auch sein sollte :)

              Doch diese beiden Fehler tauchen nach abbrechen des Downloads immer wieder auf:
              [Sat Sep 03 10:45:44 2011] [warn] [client ##.###.###.###] (104)Connection reset by peer: mod_fcgid: error reading data from FastCGI server
              [Sat Sep 03 10:45:44 2011] [warn] [client ##.###.###.###] (104)Connection reset by peer: mod_fcgid: ap_pass_brigade failed in handle_request_ipc function

          2. Hi!

            $md5 = $_GET['id'];
            [...]
              mysql_query("DELETE FROM ".$db_tabell." WHERE mail='".$md5."'");

            Mal eben
              x' AND 1 --
            übergeben und leer ist die Tabelle. Und das ist nur eine Anwendungsmöglichkeit für SQL-Injection. Kontextwechsel beachten! Immer! Das Umkopieren von $_GET/$_POST in andere Variablen ist sinnlos und schützt nicht vor solchen kapitalen Sicherheitslücken.

            Lo!

            1. Hi!

              $md5 = $_GET['id'];
              [...]
                mysql_query("DELETE FROM ".$db_tabell." WHERE mail='".$md5."'");

              Mal eben
                x' AND 1 --
              übergeben und leer ist die Tabelle. Und das ist nur eine Anwendungsmöglichkeit für SQL-Injection. Kontextwechsel beachten! Immer! Das Umkopieren von $_GET/$_POST in andere Variablen ist sinnlos und schützt nicht vor solchen kapitalen Sicherheitslücken.

              So sollte es besser sein oder?

                
              $md5 = mysql_real_escape_string(trim($_GET['id']));  
              
              
              1. Hi!

                So sollte es besser sein oder?
                $md5 = mysql_real_escape_string(trim($_GET['id']));

                Etwas, ja. Noch besser wird es, wenn du die Behandlung erst beim Einbau in den anderen Kontext vornimmst. Das passiert dann gegebenenfalls mehrfach, wenn der Wert an mehreren Stellen eingefügt werden soll. Aber Sicherheit geht vor (minimalen) Performanceverlust und so hast du in $md5 (in diesem Fall) immer die Rohdaten zur Verfügung, die man ja für andere Fälle/Kontexte benötigen könnte. Zudem sieht man durch das Behandeln am Zielort gleich, dass das nicht vergessen wurde und muss sich nicht durch den Code rückwärts bewegen, um sicherzustellen, dass das unter allen Umständen bereits erfolgt ist. Die Behandlung sollte/muss auch für aus dem DBMS kommende Werte erfolgen, die in weitere Querys eingebaut werden. Besonders bei Daten aus String-Feldern können ebenfalls Zeichen mit besonderer Bedeutung enthalten sein. Auch eine indirekte SQL-Injection bleibt eine.

                Lo!

                1. Etwas, ja. Noch besser wird es, wenn du die Behandlung erst beim Einbau in den anderen Kontext vornimmst. Das passiert dann gegebenenfalls mehrfach, wenn der Wert an mehreren Stellen eingefügt werden soll. Aber Sicherheit geht vor (minimalen) Performanceverlust und so hast du in $md5 (in diesem Fall) immer die Rohdaten zur Verfügung, die man ja für andere Fälle/Kontexte benötigen könnte. Zudem sieht man durch das Behandeln am Zielort gleich, dass das nicht vergessen wurde und muss sich nicht durch den Code rückwärts bewegen, um sicherzustellen, dass das unter allen Umständen bereits erfolgt ist. Die Behandlung sollte/muss auch für aus dem DBMS kommende Werte erfolgen, die in weitere Querys eingebaut werden. Besonders bei Daten aus String-Feldern können ebenfalls Zeichen mit besonderer Bedeutung enthalten sein. Auch eine indirekte SQL-Injection bleibt eine.

                  Ok hab jetzt mal glaube ich die beste Lösung dafür gefunden, da meine $m5 eh nur aus Zahlen besteht:

                    
                  $md5 = mysql_real_escape_string(trim($_GET['id']));  
                  if(!is_numeric($md5)) $md5 = md5(0);  
                  
                  
                  1. Moin!

                    Ok hab jetzt mal glaube ich die beste Lösung dafür gefunden, da meine $m5 eh nur aus Zahlen besteht:

                    Nein.

                    MD5 erzeugt einen 16 Byte langen Hash, welcher oft als 32 Zeichen langer Hexadezimalstring ausgegeben wird. Es sind also mindestens die Buchstaben a-f in beliebiger Häufigkeit enthalten.

                    is_numeric() erlaubt zum Glück hexadezimale Zahlen, wenn sie ohne Vorzeichen sind, aber grundsätzlich hast du dich von Panik anstecken lassen.

                    mysql_real_escape_string() garantiert dir, dass egal was du hinein tust, am Ende ein sicherer String herauskommt, den du in Anführungszeichen in ein SQL-Statement integrieren kannst.

                    Dieser Code hier ist in der Sache fehlerhaft (die Diskussion, ob am Ende nicht trotzdem gültige Ergebnisse bei rauskommen, will ich nicht im Detail führen):

                    $md5 = mysql_real_escape_string(trim($_GET['id']));
                    if(!is_numeric($md5)) $md5 = md5(0);

                      
                    1\. Du nimmst den URL-Parameter "id". Ok.  
                    2\. trim()? Warum? Packst du zuviele Spaces in den Parameter, die du nicht brauchst? Das klingt etwas nach übertriebener Fehlerkorrektur dort, wo sie in der Sache nicht gerechtfertigt ist.  
                    3\. Das getrimmte Ergebnis (alternativ: direkt die ID) wird durch mysql\_real\_escape\_string() so escaped, dass es direkt in einem SQL-String als quote-begrenzter String verwendet werden kann. Ok.  
                      
                    4\. Jetzt aber prüfst du, ob dieses escapte Ergebnis "numeric" ist. Falsche Stelle. WENN $\_GET['id'] numerisch war, könnte ja trotzdem das Escaping irgendein Zeichen verändert haben, so dass die is\_numeric-Prüfung jetzt irrtümlich fehlschlägt. Diese Prüfung kommt zum falschen Zeitpunkt. Prüfe immer die Originaldaten, die sich im Plaintext-Format befinden.  
                    5\. Wenn is\_numeric() scheitert, erzeugst du einen neuen MD5-Wert von 0. Dieser Wert wird aber nicht escaped. Auch das ist grundsätzlich falsch.  
                      
                    Das Escaping einer spezialisierten Funktion zu überlassen versetzt dich in die Lage, dir selbst keine Gedanken mehr darüber machen zu müssen, ob irgendein Zeichen in deinem String konvertiert, escaped oder sonstwie verändert werden muss. Aber vor allem: Du kannst nicht wissen, ob sich die Liste der zu verändernden Zeichen nicht irgendwann mal ändert. Und du musst es auch nicht wissen, denn dafür gibts ja die Funktion.  
                      
                    Das aber bedeutet, dass du alle deine eigenen Prüffunktionen nur auf die Daten anwenden kannst, die sich als "plain text" in deinen Variablen befinden. Das sind alle die Strings, die nicht escaped wurden.  
                      
                    Alle Strings, die bereits escaped wurden, enthalten potentiell Zeichen, die nicht mehr der Standarddarstellung von "plain text" entsprechen. Deshalb kann man die Funktionen von PHP auf solche Strings nicht mehr anwenden, weil diese als Argument nur "plain text" akzeptieren. Diese Unterscheidung zu machen ist extrem wichtig, weil nur so die korrekte Trennung von "Prüf- und Korrekturphase der Daten" und "Verwendung und Escaping der Daten für den konkreten Kontext" geschehen kann.  
                      
                    Dein Code funktioniert technisch, weil alle Zeichen, die MD5 erzeugt, derzeit sowohl mit als auch ohne MySQL-Escaping identisch sind. Er ist aber sachlich falsch. Er birgt deshalb das Potential, technisch nicht mehr zu funktionieren, wenn du die hier programmierten Prinzipien identisch für einen anderen Datensachverhalt anwendest.  
                      
                    Korrekt wäre:  
                      
                    ~~~php
                      
                    // Standardbelegung der Variablen  
                    $md5 = md5(0); // ist trotzdem fragwürdig, aber vielleicht inhaltlich ja gerechtfertigt  
                    if (is_numeric($_GET['id'])) {  
                      // Wenn die ID in der URL numerisch ist, dann nehmen wir diese  
                      $md5 = $_GET['id'];  
                    }  
                      
                    // SQL bauen, erst hier escapen!  
                    $sql = 'SELECT whatever FROM table WHERE id = "'.mysql_real_escape_string($md5).'"';  
                    
                    

                    Insbesondere der letzte Schritt ist wichtig: Du siehst hier, in dieser einzelnen Zeile, dass $md5 in jedem Fall dem notwendigen Escaping unterzogen wird. Das ist gut! Wenn du das Escaping aus der Zeile herausziehst und woanders reinschreibst, dann passiert technisch natürlich dasselbe. Aber du siehst nicht mehr auf den ersten Blick beim Betrachten der SQL-Zeile, ob für die Variable schon Escaping durchgeführt wurde:

                      
                    $sql = 'SELECT whatever FROM table WHERE id = "'.$md5.'"';  
                    
                    

                    Und das ist schlecht! Wenn man erst aufwendig weiteren Code lesen muss, um herauszufinden, ob eine Variable korrekt behandelt wurde, dann verursacht das Fehler - weil man die fehlerhaften Stellen nicht sofort erkennt oder unterscheiden kann von den korrekten Stellen.

                    - Sven Rautenberg

                    1. Werde noch mal alles durchgehen und mit "mysql_real_escape_string" sichern, direkt beim zugreifen auf die DB.

      2. Hi!

        [Sat Sep 03 08:28:47 2011] [warn] [client 83.216.237.188] mod_fcgid: stderr: ALERT - script tried to increase memory_limit to 1073741824 bytes which is above the allowed value (attacker '**.***.***.***', file '/var/www/web2561/html/diamonddog/download2.php', line 47)

        Zeile 47? Soviel Zeilen hat der gezeigte Ausschnitt nicht. Und readfile() arbeitet genauso, wie du das dann nachgebildet hast: es reicht die Datei in kleinen Häppchen an die Ausgabe weiter.

        Wozu brauchst du 1G Speicher?
        Hatte irgendwo gelesen das große Files mehr Speicher benötigen würden.

        Ja, wenn man sie komplett liest. Tust du in dem gezeigten Code nicht.

        Lo!

  2. große Dateien sind Dateien > 500 MB. Der Rest ist Mumpitz!

    1. Hi!

      große Dateien sind Dateien > 500 MB. Der Rest ist Mumpitz!

      Es geht hier um ein Zeit-Problem. Dabei spielt die tatsächliche Größe nur eine geringe Rolle. Wenn die Übertragungsrate zu gering ist, trifft das auch nach deiner Kategorisierung kleine Dateien.

      Lo!