Seite 1 von 1

QHttp Upload/Download Authentifizierung/Passwort-Schutz

Verfasst: 11. Januar 2009 02:08
von FaS
Es geht um ein Updater-Programm, welcher Updates herunterladen muss und einen UpdateManager, welcher Updates bereitstellen soll.

Derzeit habe ich das mit einer FTP-Verbindung gelöst, wobei ich jedoch 2 Probleme habe:
1. Wie soll ich den UpdateManager authentifizieren (dieser muss schreibend auf das Update-Verzeichnis zugreifen können, die Benutzer der zu aktualisierenden Software jedoch natürlich nicht)? Ich kenne mich mit FTP-Servern nicht sonderlich aus, ich schätze man müsste verschiedene Benutzer anlegen und legitimierte per symlink auf die Updateordner zugreifen lassen.. Ist mir irgendwie zu umständlich und außerdem ist FTP nicht sicher bzgl. der Login-Daten.
2. FTP ist lahm. Zumindest wenn ich erstmal lediglich eine Versionsdatei herunterladen möchte. Aber irgendwie scheint Qt auch langsamer zu sein als Firefox, dumm..

Also HTTP und HTTPS. Für den Daten-Download könnte man ja vielleicht zu FTP wechseln, wüsste jetzt allerdings auch nicht was mir das bringen sollte.
Ist es denn generell erstmal unempfehlenswert Dateien per HTTP-POST hochzuladen (binäre Dateien, ggf. mehrere MB)?

Nun die Hauptproblematik: Wie mache ich das mit der Authentifizierung? Cookies? Wie soll ein zustandsloses PHP-Script das verwalten? Hab was von einem Serverseitig erzeugten Schlüssel gelesen, den der Client in einem Cookie immer mitschleppen sollte. Soll das PHP-Script sich nun jeden Schlüssel merken, den es für die Clients generiert, geht irgendwie nur mit Datenbanken, und das nur um den Down/Uploader zu legalisieren? Reicht es sicherheitstechnisch einfach Benutzername und Passwort in einem Cookie mitzusenden? Und wie kann sowas bei GET-Befehlen ausgewertet werden? Oder kann man ein POST-Befehl senden und bekommt als Antwort ggf. die Dateien? Und wie sieht das dann beim Upload aus?

Oder einfach: Wie macht man sowas?

Und wozu gibts eigentlich QNetworkAccessManager? sieht wie ein eingeschränktes QHttp aus, versteh nicht ganz den Sinn seiner Existenz..

Gruß,
FaS

Verfasst: 11. Januar 2009 20:51
von ganwell
Die Doku im QHttp sagt:
This class provides a direct interface to HTTP that allows you to have more control over the requests and that allows you to access the response header fields. However, for new applications, it is recommended to use QNetworkAccessManager and QNetworkReply, as those classes possess a simpler, yet more powerful API.
QHttp ist direkter zu bedienen als QNetworkAccessManager, kann aber weniger. Das heisst QNetworkAccessManager ist Dein Freund:

* Du kannst über den CookieJar auf Cookies zugreifen.
* Du kannst über Signals Authentizieren, SslErrors auflösen
* Du kannst auf die Headers zugreifen

Neue Programme sollten QNetworkAccessManager verwenden.

Das Beispiel zeigt schon einiges:

Code: Alles auswählen

 
QNetworkRequest request;
 request.setUrl("http://www.trolltech.com");
 request.setRawHeader("User-Agent", "MyOwnBrowser 1.0");

 QNetworkReply *reply = manager->get(request);
 connect(reply, SIGNAL(readyRead()), this, SLOT(slotReadyRead()));
 connect(reply, SIGNAL(error(QNetworkReply::NetworkError)),
         this, SLOT(slotError(QNetworkReply::NetworkError)));
 connect(reply, SIGNAL(sslErrors(QList<QSslError>)),
         this, SLOT(slotSslErrors(QList<QSslError>)));

Das meiste geht auch über QHttp. Du müsstest aber die Cookies selbst aus den Headern holen.

http://www.ietf.org/rfc/rfc2109.txt

Gruss
Ganwell

