Hallo DeluxXx,
hier kurz alle Antworten zu Deinen Fragen:
Ja, SSL/TLS implizit und explizit über
FTP geht mit
Indy und
Indy OpenSSL. Ich habe hier irgendwo die letzten Sicherheitspatches für OpenSSL gepostet. Leider hat mir der angeschrieben Moderator nicht geantwortet, ob es nicht bald mal eine Sicherheitsrubrik geben sollte...
Kurz erklärt (übliche Anwendung):
Explizites FTP (FTPES):
Normaler
FTP Port (21), es wird per "Auth" versucht, den Controlchannel zu verschlüsseln. Datenkanal optional auch. Passwort/Benutzername werden dann verschlüsselt übertragen. Wenn die Verbindung per SSL/TLS und "Auth" vom Server abgelehnt wird, liegt es an Deinen Einstellungen, ob Du trotzdem unverschlüsselt Benutzer/Pass-Authentisierung zulassen willst.
Implizites FTP (FTPS):
FTP Port 990, es wird direkt die verschlüsselte Kommunikation eingeleitet. Auth entfällt. Ist die Verbindung per SSL/TLS nicht möglich, wird i.d.R. der Aufbau abgebrochen.
Umsetzung
-
Indy 9/10 (am besten aktueller
Indy 10 Snapshot wegen diverser ReadTimeouts)
-
Indy OpenSSL (aktuelle Version wegen diverser Sicherheitslücken in den Delphi OpenSSL DLLs bis einschließlich Delphi 2007)
- Etwas Code:
Beispiel
Delphi-Quellcode:
...
uses
// mindestens:
IdFTP, IdSSLOpenSSL,
// besser auch (für FTP RFC ErrorCodes):
IdFTPCommon, IdExplicitTLSClientServerBase, IdReplyRFC, IdFTPList,
// Für korrektes List-Parsing manuell einbinden:
IdAllFTPListParsers,
// Für korrekte Charsets manuell einbinden:
IdCharsets;
...
type MeinForm =
class(TForm)
...
private
function MeinSSLZertifikatChecker(Certificate: TIdX509; AOk: Boolean): Boolean;
// bzw bei Indy 10 Snapshot:
// function MeinSSLZertifikatChecker(Certificate: TIdX509; AOk: Boolean; ADepth: Integer): Boolean;
...
end;
...
function MeinForm.MeinSSLZertifikatChecker(Certificate: TIdX509; AOk: Boolean): Boolean;
// bzw. bei Indy 10 Snapshot:
// function MeinForm.MeinSSLZertifikatChecker(Certificate: TIdX509; AOk: Boolean; ADepth: Integer): Boolean;
begin
// hier kann man das SSL Zertifikat auf Aussteller etc. prüfen...
Result := True;
// muß man aber nicht, wir akzeptieren alle gültigen Zertifikate
end;
...
procedure SSLTest;
var
FIdFTPClient: TIdFTP;
FIdSSLHandler: TIdSSLIOHandlerSocketOpenSSL;
begin
FIdFTPClient := TIdFTP.Create;
FIdSSLHandler := TIdSSLIOHandlerSocketOpenSSL.Create;
try
// jetzt die allgemeinen Settings
FIdFTPClient.Passive := True;
// setzt den Passive-Mode, damit wir auch keine Router/NAT Probleme bekommen
FIdFTPClient.Username := '
TestAnonymous';
FIdFTPClient.Passwort := '
GeInsHeim';
FIdFTPClient.Host := '
ftp.meinserver.de';
// jetzt die SSL/TLS Settings
// 1. Fall: unverschlüsselt, muß nicht extra angegeben werden...
FIdFTPClient.UseTLS := utNoTLSSupport;
FIdFTPClient.DataPortProtection := ftpdpsClear;
// ODER:
// 2. Fall: FTPES (also Port 21 mit Auth SSL/TLS)
FIdSSLHandler.SSLOptions.Method := sslvSSLv23;
FIdSSLHandler.SSLOptions.Mode := sslmClient;
FIdSSLHandler.SSLOptions.VerifyMode := [sslvrfPeer];
// z.B.
FIdSSLHandler.Port := 21;
FIdSSLHandler.PassThrough := False;
// SSL würde übergangen, wenn True
FIdSSLHandler.OnVerifyPeer := MeinSSLZertifikatChecker;
FIdFTPClient.IOHandler := FIdSSLHandler;
FIdFTPClient.UseTLS := utUseExplicitTLS;
FIdFTPClient.AUTHCmd := tAuto;
// wir wissen ja nicht, ob SSL oder TLS
FIdFTPClient.DataPortProtection := ftpdpsPrivate;
// !!! Datenkanal auch verschlüsseln
// ODER:
// 3. Fall: FTPS (also Port 990, kein Auth da direkt verschlüsselt)
FIdSSLHandler.SSLOptions.Method := sslvSSLv23;
FIdSSLHandler.SSLOptions.Mode := sslmClient;
FIdSSLHandler.SSLOptions.VerifyMode := [sslvrfPeer];
// z.B.
FIdSSLHandler.Port := 990;
FIdSSLHandler.PassThrough := False;
// SSL würde übergangen, wenn True
FIdSSLHandler.OnVerifyPeer := MeinSSLZertifikatChecker;
FIdFTPClient.IOHandler := FIdSSLHandler;
FIdFTPClient.UseTLS := utUseImplicitTLS;
// man beachte den Unterschied
FIdFTPClient.AUTHCmd := tAuto;
FIdFTPClient.DataPortProtection := ftpdpsPrivate;
// und nun connecten
FIdFTPClient.Connect;
// nun z.B. Verzeichnis wechseln:
FIdFTPClient.ChangeDir('
/Test');
// und disconnecten
FIdFTPClient.Disconnect;
finally
FIdSSLHandler.Free;
FIdFTPClient.Free;
end;
end;
Zusätzlich gibt es tolle Möglichkeiten, insbesondere die
RFC Fehler zu prüfen. Nicht alles, was
Indy als
Exception "Raised" ist per se ein Fehler. Es geht auch um das korrekte Handling von
RFC Rückgabewerten.
Beispiel Verzeichniswechsel
Delphi-Quellcode:
function MeinDirExists(ADirname:
String): Boolean;
begin
Result := False;
try
FIdFTPClient.ChangeDir(ADirname);
Result := True;
except
On E:EIdReplyRFCError
do
begin
if E.ErrorCode <> 550
then // 550 ist der RFC Code für "CWD failed"
Raise;
end;
end;
end;
Hier wird also geprüft, ob es das Verzeichnis gibt. Bei einem
RFC-konformen Server kommt der
RFC Code 550 nach Changedir wenn es das Verzeichnis nicht gibt bzw. ein Wechsel nicht möglich ist.
Wir "Raisen" also alles -AUßER- 550. Dann wissen wir per Result, es gibt kein Dir mit dem Namen.
Unicode
Weiterhin kann man
Indy sogar auf
Unicode-Filenamen biegen:
z.B. bei einem
Unicode-Filenamen im Upload:
Delphi-Quellcode:
var
UniFilename: WideString;
...
begin
...
UniFilename := '
MeinUnicodeName.Local';
// ja, ist jetzt kein echtes Unicode ;)
FIdFTPClient.Put(UniFilename, WideStringToUTF8(UniFilename));
// also lokaler name = remote name, bitte Verzeichnisse etc. vorher rausfiltern
...
end;
Bei
Unicode gibt es natürlich noch mehr zu beachten. Aber als grobes Beispiel: Es geht!
Das sollte als kleines Tutorial erst mal reichen.
Gruß winkel79