Creare un provider di autenticazione FTP con restrizioni IP dinamiche
Microsoft ha creato un nuovo servizio FTP completamente riscritto per Windows Server® 2008. Questo nuovo servizio FTP incorpora molte nuove funzionalità che consentono agli autori Web di pubblicare il contenuto più facilmente rispetto a prima e offre agli amministratori Web più opzioni di sicurezza e distribuzione.
Il nuovo servizio FTP 7.5 supporta l'estendibilità che consente di estendere la funzionalità predefinita inclusa nel servizio FTP. In particolare, FTP 7.5 supporta la creazione di provider di autenticazione personalizzati. È anche possibile creare provider per la registrazione FTP personalizzata e determinare le informazioni della home directory per gli utenti FTP.
Questa procedura dettagliata illustra la procedura per usare il codice gestito a un provider di autenticazione FTP che fornisce il supporto per restrizioni IP dinamiche che usano un database di SQL Server per archiviare le informazioni sull'account. Questo provider implementa questa logica registrando il numero di errori dagli indirizzi IP remoti e quindi usando queste informazioni per bloccare gli indirizzi IP che non riescono ad accedere al server entro un determinato intervallo di tempo.
Importante
Per usare il provider in questa procedura dettagliata, è necessario installare la versione più recente del servizio FTP 7.5. Una versione FTP 7.5 è stata rilasciata il 3 agosto 2009 che ha risolto un problema per cui gli indirizzi IP locali e remoti nel metodo IFtpLogProvider.Log() non erano corretti. Per questo motivo, l'uso di una versione precedente del servizio FTP impedirà al provider di funzionare.
Prerequisiti
Per completare le procedure descritte in questo articolo sono necessari gli elementi seguenti:
IIS 7.0 o versione successiva deve essere installato nel server Windows Server 2008 e deve essere installato anche Gestione Internet Information Services (IIS).
È necessario installare il nuovo servizio FTP 7.5.
Importante
Come accennato in precedenza in questa procedura dettagliata, è necessario installare la versione più recente del servizio FTP 7.5 per usare il provider in questa procedura dettagliata. Una versione FTP 7.5 è stata rilasciata il 3 agosto 2009 che ha risolto un problema per cui gli indirizzi IP locali e remoti nel metodo IFtpLogProvider.Log() non erano corretti. Per questo motivo, l'uso di una versione precedente del servizio FTP impedirà al provider di funzionare.
È necessario che la pubblicazione FTP sia abilitata per un sito.
È necessario usare Visual Studio 2008.
Nota
Se si usa una versione precedente di Visual Studio, alcuni dei passaggi di questa procedura dettagliata potrebbero non essere corretti.
È necessario usare un database di SQL Server per l'elenco di account utente e gli elenchi di restrizioni associati; Questo esempio non può essere usato con l'autenticazione FTP Basic. La sezione "Informazioni aggiuntive" di questa procedura dettagliata contiene uno script per SQL Server che crea le tabelle necessarie per questo esempio.
Sarà necessario Gacutil.exe nel computer IIS; è necessario per aggiungere gli assembly alla Global Assembly Cache (GAC).
Importante
Per migliorare le prestazioni per le richieste di autenticazione, il servizio FTP memorizza nella cache le credenziali per gli accessi riusciti per 15 minuti per impostazione predefinita. Questo provider di autenticazione negherà immediatamente le richieste da un utente malintenzionato, ma se l'utente malintenzionato è riuscito a indovinare correttamente la password per un utente che ha effettuato l'accesso di recente, potrebbe ottenere l'accesso tramite le credenziali memorizzate nella cache. Ciò potrebbe avere la conseguenza involontaria di consentire a un utente malintenzionato di attaccare il server dopo che questo provider ha bloccato il proprio indirizzo IP. Per alleviare questo potenziale rischio di attacco, è necessario disabilitare la memorizzazione nella cache delle credenziali per il servizio FTP. A tale scopo, seguire questa procedura:
Apri un prompt dei comandi.
Digitare i comandi seguenti:
cd /d "%SystemRoot%\System32\Inetsrv" Appcmd.exe set config -section:system.ftpServer/caching /credentialsCache.enabled:"False" /commit:apphost Net stop FTPSVC Net start FTPSVC
Chiudere il prompt dei comandi.
Dopo aver apportato queste modifiche, il provider di autenticazione in questo esempio sarà in grado di negare immediatamente tutte le richieste da un potenziale utente malintenzionato.
Descrizione provider
Questa procedura dettagliata contiene diversi punti che richiedono alcune discussioni. Gli attacchi basati su Internet spesso sfruttano un server FTP nel tentativo di ottenere il nome utente e la password per un account in un sistema. Il rilevamento di questo comportamento è possibile tramite l'analisi dei log attività FTP e l'analisi degli indirizzi IP usati per attaccare il sistema e bloccare tali indirizzi dall'accesso futuro. Sfortunatamente, si tratta di un processo manuale e, anche se tale processo è automatizzato, non sarà in tempo reale.
Il servizio FTP contiene una funzionalità per limitare le connessioni in base agli indirizzi IP, ma l'elenco degli indirizzi IP viene archiviato nei file di configurazione IIS e richiede l'accesso amministrativo per l'aggiornamento. Il processo di estendibilità per il servizio FTP viene eseguito come account con privilegi inferiori che non dispone delle autorizzazioni per aggiornare le impostazioni necessarie i file di configurazione IIS. È possibile scrivere un provider di registrazione FTP che rileva l'inondazione del nome utente e scrive tali informazioni in un archivio dati e un servizio separato che viene eseguito come account con privilegi più elevati che può aggiornare i file di configurazione IIS, ma che richiede una conoscenza più approfondita dell'architettura di sistema e richiede una serie di dettagli di implementazione difficili. Per questo motivo, è necessario un archivio dati alternativo.
Un database offre una scelta ideale a causa della facilità di accesso ai dati e della disponibilità generale degli strumenti disponibili per la modifica dei dati in un database. La sfida successiva consiste nell'usare le interfacce di estendibilità FTP esistenti per implementare la logica necessaria per rilevare l'inondazione dell'accesso che un utente malintenzionato userebbe. Per esaminare, le interfacce di estendibilità disponibili sono:
- Interfaccia IFtpAuthenticationProvider
- Interfaccia IFtpHomeDirectoryProvider
- Interfaccia IFtpLogProvider
- Interfaccia IFtpRoleProvider
È possibile scrivere facilmente un provider che sfrutta tutte queste interfacce per rafforzare la sicurezza in modo più elevato, ma il provider in questa procedura dettagliata userà solo le interfacce seguenti:
- IFtpAuthenticationProvider : il provider userà questa interfaccia per consentire o negare l'accesso al server FTP.
- IFtpLogProvider : il provider userà questa interfaccia come listener di eventi generici.
Il servizio FTP non dispone di notifiche di eventi effettive per cui un provider può registrarsi, ma è possibile scrivere provider che usano il metodo IFtpLogProvider.Log() per fornire l'elaborazione post-evento. Ad esempio, eventuali tentativi di accesso non riusciti registrano il comando "PASS" con un codice di stato diverso da "230", ovvero il codice di stato per un accesso FTP riuscito. Acquisendo informazioni aggiuntive sul tentativo di accesso non riuscito, ad esempio l'indirizzo IP del client che non è riuscito ad accedere, è possibile usare queste informazioni per fornire funzionalità aggiuntive, ad esempio impedire agli indirizzi IP di accedere al server FTP in futuro.
Architettura e logica del provider
Le descrizioni seguenti riepilogano il comportamento per questo provider di autenticazione:
Quando si registra il provider nel sistema, specificare la connessione al database da usare e i valori per il numero di tentativi di accesso non riusciti e il timeout di flood nei file di configurazione iis.
Quando il servizio FTP carica il provider, fornisce i valori dei file di configurazione iis al metodo Initialize() del provider. Dopo l'archiviazione di questi valori nelle impostazioni globali, il metodo Initialize() esegue un'operazione iniziale di Garbage Collection per pulire tutte le informazioni delle sessioni FTP precedenti che potrebbero trovarsi nel database.
Quando un client FTP si connette al server FTP, il metodo Log() del provider viene inviato il messaggio "ControlChannelOpened" dal servizio FTP. Il metodo Log() controlla il database per verificare se l'indirizzo IP del client è stato bloccato. In tal caso, contrassegna la sessione nel database.
Quando l'utente immette il nome utente e la password, il servizio FTP chiama il metodo AuthenticateUser() del provider, che verifica se la sessione è contrassegnata. Se la sessione è contrassegnata, il provider restituisce false, che indica che l'utente non è riuscito ad accedere. Se la sessione non è contrassegnata, il nome utente e la password vengono controllati con il database per verificare se sono validi. Se sono validi, il metodo restituisce true, a indicare che l'utente è valido e può eseguire l'accesso.
Se l'utente non riesce a immettere un nome utente e una password validi, il metodo Log() viene chiamato dal servizio FTP e il metodo esegue un'operazione di Garbage Collection periodica per assicurarsi che il numero di errori sia inferiore al timeout di flood. Il metodo verifica quindi se il numero rimanente di errori è minore del numero massimo di errori:
- Se il numero massimo di errori non è stato raggiunto, il metodo aggiunge una notifica di errore per l'indirizzo IP del client al database.
- Se è stato raggiunto il numero massimo di errori, il metodo aggiunge l'indirizzo IP del client all'elenco di indirizzi IP bloccati nel database.
Quando un client FTP si disconnette dal server, il servizio FTP chiama il metodo Log() del provider e invia il messaggio "ControlChannelClosed". Il metodo Log() sfrutta questa notifica per eseguire l'operazione di Garbage Collection per la sessione.
Note aggiuntive
- Questo provider espone la funzionalità per la convalida degli indirizzi IP e degli utenti, ma non fornisce un'implementazione per le ricerche dei ruoli. Detto questo, sarebbe relativamente facile aggiungere una tabella aggiuntiva per i mapping da utente a ruolo e aggiungere il metodo IFtpRoleProvider.IsUserInRole() al provider, ma non rientra nell'ambito di questa procedura dettagliata.
- Questo provider effettua un numero ridotto di chiamate al server di database SQL durante il processo di autenticazione. Tramite il consolidamento di alcune istruzioni SQL in singole query composte o stored procedure, è possibile ridurre ulteriormente il numero di round trip al database, ma non rientra nell'ambito di questa procedura dettagliata.
Passaggio 1: Configurare l'ambiente del progetto
In questo passaggio verrà creato un progetto in Visual Studio 2008 per il provider demo.
Aprire Microsoft Visual Studio 2008.
Fare clic sul menu File , quindi su Nuovo, quindi su Progetto.
Nella finestra di dialogo Nuovo progetto:
- Scegliere Visual C# come tipo di progetto.
- Scegliere Libreria di classi come modello.
- Digitare FtpAddressRestrictionAuthentication come nome del progetto.
- Fare clic su OK.
Quando si apre il progetto, aggiungere un percorso di riferimento alla libreria di estendibilità FTP:
Fare clic su Progetto e quindi su FtpAddressRestrictionAuthentication Properties.Click Project, and then click FtpAddressRestrictionAuthentication Properties.
Fare clic sulla scheda Percorsi di riferimento.
Immettere il percorso dell'assembly di estendibilità FTP per la versione di Windows, dove C: è l'unità del sistema operativo.
Per Windows Server 2008 e Windows Vista:
C:\Windows\assembly\GAC_MSIL\Microsoft.Web.FtpServer\7.5.0.0__31bf3856ad364e35
Per Windows 7:
C:\Program Files\Reference Assemblies\Microsoft\IIS
Fare clic su Aggiungi cartella.
Aggiungere una chiave con nome sicuro al progetto:
- Fare clic su Progetto e quindi su FtpAddressRestrictionAuthentication Properties.Click Project, and then click FtpAddressRestrictionAuthentication Properties.
- Fare clic sulla scheda Firma .
- Selezionare la casella di controllo Firma assembly .
- Scegliere <Nuovo...> dalla casella di riepilogo a discesa Nome chiave complessa.
- Immettere FtpAddressRestrictionAuthenticationKey per il nome del file di chiave.
- Se lo si desidera, immettere una password per il file di chiave; in caso contrario, deselezionare la casella di controllo Proteggi il file di chiave con una password .
- Fare clic su OK.
Facoltativo: è possibile aggiungere un evento di compilazione personalizzato per aggiungere automaticamente la DLL alla Global Assembly Cache (GAC) nel computer di sviluppo:
Fare clic su Progetto e quindi su FtpAddressRestrictionAuthentication Properties.Click Project, and then click FtpAddressRestrictionAuthentication Properties.
Fare clic sulla scheda Eventi di compilazione.
Immettere quanto segue nella finestra di dialogo Riga di comando dell'evento post-compilazione:
net stop ftpsvc call "%VS90COMNTOOLS%\vsvars32.bat">null gacutil.exe /if "$(TargetPath)" net start ftpsvc
Salvare il progetto.
Passaggio 2: Creare la classe Extensibility
In questo passaggio si implementerà l'interfaccia di estendibilità della registrazione per il provider demo.
Aggiungere un riferimento alla libreria di estendibilità FTP per il progetto:
- Fare clic su Progetto e quindi su Aggiungi riferimento...
- Nella scheda .NET fare clic su Microsoft.Web.FtpServer.
- Fare clic su OK.
Aggiungere un riferimento a System.Web per il progetto:
- Fare clic su Progetto e quindi su Aggiungi riferimento...
- Nella scheda .NET fare clic su System.Web.
- Fare clic su OK.
Aggiungere un riferimento a System.Configuration per il progetto:
- Fare clic su Progetto e quindi su Aggiungi riferimento...
- Nella scheda .NET fare clic su System.Configuration.
- Fare clic su OK.
Aggiungere un riferimento a System.Data per il progetto:
- Fare clic su Progetto e quindi su Aggiungi riferimento.
- Nella scheda .NET fare clic su System.Data.
- Fare clic su OK.
Aggiungere il codice per la classe di autenticazione:
In Esplora soluzioni fare doppio clic sul file Class1.cs.
Rimuovere il codice esistente.
Incollare il codice seguente nell'editor:
using System; using System.Collections.Generic; using System.Collections.Specialized; using System.Configuration.Provider; using System.Data; using System.Data.SqlClient; using System.Text; using Microsoft.Web.FtpServer; public class FtpAddressRestrictionAuthentication : BaseProvider, IFtpLogProvider, IFtpAuthenticationProvider { // Define the default values - these are only // used if the configuration settings are not set. const int defaultLogonAttempts = 5; const int defaultFloodSeconds = 30; // Define a connection string with no default. private static string _connectionString; // Initialize the private variables with the default values. private static int _logonAttempts = defaultLogonAttempts; private static int _floodSeconds = defaultFloodSeconds; // Flag the application as uninitialized. private static bool _initialized = false; // Define a list that will contain the list of flagged sessions. private static List<string> _flaggedSessions; // Initialize the provider. protected override void Initialize(StringDictionary config) { // Test if the application has already been initialized. if (_initialized == false) { // Create the flagged sessions list. _flaggedSessions = new List<string>(); // Retrieve the connection string for the database connection. _connectionString = config["connectionString"]; if (string.IsNullOrEmpty(_connectionString)) { // Raise an exception if the connection string is missing or empty. throw new ArgumentException( "Missing connectionString value in configuration."); } else { // Determine whether the database is a Microsoft Access database. if (_connectionString.Contains("Microsoft.Jet")) { // Throw an exception if the database is a Microsoft Access database. throw new ProviderException("Microsoft Access databases are not supported."); } } // Retrieve the number of failures before an IP // address is locked out - or use the default value. if (int.TryParse(config["logonAttempts"], out _logonAttempts) == false) { // Set to the default if the number of logon attempts is not valid. _logonAttempts = defaultLogonAttempts; } // Retrieve the number of seconds for flood // prevention - or use the default value. if (int.TryParse(config["floodSeconds"], out _floodSeconds) == false) { // Set to the default if the number of logon attempts is not valid. _floodSeconds = defaultFloodSeconds; } // Test if the number is a positive integer and less than 10 minutes. if ((_floodSeconds <= 0) || (_floodSeconds > 600)) { // Set to the default if the number of logon attempts is not valid. _floodSeconds = defaultFloodSeconds; } // Initial garbage collection. GarbageCollection(true); // Flag the provider as initialized. _initialized = true; } } // Dispose of the provider. protected override void Dispose(bool disposing) { base.Dispose(disposing); // Test if the application has already been uninitialized. if (_initialized == true) { // Final garbage collection. GarbageCollection(true); // Flag the provider as uninitialized. _initialized = false; } } // Authenticate a user. bool IFtpAuthenticationProvider.AuthenticateUser( string sessionId, string siteName, string userName, string userPassword, out string canonicalUserName) { // Define the canonical user name. canonicalUserName = userName; // Check if the session is flagged. if (IsSessionFlagged(sessionId) == true) { // Return false (authentication failed) if the session is flagged. return false; } // Check the user credentials and return the status. return IsValidUser(userName, userPassword); } // Implement custom actions by using the Log() method. void IFtpLogProvider.Log(FtpLogEntry loggingParameters) { // Test if the control channel was opened or the USER command was sent. if ((String.Compare(loggingParameters.Command, "ControlChannelOpened", true) == 0) || (String.Compare(loggingParameters.Command, "USER", true) == 0)) { // Check if the IP address is banned. if (IsAddressBanned(loggingParameters.RemoteIPAddress) == true) { // If the IP is banned, flag the session. FlagSession(loggingParameters.SessionId); return; } } // Test if the PASS command was sent. if (String.Compare(loggingParameters.Command, "PASS", true) == 0) { // Check for password failures (230 is a success). if (loggingParameters.FtpStatus != 230) { // Periodic garbage collection - remove authentication // failures that are older than the flood timeout. GarbageCollection(false); // Test if the existing number of failures exceeds the maximum logon attempts. if (GetRecordCountByCriteria("[Failures]", "[IPAddress]='" + loggingParameters.RemoteIPAddress + "'") < _logonAttempts) { // Add the failure to the list of failures. InsertDataIntoTable("[Failures]", "[IPAddress],[FailureDateTime]", "'" + loggingParameters.RemoteIPAddress + "','" + DateTime.Now.ToString() + "'"); } else { // Ban the IP address if authentication has failed // from that IP more than the defined number of failures. BanAddress(loggingParameters.RemoteIPAddress); FlagSession(loggingParameters.SessionId); } return; } } // Test if the control channel was closed. if (String.Compare(loggingParameters.Command, "ControlChannelClosed", true) == 0) { // Session-based garbage collection - remove the // current session from the list of flagged sessions. _flaggedSessions.Remove(loggingParameters.SessionId); return; } } // Check for a valid username/password. private static bool IsValidUser( string userName, string userPassword) { // Define the initial status as the credentials are not valid. try { // Create a new SQL connection object. using (SqlConnection connection = new SqlConnection(_connectionString)) { // Create a new SQL command object. using (SqlCommand command = new SqlCommand()) { // Specify the connection for the command object. command.Connection = connection; // Specify a text command type. command.CommandType = CommandType.Text; // Specify the SQL text for the command object. command.CommandText = "SELECT COUNT(*) AS [NumRecords] " + "FROM [Users] WHERE [UID]=@UID AND [PWD]=@PWD AND [Locked]=0"; // Add parameters for the user name and password. command.Parameters.Add("@UID", SqlDbType.NVarChar).Value = userName; command.Parameters.Add("@PWD", SqlDbType.NVarChar).Value = userPassword; // Open the database connection. connection.Open(); // Return the valid status for the credentials. return ((int)command.ExecuteScalar() > 0); } } } catch (Exception ex) { // Raise an exception if an error occurs. throw new ProviderException(ex.Message); } } // Check if the IP is banned. private bool IsAddressBanned(string ipAddress) { // Return whether the IP address was found in the banned addresses table. return (GetRecordCountByCriteria("[BannedAddresses]", "[IPAddress]='" + ipAddress + "'") != 0); } // Check if the session is flagged. private bool IsSessionFlagged(string sessionId) { // Return whether the session ID was found in the flagged sessions table. return _flaggedSessions.Contains(sessionId); } // Mark a session as flagged. private void FlagSession(string sessionId) { // Check if the session is already flagged. if (IsSessionFlagged(sessionId) == false) { // Flag the session if it is not already flagged. _flaggedSessions.Add(sessionId); } } // Mark an IP address as banned. private void BanAddress(string ipAddress) { // Check if the IP address is already banned. if (IsAddressBanned(ipAddress) == false) { // Ban the IP address if it is not already banned. InsertDataIntoTable("[BannedAddresses]", "[IPAddress]", "'" + ipAddress + "'"); } } // Perform garbage collection tasks. private void GarbageCollection(bool deleteSessions) { // Remove any authentication failures that are older than the flood timeout. DeleteRecordsByCriteria("[Failures]", String.Format("DATEDIFF(second,[FailureDateTime],'{0}')>{1}", DateTime.Now.ToString(),_floodSeconds.ToString())); // Test if flagged sessions should be deleted. if (deleteSessions == true) { // Remove any sessions from the list of flagged sessions. _flaggedSessions.Clear(); } } // Retrieve the count of records based on definable criteria. private int GetRecordCountByCriteria( string tableName, string criteria) { // Create a SQL string to retrieve the count of records // that are found in a table based on the criteria. StringBuilder sqlString = new StringBuilder(); sqlString.Append("SELECT COUNT(*) AS [NumRecords]"); sqlString.Append(String.Format( " FROM {0}",tableName)); sqlString.Append(String.Format( " WHERE {0}",criteria)); // Execute the query. return ExecuteQuery(true, sqlString.ToString()); } // Insert records into a database table. private void InsertDataIntoTable( string tableName, string fieldNames, string fieldValues) { // Create a SQL string to insert data into a table. StringBuilder sqlString = new StringBuilder(); sqlString.Append(String.Format( "INSERT INTO {0}",tableName)); sqlString.Append(String.Format( "({0}) VALUES({1})",fieldNames, fieldValues)); // Execute the query. ExecuteQuery(false, sqlString.ToString()); } // Remove records from a table based on criteria. private void DeleteRecordsByCriteria( string tableName, string queryCriteria) { // Create a SQL string to delete data from a table. StringBuilder sqlString = new StringBuilder(); sqlString.Append(String.Format( "DELETE FROM {0}",tableName)); // Test if any criteria is specified. if (string.IsNullOrEmpty(queryCriteria) == false) { // Append the criteria to the SQL string. sqlString.Append(String.Format( " WHERE {0}",queryCriteria)); } // Execute the query. ExecuteQuery(false, sqlString.ToString()); } // Execute SQL queries. private int ExecuteQuery(bool returnRecordCount, string sqlQuery) { try { // Create a new SQL connection object. using (SqlConnection connection = new SqlConnection(_connectionString)) { // Create a new SQL command object. using (SqlCommand command = new SqlCommand(sqlQuery, connection)) { // Open the connection. connection.Open(); // Test whether the method should return a record count. if (returnRecordCount == true) { // Run the database query. SqlDataReader dataReader = command.ExecuteReader(); // Test if data reader has returned any rows. if (dataReader.HasRows) { // Read a single row. dataReader.Read(); // Return the number of records. return ((int)dataReader["NumRecords"]); } } else { // Run the database query. command.ExecuteNonQuery(); } } } // Return a zero record count. return 0; } catch (Exception ex) { // Raise an exception if an error occurs. throw new ProviderException(ex.Message); } } }
Salvare e compilare il progetto.
Nota
Se non sono stati usati i passaggi facoltativi per registrare gli assembly nella GAC, sarà necessario copiare manualmente gli assembly nel computer IIS e aggiungere gli assembly alla GAC usando lo strumento Gacutil.exe. Per altre informazioni, vedere Gacutil.exe (Global Assembly Cache Tool) (Strumento Global Assembly Cache, Gacutil.exe).
Passaggio 3: Aggiungere il provider demo a FTP
In questo passaggio si aggiungerà il provider demo al servizio FTP e al sito Web predefinito.
Determinare le informazioni sull'assembly per il provider di estendibilità:
- In Esplora risorse aprire il
C:\Windows\assembly
percorso, dove C: è l'unità del sistema operativo. - Individuare l'assembly FtpAddressRestrictionAuthentication .
- Fare clic con il pulsante destro del mouse sull'assembly e quindi scegliere Proprietà.
- Copiare il valore Culture, ad esempio Neutral.
- Copiare il numero di versione , ad esempio 1.0.0.0.
- Copiare il valore del token di chiave pubblica, ad esempio 426f62526f636b73.
- Fare clic su Annulla.
- In Esplora risorse aprire il
Usando le informazioni dei passaggi precedenti, aggiungere il provider di estendibilità all'elenco globale dei provider FTP e configurare le opzioni per il provider:
Al momento non esiste un'interfaccia utente che consente di aggiungere proprietà per un modulo di autenticazione personalizzata, pertanto è necessario usare la riga di comando seguente:
cd %SystemRoot%\System32\Inetsrv appcmd.exe set config -section:system.ftpServer/providerDefinitions /+"[name='FtpAddressRestrictionAuthentication',type='FtpAddressRestrictionAuthentication,FtpAddressRestrictionAuthentication,version=1.0.0.0,Culture=neutral,PublicKeyToken=426f62526f636b73']" /commit:apphost appcmd.exe set config -section:system.ftpServer/providerDefinitions /+"activation.[name='FtpAddressRestrictionAuthentication']" /commit:apphost appcmd.exe set config -section:system.ftpServer/providerDefinitions /+"activation.[name='FtpAddressRestrictionAuthentication'].[key='connectionString',value='Server=localhost;Database=FtpAuthentication;User ID=FtpLogin;Password=P@ssw0rd']" /commit:apphost appcmd.exe set config -section:system.ftpServer/providerDefinitions /+"activation.[name='FtpAddressRestrictionAuthentication'].[key='logonAttempts',value='5']" /commit:apphost appcmd.exe set config -section:system.ftpServer/providerDefinitions /+"activation.[name='FtpAddressRestrictionAuthentication'].[key='floodSeconds',value='30']" /commit:apphost
Nota
Il stringa di connessione specificato nell'attributo connectionString deve essere un account di accesso valido per il database.
Aggiungere il provider personalizzato a un sito:
Al momento non esiste un'interfaccia utente che consente di aggiungere funzionalità personalizzate a un sito, quindi è necessario usare la riga di comando seguente:
AppCmd.exe set config -section:system.applicationHost/sites /"[name='Default Web Site'].ftpServer.security.authentication.basicAuthentication.enabled:False" /commit:apphost AppCmd.exe set config -section:system.applicationHost/sites /+"[name='Default Web Site'].ftpServer.security.authentication.customAuthentication.providers.[name='FtpAddressRestrictionAuthentication',enabled='True']" /commit:apphost AppCmd set site "Default Web Site" /+ftpServer.customFeatures.providers.[name='FtpAddressRestrictionAuthentication',enabled='true'] /commit:apphost
Nota
Questa sintassi disabilita l'autenticazione DI base FTP ed è importante disabilitare l'autenticazione di base quando si usa questo provider di autenticazione. In caso contrario, quando l'indirizzo IP di un utente malintenzionato è stato bloccato da questo provider di autenticazione, un utente malintenzionato potrebbe comunque essere in grado di attaccare gli account che usano l'autenticazione di base.
Aggiungere una regola di autorizzazione per il provider di autenticazione:
Fare doppio clic su Regole di autorizzazione FTP nella finestra principale.
Fare clic su Aggiungi regola consenti... nel riquadro Azioni .
Selezionare Utenti specificati per l'opzione di accesso.
Immettere un nome utente.
Nota
Il nome utente deve essere immesso nel database all'esterno di questo elenco di passaggi.
Selezionare Lettura e/o Scrittura per l'opzione Autorizzazioni .
Fare clic su OK.
Passaggio 4: Uso del provider con FTP 7.5
Quando i client FTP si connettono al sito FTP, il servizio FTP tenterà di autenticare gli utenti con il provider di autenticazione personalizzato usando account archiviati nel database. Se un client FTP non riesce a eseguire l'autenticazione, il provider tiene traccia dell'indirizzo IP e della data/ora dell'errore nel database. Quando un client FTP non riesce ad accedere da un indirizzo IP specifico per il numero di errori specificati nell'impostazione logonAttempts e entro l'intervallo di tempo specificato nell'impostazione floodSeconds , il provider bloccherà l'accesso all'indirizzo IP al servizio FTP.
Nota
Questo provider di esempio implementa la logica di autenticazione per il servizio FTP, ma non fornisce un modulo di amministrazione per gestire i dati nel database. Ad esempio, non è possibile gestire l'elenco di account utente FTP, indirizzi IP vietati o errori di autenticazione usando questo provider. Per gestire i dati tramite Gestione IIS, è possibile utilizzare Gestione database IIS. Per altre informazioni, vedere l'argomento seguente:
Informazioni aggiuntive
Per creare il database e le tabelle necessarie, è possibile usare lo script SQL seguente per Microsoft SQL Server. Per usare questo script, è necessario aggiornare il nome del database e il percorso dei file di database. In SQL Server si eseguirà lo script in una nuova finestra di query e quindi si creerà un account di accesso al database che verrà usato con il stringa di connessione.
Nota
È possibile modificare lo script SQL per archiviare il database in un percorso diverso da c:\databases
.
/****** Create the FtpAuthentication Database ******/
USE [master]
GO
CREATE DATABASE [FtpAuthentication] ON PRIMARY
( NAME = N'FtpAuthentication', FILENAME = N'c:\databases\FtpAuthentication.mdf' , SIZE = 2048KB , MAXSIZE = UNLIMITED, FILEGROWTH = 1024KB )
LOG ON
( NAME = N'FtpAuthentication_log', FILENAME = N'c:\databases\FtpAuthentication_log.ldf' , SIZE = 1024KB , MAXSIZE = 2048GB , FILEGROWTH = 10%)
COLLATE SQL_Latin1_General_CP1_CI_AS
GO
EXEC dbo.sp_dbcmptlevel @dbname=N'FtpAuthentication', @new_cmptlevel=90
GO
IF (1 = FULLTEXTSERVICEPROPERTY('IsFullTextInstalled'))
begin
EXEC [FtpAuthentication].[dbo].[sp_fulltext_database] @action = 'enable'
end
GO
ALTER DATABASE [FtpAuthentication] SET ANSI_NULL_DEFAULT OFF
GO
ALTER DATABASE [FtpAuthentication] SET ANSI_NULLS OFF
GO
ALTER DATABASE [FtpAuthentication] SET ANSI_PADDING OFF
GO
ALTER DATABASE [FtpAuthentication] SET ANSI_WARNINGS OFF
GO
ALTER DATABASE [FtpAuthentication] SET ARITHABORT OFF
GO
ALTER DATABASE [FtpAuthentication] SET AUTO_CLOSE OFF
GO
ALTER DATABASE [FtpAuthentication] SET AUTO_CREATE_STATISTICS ON
GO
ALTER DATABASE [FtpAuthentication] SET AUTO_SHRINK OFF
GO
ALTER DATABASE [FtpAuthentication] SET AUTO_UPDATE_STATISTICS ON
GO
ALTER DATABASE [FtpAuthentication] SET CURSOR_CLOSE_ON_COMMIT OFF
GO
ALTER DATABASE [FtpAuthentication] SET CURSOR_DEFAULT GLOBAL
GO
ALTER DATABASE [FtpAuthentication] SET CONCAT_NULL_YIELDS_NULL OFF
GO
ALTER DATABASE [FtpAuthentication] SET NUMERIC_ROUNDABORT OFF
GO
ALTER DATABASE [FtpAuthentication] SET QUOTED_IDENTIFIER OFF
GO
ALTER DATABASE [FtpAuthentication] SET RECURSIVE_TRIGGERS OFF
GO
ALTER DATABASE [FtpAuthentication] SET ENABLE_BROKER
GO
ALTER DATABASE [FtpAuthentication] SET AUTO_UPDATE_STATISTICS_ASYNC OFF
GO
ALTER DATABASE [FtpAuthentication] SET DATE_CORRELATION_OPTIMIZATION OFF
GO
ALTER DATABASE [FtpAuthentication] SET TRUSTWORTHY OFF
GO
ALTER DATABASE [FtpAuthentication] SET ALLOW_SNAPSHOT_ISOLATION OFF
GO
ALTER DATABASE [FtpAuthentication] SET PARAMETERIZATION SIMPLE
GO
ALTER DATABASE [FtpAuthentication] SET READ_WRITE
GO
ALTER DATABASE [FtpAuthentication] SET RECOVERY SIMPLE
GO
ALTER DATABASE [FtpAuthentication] SET MULTI_USER
GO
ALTER DATABASE [FtpAuthentication] SET PAGE_VERIFY CHECKSUM
GO
ALTER DATABASE [FtpAuthentication] SET DB_CHAINING OFF
/****** Create the Database Tables ******/
USE [FtpAuthentication]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[BannedAddresses]') AND type in (N'U'))
BEGIN
CREATE TABLE [BannedAddresses](
[IPAddress] [nvarchar](50) NOT NULL
) ON [PRIMARY]
END
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[Failures]') AND type in (N'U'))
BEGIN
CREATE TABLE [Failures](
[IPAddress] [nvarchar](50) NOT NULL,
[FailureDateTime] [datetime] NOT NULL
) ON [PRIMARY]
END
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[Users]') AND type in (N'U'))
BEGIN
CREATE TABLE [Users](
[UID] [nvarchar](50) NOT NULL,
[PWD] [nvarchar](50) NOT NULL,
[Locked] [bit] NOT NULL
) ON [PRIMARY]
END
Riepilogo
In questa procedura dettagliata si è appreso come:
- Creare un progetto in Visual Studio 2008 per un provider FTP personalizzato.
- Implementare le interfacce di estendibilità per un provider FTP personalizzato.
- Aggiungere un provider personalizzato FTP al servizio FTP.
Commenti e suggerimenti
https://aka.ms/ContentUserFeedback.
Presto disponibile: Nel corso del 2024 verranno gradualmente disattivati i problemi di GitHub come meccanismo di feedback per il contenuto e ciò verrà sostituito con un nuovo sistema di feedback. Per altre informazioni, vedereInvia e visualizza il feedback per