Verfasst: 11. Januar 2009 22:00
von FaS
Danke ganwell, hab ich überlesen. Damit wäre meine Nebenfrage geklärt ;-).
Es geht mir nun primär nicht um die technische Umsetzung, sondern der theoretischen Vorgehensweise für die erforderliche Authentifizierung für HTTP-Down- und Uploads. Dieser Authentication-Kram in Qt ist ja nur für "normalen" (globalen) Verzeichnisschutz, wie man ihn z.B. mit .htpasswd-Dateien erreichen kann, oder? Für meinen Fall soll es aber möglich sein, ohne (oder mit anderem Passwort) herunterzuladen und nur mit einem speziellen Passwort hochzuladen.

Danke schonmal,
FaS

Verfasst: 11. Januar 2009 22:43
von ganwell
Dein Serverprogramm muss einfach Authentikation unterstützen. Du kannst das über .htaccess im Apache reichen, Regeln definieren: wer wann wie authentifiziert wird. Das ist viel mehr als "globaler Verzeichnisschutz".

Du kannst das aber auch in php (oder sonst was) programmieren:

http://www.php.net/manual/en/features.http-auth.php

Grundsätzlich sendet man (oder eben Apache):

header('HTTP/1.0 401 Unauthorized');

( http://www.ietf.org/rfc/rfc2617.txt )

bis man mit dem User/Passwort im Request zufrieden ist.

Qt löst dann das authenticationRequired Signal aus, im entsprechenden Slot kann man User/Password zur Verfügung stellen.

Viele Webapps lösen das über ein Formular/Cookies, was eigentlich total unnötig ist. Mit http-auth kannst Du imo alles genauso erreichen, halt nur ein kein selbstgestaltetes Webformular. Aber wenn Dein Client Qt ist, macht das nichts aus.

Im Netz findest Du eine Trilliarde Examples und Tutorials zu dem Thema. Qt kann grundsätzlich jede gebräuchliche Authentifikationsmethode bedienen. Such Dir auf Deinem Server eine aus und schreibe das Clientprogramm entsprechend.

Und hochladen mit POST ist ok, Du bruchst einfach ein wenig Code auf dem Server, der die Daten in eine Datei speichert. Ich würde wahrscheinlich WebDAV im Apache aktivieren und versuchen die PUT Methode statt POST zu verwenden.

http://www.w3.org/Protocols/rfc2616/rfc ... tml#sec9.6

Gruss
Ganwell

Verfasst: 12. Januar 2009 12:12
von RHBaum
Es bieten sich hier natuerlich mehrere Wege an, fuer welche du dich entscheidest ... kommt auf ide umstaende an.

Nen FTP server kann man schon so einrichten, dass ein bestimmter User (Usergruppe) nur schreib und leserechte hat, und eine anderer User(Usergruppe) nur leserechte. Iss halt nur ned ganz trivial und von ftpserver zu ftpserver mehr oder weniger umstaendlich ....

man sollt auch bedenken, ftp ist ned verschluesselt, d.h. man kann das ausspionieren. sftp oder ftp über ssl ist ned wirklich gut standardisiert. gibts gleich wieder mehrere inkompatible varianten.

http kann eigentlich nur lesen, schreiben geht nur umstaendlich ueber eigene erweiterungen mittels post ....

standardisierter ist da dav (webdav)
wenn man sich mit dem apache ausgennt, kann man die tollsten dinge machen ....
z.b. nen normales user downloadverzeichniss, wo die daten per normales http unverschluesselt, oder verschluesselt ueber https mit authentifizierung zum downloaden zu gehen, ohne das man da schreiben kann ....
und ne andere url die verschluesseltes webdav zur verfuegung stellt, die dem (damin)user schreibenden und lesenden zugriff auf das selbe verzeichniss aufn server zur verfuegung stellt.

Es gibt viele moeglichkeiten :
Wie mache ich das mit der Authentifizierung? Cookies? Wie soll ein zustandsloses PHP-Script das verwalten?
DIe frage ist zuerst eher:
Brauchst du den zugriff auf die daten ueberhaupt ueber ein standardisiertes Protokoll ? Macht nur sinn, wenn der normale user und oder der admin die daten erreichen /verwalten koennen muessen, ohne dein Programm zu nutzen.

Soll der zugriff prinzipiell nur ueber dein Programm funktionieren, wuerd ich mir das ganze da gar ned antun ... sondern die daten inklusive filestruktur in ne Datenbank hauen, da ersparst dir viel Aerger. Und nen RDBMS ist eigentlich genau darauf spezialisiert ....

Ciao ....

Verfasst: 12. Januar 2009 17:13
von FaS
ganwell:
Vielen Dank für die nützlichen Infos, werd mir das alles genauer anschauen. Hab garnicht daran gedacht, dass die Authentifizierung von PHP aus so quasi während des Abrufens der Seite geschieht, ist natürlich praktisch.

WebDAV hört sich interessant an, aber das ist mir glaubich zu kompliziert für's erste.. Außerdem ist es besser, wenn mein update system nicht auf bestimmte Server-Konfigurationen angewiesen ist. Könnte man später vielleicht zusätzlich einbauen, genauso wie ich FTP wohl optional drin lassen werde.

RHBaum:
http kann eigentlich nur lesen, schreiben geht nur umstaendlich ueber eigene erweiterungen mittels post ....
Naja einfach ein php-Script, oder? Ich werd das mal ausprobieren.
Brauchst du den zugriff auf die daten ueberhaupt ueber ein standardisiertes Protokoll ? Macht nur sinn, wenn der normale user und oder der admin die daten erreichen /verwalten koennen muessen, ohne dein Programm zu nutzen.
Man soll zumindest theoretisch auch die Möglichkeit haben, selber einfach, über eigene Scripte oder was auch immer, Updates zu publizieren. Technisch muss dazu nur eine Änderungssequenz an das update script angehangen werden und die neuen Dateien in den Updateordner verfrachtet werden. Alles andere erledigt das clientseitige Updateprogramm. Daher macht es keinen Sinn ein kompliziertes Backend zu schaffen.
Aber da fällt mir ein, die Dateien müssen ja auch gelöscht werden können.. Brauch ich wohl noch ein PHP-Script. Irgendwie merkwürdig, wenn ein Programm PHP-Seiten aufrufen muss..

Danke ihr beiden, ich werd schauen, wie weit ich komme..
FaS

Verfasst: 12. Januar 2009 17:43
von RHBaum
Daher macht es keinen Sinn ein kompliziertes Backend zu schaffen.
Tja, das ist die frage:

Also wenn ich es richtig verstanden hab:

willst du nen script schreiben, wo der user irgendwass updaten kann auf deiner HP ...
Also er quasi phpscript.php << myupadate.xyz aufruft, und das script auf nen "doofen" ftp oder url zugriff ... die aktuelle version in nen eigenes Verzeichniss sichert, und dann auf die position die neue version druebergebuegelt wird ?

Damit die updates ned verloren gehen, willst du dass alle Programmen und die eigegen scripts deiner user ned direkt auf die URL schreiben sondern dein Script verwenden und entsprechend der Syntax aufrufen.

Wenn das Dein Plan ist muss ich sagen : du hasst vertrauen zu den Usern, hoffentlich enttaeuschen sie dich ned ^^

wie siehts mit Kollisionen aus ? wird es nie den Fall geben, das 2 User gleichzeitig nen update fahren wollen ? ist da sichergestellt, das die sich nie ins gehege kommen ???


Brauch ich wohl noch ein PHP-Script. Irgendwie merkwürdig, wenn ein Programm PHP-Seiten aufrufen muss..
Ja schon, das wird immer wie ne Bastelloesung aussehen ....

Du kannst aber das, was das clientseitige Updateprogramm (php) natuerlich auch in ne compilersprache in ne bib auslagern ....

diese Bib kann dann dein Hauptprogramm verwenden, um auf updates zu checken und ggf. zu holen ... sowie selber updates raufzuspielen ...

zusaetzlich kannst nen consolenprogramm schreiben was die selbe bib anzieht und mit dem du mit hilfe von aufrufparametern das update und das holen das aktuellen standes anfordern kannst ....

ala myclientprog --update --file newversion.txt --user=test --pass=test2
das koennten die meisten scriptsprachen bedienen ....

Ciao ....

Verfasst: 12. Januar 2009 18:33
von FaS
Also das Ziel ist ganz einfaches folgendes:

Ein universelles Update-Programm (bereits fertig für FTP), welches für alle möglichen Software-Projekte verwendet werden kann. Dieser Updater muss und darf nur lesend auf die Dateien des Updateservers zugreifen können. Niemand außer dieses Programm benötigt diese Download-Funktionalität.

Aber: Jeder, der das Update-System für sein Projekt verwenden möchte, muss in der Lage sein, Updates bereitzustellen. Daher ist es vorteilhaft, wenn die Serveranforderungen gering ausfallen, und wenn sie dies einfach per FTP-Verbindung erledigen könnten. Der Projektinhaber ist also der Einzige, der schreibenden Zugriff haben darf. Zur Vereinfachung will ich ein UpdateManager schreiben, welcher Änderungen (lokal) mehr oder weniger selbst erkennt, die Änderungs-Sequenz-Ergänzung verfasst, neue Dateien 7zippt und alles automatisch hochlädt, bzw. nicht mehr benötigte Dateien löscht. Daher ist der einzige Grund, warum ich HTTP-Uploads benutzen möchte, der, weil Qt kein SFTP kann (und wenn, mir die Rechteverwaltung zu umständlich ist), die Dateien aber mit einer (S)FTP-Verbindung, oder ohne Umstände auf dem Server selbst, manipuliert werden können sollen.

Verfasst: 13. Januar 2009 11:03
von RHBaum
Ahh ok, nu iss scho bissi klarer

Also die rechteverwaltung wuerd ich ausschliesslich nur dem Server ueberlassen. Alles andere waer kontraproduktiv, erweckt den Anschein einer Bastelloesung und wuerde frueher oder spaeter eh untergraben werden ...

Also faellt nen minimum an konfigurationsaufwand serverseitig eh schon an ...

ftp ist ziemlich einfach von der clientseite her zu bedienen. Als ersten ansatz und zum probieren kann man das scho verwenden.

da sowieso fast jeder die updates lesen kann, iss es ned so tragisch wenn die verbindung zum download unverschluesselt ist.

wenn unverschluesselt fuer den upload nen problem ist, musst hier halt weiterentwickeln, klar ...
gibts wieder mehrer moeglichkeiten ....

weiss ned mehr wleches ftp ... sftp oder ftps war nur einkanaliges FTP ueber nen SSL tunnel.
Der entsprechende server legt dir den tunnel an ....
was du machen musst, ist eigentlich nur den tunnel nehmen, per ssl bib entschluesseln, und aufn anderen port legen .... dann kannst mit nem herkoemmlichen ftp programm auf den entsprechenden port aufn lokalen rechner gehen und ftp wie unverschluesselt nutzen, also auch mit QFTP
Alternativ ftp selber implementieren oder QFtp modifizieren und die ssl entschluessung dazwischenhaengen.

https und post ..... damit musst aber die funktionalitaet zum updaten, umkopieren, das update registrieren etc, aufn server legen ... in nen serverseitiges php script z.b.
Haette aber den vorteil, das du am flexibelsten bist was automatismen betrifft. Weil der, der am meisten weis was Sache ist auf dem Server, ist der Server selber ....
musst aber dafuer serverseitig was programmieren.
clientseitig eher nicht trivial, QHttp arbeitet auch nur unverschluesselt.
Selbige optionen wie bei ftp.
wobei nen "http-client" ueber nen QSocket fuer deine zwecke recht schnell implementiert sein sollte. Und statt dem QSocket nimmst ab 4.4 nen QSSLSocket ....


alternative waer webdav ueber https.
Konfigurationsaufwand haelt sich in grenzen
Probleme aber wie bei https ..... iss halt nur das ein filesystem standardisiert ueber https exportierst ....
webdav hat aber den vorteil, das die konfiguration zu 100% aufn user abschieben kannst. weil webdav laesst sich auf allen BS als Filesystem einbinden. Damit reduziert sich fuer das Programm selber der aufwand auf nen einfaches copy ...


nen nen tunnel aufloesen(entschluesseln) und auf nen anderen port legen, sollte mit QSSLSocket aber ned das Problem sein ....

alternativ nach anderen bibliotheken schauen.
fuer webdav ueber https gibs sicher bibliotheken fuer c++
fuer sftp vielleicht auch ...

vielleicht kennt hier wer ausm forum was geeignetes ???

Also ich wuerd erstmal mit unverschluesseltem ftp anfangen ...
und dann die sache vielleicht modular aufbauen. so das angeben(configurieren) kannst ob ftp, sftp https auf welchen kanal (hin oder rueck) verwendet wird.

Ciao ...

Verfasst: 13. Januar 2009 11:27
von FaS
Na libcurl wär wohl eine geeignete Bibliothek. Aber Qt unterstützt doch HTTPS, das sollte doch ausreichen für den Upload? Soll ja eigentlich nur das Passwort sicher übertragen werden. Vielleicht reicht dafür sogar schon einfaches HTTP mit Digest-Authentifizierung? Die Uploads selbst müssen ja nicht geschützt sein, schließlich werden sie selbst eh wieder runtergeladen irgendwann. Aber HTTPS ist schon eingerichtet, ist ja nicht das Problem. Deine anderen Vorschläge sind mir glaube ich etwas zu aufwendig im Moment.

Verfasst: 13. Januar 2009 12:20
von RHBaum
wenn QT https unterstuetzt, wuerd ich webdav nehmen ....

das runterladen iss sowieso nur nen GET
und das raufladen dann nen POST in nem bestimmten format.

Ciao ...

Verfasst: 13. Januar 2009 23:55
von FaS
Also ich möchte das jetzt erstmal ohne webdav machen, auch wegen der höheren Server-Anforderungen, würde ich es verwenden.

Nun habe ich ein paar Fragen zu HTTP-POST.

Zunächst mein derzeitiger POST-Testcode, welcher auf macmans Beispiel basiert, aber QNetworkAccessManager statt QHttp benutzt.
Anmerkung: Mein Beispiel funktioniert im Zusammenspiel mit dem Server.

Code: Alles auswählen

  manager = new QNetworkAccessManager( 0 );
  // [signals/slots]

  QString fileName( "data.txt" );
  QString dirName( "files/" ); // Speicherort auf dem Server
  QFile file( fileName );
  if( !file.exists() || !file.open( QIODevice::ReadOnly ) )
    return;

  QString boundary; // Wozu "---------------------------125282808721657"?
  QNetworkRequest request;
  request.setUrl( QUrl( "http://update.nevees.org/upload.php" ) );
  request.setHeader( QNetworkRequest::ContentTypeHeader, "multipart/form-data boundary=" + boundary );

  bytes = new QByteArray();
  bytes->append( "--" + boundary + '\n' );
  bytes->append( "Content-Disposition: form-data; name=\"userdir\"\n" );
  bytes->append( '\n' + dirName + '\n' );
  bytes->append( "--" + boundary + '\n' );
  bytes->append( "Content-Disposition: form-data; name=\"userfile\"; filename=\"" + fileName + "\"\n" );
  bytes->append( "Content-Length: " + QString::number( file.size() ) + '\n' );
  bytes->append( "Content-Type: binary/octet-stream\n\n" );
  bytes->append( file.readAll() );
  file.close();
  bytes->append( "\n" );
  bytes->append( "--" + boundary + '\n' ); // Wozu "--\n" statt '\n'?

  QNetworkReply *reply = manager->post( request, *bytes );
  // [signals/slots]
<1> Allgemeines zum Aufbau des Requests
(a)
Wozu findet man überall diese Boundaries ("QString boundary" im Beispiel)? Ich habe den String leer gelassen, und der Upload funktioniert trotzdem. Kann doch nicht sein, dass er diese 2 "--" + den restlichen Text braucht, um die Grenzen zu ermitteln? Dann müsste man ja aufpassen, was man für Daten sendet - nicht, dass sie diesen Text enthalten.. Für diese Frage gibt es ja eigentlich Content-Length.
(b)
Am Ende wurde dem Boundary auch noch ein finales "--" angehangen, wozu, wenn's auch ohne geht?

<2> Kernfrage: Große Dateien
Hintergrund: Es ist unschön, eine Datei komplett in den Speicher laden zu müssen, um sie zu versenden.
Lösungsidee: Verwenden von "QNetworkReply *QNetworkAccessManager::post( const QNetworkRequest &request, QIODevice *data )"
Problem: Man muss ja diese ganze Content-Beschreibung anfügen, müsste also zunächst eine neue Datei erstellen, mit diesem Kram drumherum.
Frage: Gibt es also eine bessere Möglichkeit mit POST? Vielleicht mit "Content-Type: binary/octet-stream" im Request-Header oder sowas? Aber wie handelt das dann der Server?

<3> Kleinigkeiten
(a)
Wozu hat QNetworkAccessManager einen optionalen QObject-Parent? Nur um sich von ihm löschen zu lassen??
(b)
Wann lösche ich das vom QNetworkAccessManager zurückgegebene QNetworkReply-Objekt? Zuletzt taucht es im Signal "void QNetworkAccessManager::finished( QNetworkReply *reply )" auf, lösche ich es dort, wird sein "void QNetworkReply::finished()" aber nicht mehr aufgerufen. Muss ich es dort per "delete sender();" löschen? Oder löscht es sich selbst?

Danke,
FaS