# Server absichern

> Anleitung: Den Server absichern: Endpunkte, Zertifikate, Benutzer, Rollen und Zugriffskontrolllisten, Audit-Ereignisse und eine Checkliste für den Betrieb.

**Beispielanwendungen:**

- Server.Authentication ([C#](https://github.com/Traeger-GmbH/opcuanet-samples/tree/master/cs/Basic/Server.Authentication), [VB.NET](https://github.com/Traeger-GmbH/opcuanet-samples/tree/master/vb/Basic/Server.Authentication)): Lässt nur Benutzer zu, die mit Benutzername und Passwort angemeldet sind, und prüft sie über eine Zugriffsliste.
- Server.NodeAccess ([C#](https://github.com/Traeger-GmbH/opcuanet-samples/tree/master/cs/Scenarios/Server.NodeAccess), [VB.NET](https://github.com/Traeger-GmbH/opcuanet-samples/tree/master/vb/Scenarios/Server.NodeAccess)): Beschränkt den Zugriff auf Nodes je Benutzer über Zugriffskontrolllisten und liefert über abgeleitete Node-Klassen benutzerabhängige Metadaten.
- Client.Authentication ([C#](https://github.com/Traeger-GmbH/opcuanet-samples/tree/master/cs/Basic/Client.Authentication), [VB.NET](https://github.com/Traeger-GmbH/opcuanet-samples/tree/master/vb/Basic/Client.Authentication)): Meldet sich mit Benutzername und Passwort am Server an und liest und schreibt einen Node.

Diese Seite bündelt alle Mechanismen, mit denen ein Server sich und seine Daten schützt. Die Standardwerte des OPC UA .NET SDK sind auf einen einfachen Einstieg ausgelegt und für den Produktivbetrieb nicht geeignet. Welche Einstellungen Sie dafür ändern sollten, fasst der Abschnitt [Härtung für den Produktivbetrieb](https://www.traeger.de/docs/development/opcua/security/server#hartung-fur-den-produktivbetrieb) zusammen. Die Grundlagen beschreiben die Seiten [Zertifikate verstehen](https://www.traeger.de/docs/development/opcua/security/certificates) und [Benutzer und Rollen](https://www.traeger.de/docs/development/opcua/security/users-roles).

## Standardwerte im Überblick

Beteiligte Typen: `OpcServer`, `OpcServerSecurity`, `OpcServerBinding`, `OpcCertificateStores`, `OpcAuthenticationLimits`, `OpcOperationLimits`

| Einstellung | Standardwert | Empfehlung für den Produktivbetrieb |
|---|---|---|
| `Security.EndpointPolicies` | leer: Der Server bietet nur Endpunkte mit dem Modus `None` an | ausschließlich Policies mit `SignAndEncrypt` und `Basic256Sha256`, `Aes128_Sha256_RsaOaep` oder `Aes256_Sha256_RsaPss` |
| `Binding.Addresses` | leer: Der Server bindet an alle IPv4- und IPv6-Adressen des Rechners, auch wenn seine Adresse `localhost` lautet | auf die Netzwerkschnittstellen beschränken, über die Clients zugreifen |
| `Security.AutoAcceptUntrustedCertificates` | `true`: Der Server nimmt unbekannte Clientzertifikate an | `false`; Clientzertifikate vorab in den `TrustedPeerStore` aufnehmen |
| `Security.CertificateValidationRules` | `Default`: Der Server nimmt mit SHA-1 signierte Zertifikate an, die rückwärtskompatible Prüfung ist aktiv | `RejectSHA1Signed` und `DisableBackwardsCompatibleValidation` |
| `Security.AnonymousAcl` | aktiv, solange keine andere Zugriffskontrollliste aktiv ist | deaktivieren; nur bekannte Benutzer zulassen |
| `Security.IsAuditingEnabled` | `false`: Der Server meldet keine Audit-Ereignisse | nach Bedarf `true` (siehe [Audit-Ereignisse](https://www.traeger.de/docs/development/opcua/security/server#audit-ereignisse)) |
| `CertificateStores.AutoCreateCertificate` | `true`: Fehlt ein Zertifikat, erzeugt der Server ein neues (bis Version 2.58 auch, wenn das vorhandene ungültig ist) | `false` und ein verwaltetes Zertifikat verwenden |
| Automatisch erzeugtes Zertifikat | selbstsigniert, Schlüssellänge 2048 Bit, gültig für 600 Monate | Zertifikat einer eigenen Zertifizierungsstelle mit einer Laufzeit nach Ihrer Richtlinie |
| `AuthenticationLimits` | aktiv: nach 5 Fehlversuchen innerhalb von 5 Sekunden wachsende Verzögerung ab 1 Sekunde, höchstens 30 Sekunden | an die Zahl der Benutzer anpassen |
| `OperationLimits.AutoDetermineLimits` | `false`: Der Server meldet seinen Clients keine Grenzen je Anfrage | `true` |
| `Transport` | `MaxMessageSize` 10 MB, `MaxByteStringLength` 5 MB, `MaxStringLength` und `MaxArrayLength` je 65535 | nur so weit erhöhen, wie die Anwendung es braucht |

> **Unsichere Standardwerte**
> Mit den Standardwerten ist ein Server über alle Netzwerkschnittstellen des Rechners erreichbar, kommuniziert unverschlüsselt, lässt anonyme Benutzer zu und nimmt das Zertifikat jedes ihm unbekannten Clients an. Wer den Rechner über das Netzwerk erreicht, kann damit Daten lesen, schreiben und Methoden aufrufen. Ändern Sie diese Einstellungen vor dem Produktivbetrieb.

## Endpunkte und Netzwerk absichern

Beteiligte Typen: `OpcServer`, `OpcServerSecurity`, `OpcSecurityPolicy`, `OpcServerBinding`

**Nur verschlüsselte Endpunkte anbieten**: Solange die Eigenschaft `EndpointPolicies` leer ist, bietet der Server ausschließlich Endpunkte mit dem Modus `None` an. Über diese Endpunkte überträgt er Daten weder signiert noch verschlüsselt. Sobald eine Policy eingetragen ist, entfällt der Endpunkt mit dem Modus `None`. Tragen Sie deshalb nur Policies mit dem Modus `SignAndEncrypt` ein und geben Sie den Algorithmus ausdrücklich an. Ohne Angabe (`OpcSecurityAlgorithm.Auto`) wählt das SDK `Basic256Sha256`. Wie der Server Adressen und Policies zu Endpunkten kombiniert, beschreibt der Abschnitt [Endpunkte](https://www.traeger.de/docs/development/opcua/server/configuration#endpunkte).

**TLS-Versionen bei HTTPS beachten**: Ab Version 2.59 überlässt der Server bei Endpunkten mit dem Schema `https` die Wahl der TLS-Versionen dem Betriebssystem. Bis Version 2.58 waren TLS 1.0, 1.1 und 1.2 fest eingestellt. Clients, die nur TLS 1.0 oder 1.1 unterstützen, können sich deshalb über HTTPS möglicherweise nicht mehr verbinden.

**Nur an die benötigten Netzwerkschnittstellen binden**: Die Adresse des Servers (zum Beispiel `opc.tcp://localhost:4840/`) bestimmt die Endpunkte, die der Server seinen Clients meldet. Sie bestimmt nicht die Netzwerkschnittstellen, an die er seine Ports bindet. Ohne weitere Angabe bindet der Server an alle IPv4- und IPv6-Adressen des Rechners. Damit ist er auch dann aus dem Netzwerk erreichbar, wenn seine Adresse `localhost` lautet. Beschränken Sie die Bindung über die Eigenschaft `Binding` auf die benötigten Adressen (siehe [An Netzwerkschnittstellen binden](https://www.traeger.de/docs/development/opcua/server/configuration#an-netzwerkschnittstellen-binden)). Die standardmäßig aktive exklusive Bindung (`UseExclusiveAddress`) verhindert zudem, dass ein anderer Prozess denselben Port belegt.

## Clientzertifikate prüfen und vertrauen

Beteiligte Typen: `OpcServer`, `OpcServerSecurity`, `OpcCertificateValidationFailedEventArgs`, `OpcCertificateValidationRules`, `OpcCertificateStores`, `OpcCertificateStoreInfo`

Ein Client sendet beim Verbindungsaufbau sein Zertifikat zur Authentifizierung an den Server. Anhand des Clientzertifikats entscheidet der Server, ob er die Verbindung mit dem Client zulässt und ihm damit vertraut.

**Nur vertrauenswürdige Zertifikate akzeptieren**: Wenn der Server nur vertrauenswürdige Zertifikate akzeptieren soll, deaktivieren Sie die standardmäßige Akzeptanz aller unbekannten Zertifikate. Abgelaufene, gesperrte oder anderweitig ungültige Zertifikate nimmt der Server auch ohne diese Einstellung nicht automatisch an:

```csharp
server.Security.AutoAcceptUntrustedCertificates = false;

```

```vbnet
server.Security.AutoAcceptUntrustedCertificates = False

```

**Zertifikate selbst prüfen**: Sobald Sie die standardmäßige Akzeptanz aller Zertifikate deaktiviert haben, sollten Sie eine benutzerdefinierte Prüfung der Zertifikate vorsehen:

```csharp
server.CertificateValidationFailed += HandleCertificateValidationFailed;
...
private void HandleCertificateValidationFailed(object sender, OpcCertificateValidationFailedEventArgs e)
{
    if (e.Certificate.Thumbprint == "...")
        e.Accept = true;
}

```

```vbnet
AddHandler server.CertificateValidationFailed, AddressOf HandleCertificateValidationFailed
'...
Private Sub HandleCertificateValidationFailed(sender As Object, e As OpcCertificateValidationFailedEventArgs)
    If e.Certificate.Thumbprint = "..." Then
        e.Accept = True
    End If
End Sub

```

**Zertifikat manuell vertrauen**: Wenn der Server ein Clientzertifikat als nicht vertrauenswürdig eingestuft hat, können Sie es manuell als vertrauenswürdig deklarieren. Speichern Sie es dazu im `TrustedPeerStore`:

```csharp
// In context of the event handler the sender is an OpcServer.
var server = (OpcServer)sender;

if (!server.CertificateStores.TrustedPeerStore.Contains(e.Certificate))
    server.CertificateStores.TrustedPeerStore.Add(e.Certificate);

```

```vbnet
' In context of the event handler the sender is an OpcServer.
Dim server As OpcServer = CType(sender, OpcServer)

If Not server.CertificateStores.TrustedPeerStore.Contains(e.Certificate) Then
    server.CertificateStores.TrustedPeerStore.Add(e.Certificate)
End If

```

**Strengere Regeln für die Prüfung von Zertifikaten** legt die Eigenschaft `CertificateValidationRules` fest. Standardmäßig akzeptiert der Server auch Zertifikate, die mit SHA-1 signiert sind:

```csharp
server.Security.CertificateValidationRules
        = OpcCertificateValidationRules.RejectSHA1Signed
        | OpcCertificateValidationRules.DisableBackwardsCompatibleValidation;
```

```vbnet
server.Security.CertificateValidationRules _
        = OpcCertificateValidationRules.RejectSHA1Signed _
        Or OpcCertificateValidationRules.DisableBackwardsCompatibleValidation
```

## Eigenes Zertifikat schützen

Beteiligte Typen: `OpcServer`, `OpcCertificateManager`, `OpcCertificateSettings`, `OpcCertificateStores`, `OpcCertificateRequestedEventArgs`

Wie ein Zertifikat geladen, erzeugt und gespeichert wird, beschreibt der Abschnitt [Anwendungszertifikat](https://www.traeger.de/docs/development/opcua/server/configuration#anwendungszertifikat). Für die Sicherheit sind dabei die folgenden Punkte wichtig:

- **Eigenschaften des Zertifikats**: Ein vom SDK erzeugtes Zertifikat ist selbstsigniert, verwendet einen Schlüssel mit 2048 Bit und ist 600 Monate gültig. Schlüssellänge (`KeySize`), Gültigkeit in Monaten (`Lifetime`) und die Domänen, unter denen der Server erreichbar ist (`DomainNames`), legen Sie über die Klasse `OpcCertificateSettings` fest. Beim Server ergänzt das SDK die Domänen um den vollqualifizierten Namen des Rechners (FQDN), sofern er sich per DNS auflösen lässt. So nehmen Clients, die sich über diesen Namen verbinden, das Zertifikat an. Als Organisation (O) im Antragsteller (engl. subject) trägt das SDK den Hersteller aus `OpcServerInfo.CompanyName` ein (Eigenschaft `Organization`). Ein bereits vorhandenes Zertifikat ändert es nicht. Für den Produktivbetrieb empfiehlt sich ein Zertifikat, das die Zertifizierungsstelle (engl. certificate authority) Ihres Unternehmens ausgestellt hat. Clients müssen dann nur der Zertifizierungsstelle vertrauen.
- **Kein Zertifikat stillschweigend ersetzen**: Wenn der Server beim Start kein gültiges Zertifikat findet, zum Beispiel weil es abgelaufen ist, löst er das Ereignis `CertificateRequested` aus. Bis Version 2.58 gilt: Wenn kein Handler ein Zertifikat liefert und die Eigenschaft `AutoCreateCertificate` aktiv ist, löscht er das bisherige Zertifikat aus dem `ApplicationStore` und erzeugt ein neues, selbstsigniertes Zertifikat. Clients, die dem bisherigen Zertifikat vertraut haben, lehnen den Server danach ab. Ab Version 2.59 ersetzt der Server ein vorhandenes, aber ungültiges Zertifikat nie automatisch. Die Eigenschaft `Reason` der Ereignisdaten nennt dann den Grund: `Invalid`, `UseNotAllowed`, `Revoked`, `TimeInvalid`, `IssuerUseNotAllowed`, `IssuerRevoked` oder `IssuerTimeInvalid`. Für ein abgelaufenes Zertifikat lautet er zum Beispiel `TimeInvalid`. Die Eigenschaft `AutoCreateCertificate` der Ereignisdaten ist dabei `false`. Wenn der Handler kein anderes Zertifikat bereitstellt, verwendet der Server weiter das vorhandene Zertifikat. Ein Zertifikat, das der Handler über die Eigenschaft `AlternativeCertificate` bereitstellt, verwendet der Server ab Version 2.59 beim Start. Es muss seinen privaten Schlüssel enthalten. Stellen Sie über das Ereignis `CertificateRequested` selbst ein Zertifikat bereit und deaktivieren Sie die automatische Erzeugung.
- **Den privaten Schlüssel schützen**: Der `ApplicationStore` enthält das Zertifikat samt privatem Schlüssel. Das SDK legt für die Dateien der Zertifikatspeicher keine Zugriffsrechte fest. Legen Sie die Zertifikatspeicher in einem Verzeichnis ab, auf das nur das Benutzerkonto des Servers zugreifen darf. Laden Sie ein vorhandenes Zertifikat aus einer durch ein Passwort geschützten `.pfx`-Datei.

## Benutzer anmelden

Beteiligte Typen: `OpcServer`, `OpcUserIdentity`, `OpcServerIdentity`, `OpcCertificateIdentity`, `OpcServerSecurity`, `OpcAccessControlList`, `OpcAnonymousAcl`, `OpcUserNameAcl`, `OpcCertificateAcl`, `OpcAccessControlEntry`, `OpcOperationType`, `OpcRequestType`, `OpcAccessControlMode`

Ein Server erlaubt standardmäßig auch Zugriffe ohne Angabe einer konkreten Benutzeridentität. Diese Art der Benutzerauthentifizierung heißt anonyme Authentifizierung. Wenn ein Client dagegen eine Benutzeridentität angibt, muss der Server diese Identität kennen. Nur dann lässt er den Zugriff mit dieser Identität zu. Wenn sich Benutzer zum Beispiel mit einem Benutzername-Passwort-Paar oder einem Zertifikat ausweisen sollen, konfigurieren und aktivieren Sie die passenden Zugriffskontrolllisten (engl. Access Control List, ACL). Zur Konfiguration der Zugriffskontrolllisten gehört die Konfiguration der Zugriffskontrolleinträge (engl. Access Control Entries, ACE). Jeder Eintrag steht für ein Prinzipal mit einer bestimmten Identität, also Benutzername und Passwort oder ein Zertifikat. Die Einträge tragen Sie in die Listen ein.

Die Zugriffskontrollliste für anonyme Benutzer ist nur so lange aktiv, wie keine andere Zugriffskontrollliste aktiv ist. Wenn Sie also zum Beispiel die Zugriffskontrollliste für Benutzername-Passwort-Paare aktivieren, ist der anonyme Zugriff automatisch abgeschaltet. Wenn er dennoch möglich sein soll, setzen Sie die Eigenschaft `IsEnabled` der `AnonymousAcl` ausdrücklich auf `true`. Umgekehrt schaltet das folgende Beispiel den anonymen Zugriff ausdrücklich ab:

**Anonyme Zugriffskontrollliste deaktivieren**:

```csharp
server.Security.AnonymousAcl.IsEnabled = false;

```

```vbnet
server.Security.AnonymousAcl.IsEnabled = False

```

**Zugriffskontrollliste für Benutzername-Passwort-Paare konfigurieren**:

```csharp
var acl = server.Security.UserNameAcl;

acl.AddEntry("username1", "password1");
acl.AddEntry("username2", "password2");
acl.AddEntry("username3", "password3");
...
acl.IsEnabled = true;

```

```vbnet
Dim acl = server.Security.UserNameAcl

acl.AddEntry("username1", "password1")
acl.AddEntry("username2", "password2")
acl.AddEntry("username3", "password3")
'...
acl.IsEnabled = True

```

**Zugriffskontrollliste für Zertifikate konfigurieren**:

```csharp
var acl = server.Security.CertificateAcl;
  
acl.AddEntry(new X509Certificate2(@".\user1.der"));
acl.AddEntry(new X509Certificate2(@".\user2.der"));
acl.AddEntry(new X509Certificate2(@".\user3.der"));
...
acl.IsEnabled = true;

```

```vbnet
Dim acl = server.Security.CertificateAcl

acl.AddEntry(New X509Certificate2(".\user1.der"))
acl.AddEntry(New X509Certificate2(".\user2.der"))
acl.AddEntry(New X509Certificate2(".\user3.der"))
'...
acl.IsEnabled = True

```

Wenn über die Eigenschaft `CertificateValidator` der `CertificateAcl` ein eigener `X509CertificateValidator` festgelegt ist, prüft der Server das Zertifikat einer Benutzeridentität zusätzlich, bevor er den Fingerabdruck (engl. thumbprint) vergleicht.

Alle Zugriffskontrolllisten, die das SDK bereitstellt, verwenden den Zugriffskontrollmodus (engl. Access Control Mode) `Whitelist`. In diesem Modus hat jeder Eintrag allein dadurch, dass er definiert ist, Zugriff auf alle Arten (engl. Types) von Anfragen (engl. Requests). Das gilt auch, wenn ihm der Zugriff dafür nicht ausdrücklich erteilt (engl. allow) wurde. Entziehen (engl. deny) Sie den Einträgen deshalb alle nicht erlaubten Operationen. Die erlaubten und verbotenen Operationen legen Sie direkt auf dem Eintrag fest, den die Zugriffskontrollliste nach dem Eintragen bereitstellt. Andere Modi, Rollen und eigene Zugriffskontrolllisten beschreiben die Abschnitte [Zugriffskontrollmodi](https://www.traeger.de/docs/development/opcua/security/server#zugriffskontrollmodi), [Rollen](https://www.traeger.de/docs/development/opcua/security/server#rollen) und [Eigene Zugriffskontrolllisten](https://www.traeger.de/docs/development/opcua/security/server#eigene-zugriffskontrolllisten).

**1.** Einen Zugriffskontrolleintrag merken:

```csharp
var user1 = acl.AddEntry("username1", "password1");

```

```vbnet
Dim user1 = acl.AddEntry("username1", "password1")

```

**2.** Dem Zugriffskontrolleintrag zwei Rechte entziehen:

```csharp
user1.Deny(OpcRequestType.Write);
user1.Deny(OpcRequestType.HistoryUpdate);

```

```vbnet
user1.Deny(OpcRequestType.Write)
user1.Deny(OpcRequestType.HistoryUpdate)

```

**3.** Ein zuvor entzogenes Recht wieder erteilen:

```csharp
user1.Allow(OpcRequestType.HistoryUpdate);

```

```vbnet
user1.Allow(OpcRequestType.HistoryUpdate)

```

### Passwörter geschützt ablegen

Beteiligte Typen: `OpcServerIdentity`, `OpcUserNameAcl`

Wenn Sie einen Benutzer mit einem Passwort als Zeichenkette eintragen (`AddEntry(userName, password)`), hält der Server das Passwort im Klartext im Speicher. Wenn die Passwörter Ihrer Benutzer bereits als Hashwerte vorliegen, leiten Sie von der Klasse `OpcServerIdentity` ab. Der Konstruktor übernimmt den gespeicherten Hashwert als Byte-Array. Die überschriebene Methode `TransformPassword` berechnet beim Anmelden aus dem übermittelten Passwort den Hashwert nach demselben Verfahren. Der Server vergleicht die beiden Werte in konstanter Zeit. Das folgende Beispiel verwendet PBKDF2 mit einem Salt je Benutzer:

```csharp
public sealed class HashedServerIdentity : OpcServerIdentity
{
    private const int Iterations = 100000;
    private readonly byte[] salt;
 
    public HashedServerIdentity(string userName, byte[] passwordHash, byte[] salt)
        : base(userName, password: passwordHash)
    {
        this.salt = salt;
    }
 
    public static byte[] Hash(string password, byte[] salt)
    {
        using (var pbkdf2 = new Rfc2898DeriveBytes(
                password,
                salt,
                Iterations,
                HashAlgorithmName.SHA256)) {
            return pbkdf2.GetBytes(32);
        }
    }
 
    protected override byte[] TransformPassword(string password)
    {
        return Hash(password, this.salt);
    }
}
```

```vbnet
Public NotInheritable Class HashedServerIdentity
    Inherits OpcServerIdentity

    Private Const Iterations As Integer = 100000
    Private ReadOnly salt As Byte()

    Public Sub New(ByVal userName As String, ByVal passwordHash As Byte(), ByVal salt As Byte())
        MyBase.New(userName, password:=passwordHash)
        Me.salt = salt
    End Sub

    Public Shared Function Hash(ByVal password As String, ByVal salt As Byte()) As Byte()
        Using pbkdf2 = New Rfc2898DeriveBytes( _
                password, _
                salt, _
                Iterations, _
                HashAlgorithmName.SHA256)
            Return pbkdf2.GetBytes(32)
        End Using
    End Function

    Protected Overrides Function TransformPassword(ByVal password As String) As Byte()
        Return Hash(password, Me.salt)
    End Function
End Class

```

So tragen Sie die Identität ein:

```csharp
// Hash and salt come from the user store of the application.
var identity = new HashedServerIdentity("operator", storedHash, storedSalt);
 
server.Security.UserNameAcl.AddEntry(identity);
server.Security.UserNameAcl.IsEnabled = true;
```

```vbnet
'Hash and salt come from the user store of the application.
Dim identity = New HashedServerIdentity("operator", storedHash, storedSalt)

server.Security.UserNameAcl.AddEntry(identity)
server.Security.UserNameAcl.IsEnabled = True
```

### Benutzerzertifikate prüfen

Beteiligte Typen: `OpcCertificateAcl`

Wenn sich ein Benutzer mit einem Zertifikat anmeldet, vergleicht der Server dessen Fingerabdruck mit den Einträgen der `CertificateAcl`. Wenn der Server das Zertifikat vorher prüfen soll, zum Beispiel gegen die Zertifizierungsstelle Ihres Unternehmens und auf eine Sperrung, legen Sie über die Eigenschaft `CertificateValidator` einen eigenen `X509CertificateValidator` fest (Namespace `System.IdentityModel.Selectors`). Wenn der Validator das Zertifikat mit einer Ausnahme ablehnt, verweigert der Server die Anmeldung:

```csharp
public sealed class UserCertificateValidator : X509CertificateValidator
{
    public override void Validate(X509Certificate2 certificate)
    {
        using (var chain = new X509Chain()) {
            chain.ChainPolicy.RevocationMode = X509RevocationMode.Online;
 
            if (!chain.Build(certificate))
                throw new SecurityException("The user certificate is not trusted.");
        }
    }
}

```

```vbnet
Public NotInheritable Class UserCertificateValidator
    Inherits X509CertificateValidator

    Public Overrides Sub Validate(ByVal certificate As X509Certificate2)
        Using chain = New X509Chain()
            chain.ChainPolicy.RevocationMode = X509RevocationMode.Online

            If Not chain.Build(certificate) Then
                Throw New SecurityException("The user certificate is not trusted.")
            End If
        End Using
    End Sub
End Class
```

```csharp
server.Security.CertificateAcl.CertificateValidator = new UserCertificateValidator();
server.Security.CertificateAcl.AddEntry(userCertificate);
server.Security.CertificateAcl.IsEnabled = true;
```

```vbnet
server.Security.CertificateAcl.CertificateValidator = New UserCertificateValidator()
server.Security.CertificateAcl.AddEntry(userCertificate)
server.Security.CertificateAcl.IsEnabled = True
```

### Benutzer auf Endpunkte beschränken

Beteiligte Typen: `OpcAccessControlEntry`, `OpcEndpointIdentity`, `OpcAccessControlMode`

Neben den Operationen kann ein Zugriffskontrolleintrag auch die Endpunkte festlegen, über die sich ein Benutzer anmelden darf. Wie der Server erteilte (`Enable`) und entzogene (`Disable`) Endpunkte auswertet, bestimmt der Zugriffskontrollmodus für Endpunkte. Sie geben ihn beim Hinzufügen eines Eintrags als zweiten Modus an. Im Modus `Blacklist` darf sich der Benutzer nur über ausdrücklich erteilte Endpunkte anmelden. Über alle anderen Endpunkte verweigert der Server die Anmeldung mit dem Status `BadUserAccessDenied`. So lässt sich zum Beispiel ein Dienstkonto auf einen Endpunkt mit dem Modus `SignAndEncrypt` beschränken. Adresse und Policy der `OpcEndpointIdentity` müssen dabei dem Endpunkt entsprechen, über den sich der Client verbindet:

```csharp
var endpoint = new OpcEndpointIdentity(
        "opc.tcp://192.168.0.10:4840/",
        new OpcSecurityPolicy(OpcSecurityMode.SignAndEncrypt, OpcSecurityAlgorithm.Basic256Sha256));
 
// Endpoint access mode Blacklist: only explicitly enabled endpoints are permitted.
var service = server.Security.UserNameAcl.AddEntry(
        "service",
        servicePassword,
        operationAccessMode: OpcAccessControlMode.Whitelist,
        endpointAccessMode: OpcAccessControlMode.Blacklist);
 
service.Enable(endpoint);
```

```vbnet
Dim endpoint = New OpcEndpointIdentity( _
        "opc.tcp://192.168.0.10:4840/", _
        New OpcSecurityPolicy(OpcSecurityMode.SignAndEncrypt, OpcSecurityAlgorithm.Basic256Sha256))

'Endpoint access mode Blacklist: only explicitly enabled endpoints are permitted.
Dim service = server.Security.UserNameAcl.AddEntry( _
        "service", _
        servicePassword, _
        operationAccessMode:=OpcAccessControlMode.Whitelist, _
        endpointAccessMode:=OpcAccessControlMode.Blacklist)

service.Enable(endpoint)
```

### Anmeldung mit Token

Für Benutzeridentitäten mit ausgestellten Token (engl. issued token) prüft die `IssuedTokenAcl` ausschließlich Kerberos-Tickets, und das nur, wenn der Server unter .NET Framework läuft. Unter .NET Standard und .NET (Core) weist der Server solche Anmeldungen ab.

## Rollen

Beteiligte Typen: `OpcServerSecurity`, `OpcServerSecurityRoles`, `OpcAccessControlRole`, `OpcAccessControlEntry`, `OpcPrincipal`

OPC UA definiert eine Reihe von Standardrollen, denen die Benutzer eines Servers angehören können. Die Eigenschaft `Roles` der Klasse `OpcServerSecurity` stellt diese Rollen als Instanzen der Klasse `OpcAccessControlRole` bereit:

| Rolle | Bedeutung |
|---|---|
| `Anonymous` | Darf nicht sicherheitsrelevante Nodes im Server-Objekt und alle Typ-Nodes browsen und lesen. |
| `AuthenticatedUser` | Darf nicht sicherheitsrelevante Nodes browsen und lesen. |
| `Observer` | Darf browsen, aktuelle und historische Daten und Ereignisse lesen und Daten und Ereignisse abonnieren. |
| `Operator` | Wie `Observer`, darf zusätzlich einige aktuelle Daten schreiben und einige Methoden aufrufen. |
| `Engineer` | Darf browsen, Konfigurationsdaten lesen und schreiben, historische Daten und Ereignisse lesen, Methoden aufrufen und abonnieren. |
| `Supervisor` | Darf browsen, aktuelle und historische Daten und Ereignisse lesen, Methoden aufrufen und abonnieren. |
| `ConfigureAdmin` | Darf nicht sicherheitsrelevante Einstellungen der Konfiguration ändern. |
| `SecurityAdmin` | Darf sicherheitsrelevante Einstellungen ändern, zum Beispiel das Zertifikat des Servers (siehe [Zertifikate per GDS verwalten](https://www.traeger.de/docs/development/opcua/security/gds-push)). |

Anonyme Benutzer ordnet der Server automatisch der Rolle `Anonymous` zu, alle über eine Zugriffskontrollliste angemeldeten Benutzer der Rolle `AuthenticatedUser`. Weitere Rollen ordnen Sie einem Zugriffskontrolleintrag über die Methode `Assign` zu:

```csharp
var acl = server.Security.UserNameAcl;
 
var operator1 = acl.AddEntry("operator1", password: "password1");
operator1.Assign(server.Security.Roles.Operator);
 
var admin = acl.AddEntry("admin", password: "password2");
admin.Assign(server.Security.Roles.ConfigureAdmin, server.Security.Roles.SecurityAdmin);
 
acl.IsEnabled = true;
```

```vbnet
Dim acl = server.Security.UserNameAcl

Dim operator1 = acl.AddEntry("operator1", password:="password1")
operator1.Assign(server.Security.Roles.Operator)

Dim admin = acl.AddEntry("admin", password:="password2")
admin.Assign(server.Security.Roles.ConfigureAdmin, server.Security.Roles.SecurityAdmin)

acl.IsEnabled = True
```

Über die Methode `Revoke` heben Sie die Zuordnung wieder auf:

```csharp
admin.Revoke(server.Security.Roles.ConfigureAdmin);
```

```vbnet
admin.Revoke(server.Security.Roles.ConfigureAdmin)
```

Ob ein Prinzipal einer Rolle angehört, ermittelt die Methode `IsInRole` der Klasse `OpcPrincipal` beziehungsweise die Methode `Contains` der jeweiligen Rolle. Die Rollen, die der Server verwendet, liefert die Eigenschaft `AccessControlRoles` der Klasse `OpcServerSecurity`.

## Zugriffskontrollmodi

Beteiligte Typen: `OpcAccessControlList`, `OpcAccessControlEntry`, `OpcAccessControlMode`, `OpcRequestType`

Wie der Server die einem Zugriffskontrolleintrag erteilten (`Allow`) und entzogenen (`Deny`) Operationen auswertet, legt der Zugriffskontrollmodus des Eintrags fest (Enumeration `OpcAccessControlMode`):

- **Whitelist** (Standard der Zugriffskontrolllisten des SDK): Alle Operationen sind erlaubt, solange sie dem Eintrag nicht ausdrücklich entzogen wurden.
- **Blacklist**: Alle Operationen sind verweigert, solange sie dem Eintrag nicht ausdrücklich erteilt wurden.
- **Explicit**: Es zählt nur, was dem Eintrag ausdrücklich erteilt oder entzogen wurde. Der Server verweigert dabei ausschließlich ausdrücklich entzogene Operationen.

Den Modus legen Sie beim Hinzufügen eines Eintrags fest:

```csharp
var guest = acl.AddEntry(
        "guest",
        password: "password3",
        operationAccessMode: OpcAccessControlMode.Blacklist);
 
// In blacklist mode only explicitly allowed operations are permitted.
guest.Allow(OpcRequestType.Browse, OpcRequestType.Read);
```

```vbnet
Dim guest = acl.AddEntry( _
        "guest", _
        password:="password3", _
        operationAccessMode:=OpcAccessControlMode.Blacklist)

'In blacklist mode only explicitly allowed operations are permitted.
guest.Allow(OpcRequestType.Browse, OpcRequestType.Read)
```

## Eigene Zugriffskontrolllisten

Beteiligte Typen: `OpcServerSecurity`, `OpcAccessControlList`, `OpcAccessControlTokenType`, `OpcIdentityToken`, `OpcUserNameIdentityToken`, `OpcPrincipal`, `OpcServerIdentity`

Wenn Sie die Benutzer eines Servers nicht im Code des Servers pflegen möchten, sondern zum Beispiel in einer bestehenden Benutzerverwaltung, implementieren Sie eine eigene Zugriffskontrollliste. Leiten Sie dazu von der Klasse `OpcAccessControlList` ab, legen Sie über die Eigenschaft `TokenType` die Art der unterstützten Identitäten fest und implementieren Sie die Methode `Authenticate`. Wenn die Methode ein Prinzipal liefert, gilt der Benutzer als angemeldet. Wenn sie `null` liefert, prüft der Server die nächste aktive Zugriffskontrollliste. Ordnen Sie angemeldete Benutzer wie bei den Zugriffskontrolllisten des SDK der Rolle `AuthenticatedUser` zu:

```csharp
public class MyUserNameAcl : OpcAccessControlList
{
    private readonly Dictionary<string, OpcPrincipal> principals
            = new Dictionary<string, OpcPrincipal>();
 
    public MyUserNameAcl(OpcServerBase owner)
        : base(
                owner,
                operationAccessMode: OpcAccessControlMode.Whitelist,
                endpointAccessMode: OpcAccessControlMode.Whitelist)
    {
    }
 
    protected override OpcAccessControlTokenType TokenType
    {
        get { return OpcAccessControlTokenType.UserName; }
    }
 
    public override OpcPrincipal Authenticate(OpcIdentityToken identityToken)
    {
        var token = identityToken as OpcUserNameIdentityToken;
 
        // Verify the user name and password, e.g. using an existing user management.
        if (token == null || !UserStore.Verify(token.UserName, token.DecryptedPassword))
            return null;
 
        lock (this.principals) {
            OpcPrincipal principal;
 
            if (!this.principals.TryGetValue(token.UserName, out principal)) {
                principal = new OpcPrincipal(new OpcServerIdentity(token.UserName));
                this.AddEntry(principal);
 
                // Authenticated users are expected to be a member of the AuthenticatedUser role.
                this.Owner.Security.Roles.AuthenticatedUser.Assign(principal);
                this.principals.Add(token.UserName, principal);
            }
 
            return principal;
        }
    }
}
```

```vbnet
Public Class MyUserNameAcl
    Inherits OpcAccessControlList

    Private ReadOnly principals As New Dictionary(Of String, OpcPrincipal)()

    Public Sub New(ByVal owner As OpcServerBase)
        MyBase.New( _
                owner, _
                operationAccessMode:=OpcAccessControlMode.Whitelist, _
                endpointAccessMode:=OpcAccessControlMode.Whitelist)
    End Sub

    Protected Overrides ReadOnly Property TokenType As OpcAccessControlTokenType
        Get
            Return OpcAccessControlTokenType.UserName
        End Get
    End Property

    Public Overrides Function Authenticate(ByVal identityToken As OpcIdentityToken) As OpcPrincipal
        Dim token = TryCast(identityToken, OpcUserNameIdentityToken)

        'Verify the user name and password, e.g. using an existing user management.
        If token Is Nothing OrElse Not UserStore.Verify(token.UserName, token.DecryptedPassword) Then
            Return Nothing
        End If

        SyncLock Me.principals
            Dim principal As OpcPrincipal = Nothing

            If Not Me.principals.TryGetValue(token.UserName, principal) Then
                principal = New OpcPrincipal(New OpcServerIdentity(token.UserName))
                Me.AddEntry(principal)

                'Authenticated users are expected to be a member of the AuthenticatedUser role.
                Me.Owner.Security.Roles.AuthenticatedUser.Assign(principal)
                Me.principals.Add(token.UserName, principal)
            End If

            Return principal
        End SyncLock
    End Function
End Class
```

Über die Methode `AddAccessControlList` machen Sie die eigene Zugriffskontrollliste dem Server bekannt und aktivieren sie. Über die Methode `RemoveAccessControlList` entfernen Sie sie wieder:

```csharp
var customAcl = new MyUserNameAcl(server);
server.Security.AddAccessControlList(customAcl);
 
customAcl.IsEnabled = true;
```

```vbnet
Dim customAcl = New MyUserNameAcl(server)
server.Security.AddAccessControlList(customAcl)

customAcl.IsEnabled = True
```

Wie bei den Zugriffskontrolllisten des SDK schaltet die Aktivierung einer eigenen Zugriffskontrollliste den anonymen Zugriff automatisch ab (siehe [Benutzer anmelden](https://www.traeger.de/docs/development/opcua/security/server#benutzer-anmelden)).

## Anmeldeversuche begrenzen

Beteiligte Typen: `OpcServer`, `OpcAuthenticationLimits`

Zum Schutz vor dem Durchprobieren von Passwörtern (engl. brute force) begrenzt der Server die Anmeldeversuche über die Eigenschaft `AuthenticationLimits`. Er zählt die Versuche je Benutzer und IP-Adresse des Clients. Wenn innerhalb des Zeitfensters `DelayWindow` (standardmäßig 5 Sekunden) mehr als `MaxFailedAttempts` (standardmäßig 5) Anmeldeversuche fehlschlagen, verzögert der Server jeden weiteren Versuch. Er verzögert zunächst um `Delay` (standardmäßig 1 Sekunde) und bei weiteren Fehlversuchen jeweils doppelt so lange, höchstens jedoch um `MaxDelay` (standardmäßig 30 Sekunden). Eine erfolgreiche Anmeldung setzt den Zähler zurück:

```csharp
server.AuthenticationLimits.MaxFailedAttempts = 3;                  // Default: 5
server.AuthenticationLimits.DelayWindow = TimeSpan.FromSeconds(10); // Default: 5 seconds
server.AuthenticationLimits.Delay = TimeSpan.FromSeconds(2);        // Default: 1 second
server.AuthenticationLimits.MaxDelay = TimeSpan.FromMinutes(1);     // Default: 30 seconds
```

```vbnet
server.AuthenticationLimits.MaxFailedAttempts = 3                  'Default: 5
server.AuthenticationLimits.DelayWindow = TimeSpan.FromSeconds(10) 'Default: 5 seconds
server.AuthenticationLimits.Delay = TimeSpan.FromSeconds(2)        'Default: 1 second
server.AuthenticationLimits.MaxDelay = TimeSpan.FromMinutes(1)     'Default: 30 seconds
```

Zudem begrenzt der Server die Zahl gleichzeitiger Anmeldeversuche je Identität (`MaxConcurrentAttemptsPerIdentity`, standardmäßig 3) und insgesamt (`MaxConcurrentAttempts`, standardmäßig 32). Darüber hinausgehende Versuche lehnt er mit dem Status `BadUserAccessDenied` ab. Wenn der Server diese Grenzen anhand der Prozessorkerne und des verfügbaren Speichers ermitteln soll, aktivieren Sie die Eigenschaft `AutoDetermineLimits`:

```csharp
// Default: false
server.AuthenticationLimits.AutoDetermineLimits = true;
```

```vbnet
'Default: False
server.AuthenticationLimits.AutoDetermineLimits = True
```

Für Verbindungen über das Schema `https` stehen dem Server keine Informationen zur Gegenstelle zur Verfügung. Die Begrenzung gilt dort deshalb nicht.

Abgelehnte Anmeldungen meldet das Ereignis `SessionActivationFailed` (siehe [Audit-Ereignisse](https://www.traeger.de/docs/development/opcua/security/server#audit-ereignisse)).

## Schutz vor Überlastung

Beteiligte Typen: `OpcServer`, `OpcOperationLimits`, `OpcTransport`, `OpcRequestValidatingEventArgs`

Clients können einen Server mit sehr großen oder sehr vielen Anfragen belasten. Drei Mechanismen begrenzen das:

- **Grenzen je Anfrage**: Standardmäßig meldet der Server seinen Clients keine Grenzen dafür, wie viele Nodes sie je Anfrage lesen, schreiben, browsen oder abonnieren dürfen. Mit `OperationLimits.AutoDetermineLimits` ermittelt der Server anhand der verfügbaren Systemressourcen geeignete Grenzen und meldet sie seinen Clients (siehe [Grenzen der Datenübertragung](https://www.traeger.de/docs/development/opcua/server/configuration#grenzen-der-datenubertragung)).
- **Grenzen der Nachrichten**: Die Grenzen unter `Transport` begrenzen die Größe von Nachrichten, Zeichenketten und Arrays. Erhöhen Sie sie nur so weit, wie es die Anwendung erfordert.
- **Anfragen ablehnen**: Über das Ereignis `RequestValidating` kann der Server Anfragen vor der Verarbeitung ablehnen, zum Beispiel alle Anfragen, die die Struktur des Adressraums verändern (siehe [Anfragen validieren](https://www.traeger.de/docs/development/opcua/server/request-monitoring#anfragen-validieren)):

```csharp
server.RequestValidating += HandleRequestValidating;
...
private static void HandleRequestValidating(
        object sender,
        OpcRequestValidatingEventArgs e)
{
    // Clients must not change the structure of the address space.
    if (e.RequestType == OpcRequestType.AddNodes
            || e.RequestType == OpcRequestType.DeleteNodes
            || e.RequestType == OpcRequestType.AddReferences
            || e.RequestType == OpcRequestType.DeleteReferences) {
        e.Cancel = true;
    }
}
```

```vbnet
AddHandler server.RequestValidating, AddressOf HandleRequestValidating
...
Private Shared Sub HandleRequestValidating( _
        ByVal sender As Object, _
        ByVal e As OpcRequestValidatingEventArgs)
    'Clients must not change the structure of the address space.
    If e.RequestType = OpcRequestType.AddNodes _
            OrElse e.RequestType = OpcRequestType.DeleteNodes _
            OrElse e.RequestType = OpcRequestType.AddReferences _
            OrElse e.RequestType = OpcRequestType.DeleteReferences Then
        e.Cancel = True
    End If
End Sub

```

## Zertifikate per GDS verwalten

Ein Global Discovery Server (GDS) kann das Anwendungszertifikat und die vertrauenswürdigen Zertifikate des Servers aus der Ferne verwalten (GDS Push Management). Voraussetzungen, Ablauf und Einschränkungen beschreibt die Seite [Zertifikate per GDS verwalten](https://www.traeger.de/docs/development/opcua/security/gds-push).

## Audit-Ereignisse

Beteiligte Typen: `OpcServer`, `OpcServerSecurity`, `OpcSession`, `OpcRequestValidatingEventArgs`, `OpcSessionActivationFailedEventArgs`

Audit-Ereignisse (gemäß OPC UA Teil 4) protokollieren sicherheitsrelevante Aktionen. Der Server meldet sie nur, wenn die Eigenschaft `Security.IsAuditingEnabled` den Wert `true` hat (standardmäßig `false`). Die Eigenschaft lässt sich auch im laufenden Betrieb umschalten und wirkt dann sofort:

```csharp
server.Security.IsAuditingEnabled = true;
```

```vbnet
server.Security.IsAuditingEnabled = True
```

Der Server meldet die folgenden Audit-Ereignisse über das Server-Objekt:

| Ereignistyp | Ausgelöst |
|---|---|
| AuditOpenSecureChannelEventType | wenn ein Client einen sicheren Kanal öffnet; außerdem, wenn der Server das Öffnen oder Erneuern eines sicheren Kanals ablehnt |
| AuditChannelEventType | wenn ein Client einen sicheren Kanal mit einer CloseSecureChannel-Anfrage schließt |
| AuditCreateSessionEventType | wenn ein Client eine Sitzung erstellt |
| AuditActivateSessionEventType | wenn ein Client eine Sitzung aktiviert, also sich anmeldet |

Die Ereignisse zu sicheren Kanälen meldet der Server nur für Verbindungen über `opc.tcp`. Wie in OPC UA festgelegt, erhält ein Client die Audit-Ereignisse nur, wenn er über einen Endpunkt mit dem Modus `SignAndEncrypt` oder über HTTPS verbunden ist und sein Benutzer der Rolle `SecurityAdmin` angehört (siehe [Rollen](https://www.traeger.de/docs/development/opcua/security/server#rollen)).

Weitere Audit-Ereignisse, etwa für Schreibzugriffe, Methodenaufrufe oder Zertifikatsänderungen, meldet der Server nicht. Eigene Audit-Ereignisse kann eine Anwendung nicht melden. Wie ein Client die Ereignisse empfängt, beschreibt der Abschnitt [Audit-Ereignisse empfangen](https://www.traeger.de/docs/development/opcua/client/events#audit-ereignisse-empfangen).

Wenn Ihre Anwendung ein Protokoll sicherheitsrelevanter Aktionen benötigt, führen Sie es selbst über die Ereignisse des Servers, zum Beispiel `SessionActivated` für Anmeldungen, `SessionActivationFailed` für abgelehnte Anmeldungen und `RequestValidating` für Schreibzugriffe und Methodenaufrufe (siehe [Anfragen überwachen](https://www.traeger.de/docs/development/opcua/server/request-monitoring)):

```csharp
server.SessionActivated += HandleSessionActivated;
server.RequestValidating += HandleCriticalRequest;
...
private static void HandleSessionActivated(object sender, OpcSessionEventArgs e)
{
    Console.WriteLine(
            "{0:u} session {1}: {2}",
            DateTime.UtcNow,
            e.Session.Name,
            e.Session.UsedIdentity);
}
 
private static void HandleCriticalRequest(object sender, OpcRequestValidatingEventArgs e)
{
    if (e.RequestType == OpcRequestType.Write || e.RequestType == OpcRequestType.Call) {
        Console.WriteLine(
                "{0:u} {1} in session {2}",
                DateTime.UtcNow,
                e.RequestType,
                e.Context.SessionId);
    }
}
```

```vbnet
AddHandler server.SessionActivated, AddressOf HandleSessionActivated
AddHandler server.RequestValidating, AddressOf HandleCriticalRequest
...
Private Shared Sub HandleSessionActivated(ByVal sender As Object, ByVal e As OpcSessionEventArgs)
    Console.WriteLine( _
            "{0:u} session {1}: {2}", _
            DateTime.UtcNow, _
            e.Session.Name, _
            e.Session.UsedIdentity)
End Sub

Private Shared Sub HandleCriticalRequest( _
        ByVal sender As Object, _
        ByVal e As OpcRequestValidatingEventArgs)
    If e.RequestType = OpcRequestType.Write OrElse e.RequestType = OpcRequestType.Call Then
        Console.WriteLine( _
                "{0:u} {1} in session {2}", _
                DateTime.UtcNow, _
                e.RequestType, _
                e.Context.SessionId)
    End If
End Sub
```

`SessionActivationFailed` tritt bei jeder abgelehnten Anmeldung ein. Die Eigenschaft `Status` nennt den Grund, die Eigenschaft `Identity` die Identität, mit der sich der Client anmelden wollte, ohne Passwort oder Token-Daten. Bei einem Benutzernamen ist sie eine `OpcUserNameIdentityTokenInfo` mit dem Namen des Benutzers (`UserName`), bei einem Benutzerzertifikat eine `OpcCertificateIdentityTokenInfo` mit dem Zertifikat (`Certificate`):

```csharp
server.SessionActivationFailed += HandleSessionActivationFailed;
...
private static void HandleSessionActivationFailed(
        object sender,
        OpcSessionActivationFailedEventArgs e)
{
    var userName = e.Identity as OpcUserNameIdentityTokenInfo;
 
    Console.WriteLine(
            "{0:u} logon rejected ({1}): {2}",
            DateTime.UtcNow,
            e.Status.Code,
            userName != null ? userName.UserName : "no user name");
}
```

```vbnet
AddHandler server.SessionActivationFailed, AddressOf HandleSessionActivationFailed
...
Private Shared Sub HandleSessionActivationFailed( _
        ByVal sender As Object, _
        ByVal e As OpcSessionActivationFailedEventArgs)
    Dim userName = TryCast(e.Identity, OpcUserNameIdentityTokenInfo)

    Console.WriteLine( _
            "{0:u} logon rejected ({1}): {2}", _
            DateTime.UtcNow, _
            e.Status.Code, _
            If(userName IsNot Nothing, userName.UserName, "no user name"))
End Sub

```

## Härtung für den Produktivbetrieb

Prüfen Sie vor dem Produktivbetrieb die folgenden Punkte:

1. **Nur verschlüsselte Endpunkte anbieten**: ausschließlich Policies mit `SignAndEncrypt` und einem aktuellen Algorithmus eintragen (siehe [Endpunkte und Netzwerk absichern](https://www.traeger.de/docs/development/opcua/security/server#endpunkte-und-netzwerk-absichern)).
2. **Netzwerkschnittstellen beschränken**: die Eigenschaft `Binding` auf die benötigten Adressen begrenzen.
3. **Unbekannte Clientzertifikate ablehnen**: `AutoAcceptUntrustedCertificates` deaktivieren und Clientzertifikate in den `TrustedPeerStore` aufnehmen (siehe [Clientzertifikate prüfen und vertrauen](https://www.traeger.de/docs/development/opcua/security/server#clientzertifikate-prufen-und-vertrauen)).
4. **Zertifikate streng prüfen**: `CertificateValidationRules` auf `RejectSHA1Signed` und `DisableBackwardsCompatibleValidation` stellen.
5. **Eigenes Zertifikat verwalten**: ein Zertifikat mit angemessener Schlüssellänge und Laufzeit verwenden, `AutoCreateCertificate` deaktivieren und die Zertifikatspeicher in einem geschützten Verzeichnis ablegen (siehe [Eigenes Zertifikat schützen](https://www.traeger.de/docs/development/opcua/security/server#eigenes-zertifikat-schutzen)).
6. **Anonymen Zugriff abschalten und Benutzer verwalten**: nur bekannte Benutzer zulassen, Passwörter als Hashwerte ablegen und Benutzerzertifikate prüfen (siehe [Benutzer anmelden](https://www.traeger.de/docs/development/opcua/security/server#benutzer-anmelden)).
7. **Rechte minimal halten**: jedem Benutzer nur die Operationen erteilen, die er braucht, und Rollen gezielt zuordnen (siehe [Rollen](https://www.traeger.de/docs/development/opcua/security/server#rollen) und [Zugriffskontrollmodi](https://www.traeger.de/docs/development/opcua/security/server#zugriffskontrollmodi)).
8. **Anmeldeversuche begrenzen**: die `AuthenticationLimits` an die Zahl der Benutzer anpassen (siehe [Anmeldeversuche begrenzen](https://www.traeger.de/docs/development/opcua/security/server#anmeldeversuche-begrenzen)).
9. **Überlastung verhindern**: `OperationLimits.AutoDetermineLimits` aktivieren, die Grenzen unter `Transport` nicht unnötig erhöhen und unerwünschte Anfragen ablehnen (siehe [Schutz vor Überlastung](https://www.traeger.de/docs/development/opcua/security/server#schutz-vor-uberlastung)).
10. **GDS Push gezielt freischalten**: die Rolle `SecurityAdmin` nur einer Identität mit Zertifikat zuordnen (siehe [Zertifikate per GDS verwalten](https://www.traeger.de/docs/development/opcua/security/gds-push)).
11. **Sicherheitsrelevante Aktionen protokollieren**: bei Bedarf `IsAuditingEnabled` einschalten, Anmeldungen, abgelehnte Anmeldungen und kritische Anfragen selbst protokollieren (siehe [Audit-Ereignisse](https://www.traeger.de/docs/development/opcua/security/server#audit-ereignisse)) und die Kennzahlen des Servers überwachen (siehe [Diagnose](https://www.traeger.de/docs/development/opcua/server/request-monitoring#diagnose)).
12. **Lizenzschlüssel schützen**: den Schlüssel nicht im Klartext in öffentlich zugänglichen Dateien oder Repositories ablegen (siehe [Fahrlässige Veröffentlichung](https://www.traeger.de/docs/development/opcua/license#fahrlassige-veroffentlichung)).

Zusammengefasst sieht ein so konfigurierter Server wie folgt aus:

```csharp
var server = new OpcServer("opc.tcp://192.168.0.10:4840/");
 
// Only encrypted endpoints with current algorithms (removes the implicit None endpoint).
server.Security.EndpointPolicies.Add(new OpcSecurityPolicy(
        OpcSecurityMode.SignAndEncrypt,
        OpcSecurityAlgorithm.Basic256Sha256));
server.Security.EndpointPolicies.Add(new OpcSecurityPolicy(
        OpcSecurityMode.SignAndEncrypt,
        OpcSecurityAlgorithm.Aes256_Sha256_RsaPss));
 
// Listen only on the network interface that clients use.
server.Binding.Addresses.Add(IPAddress.Parse("192.168.0.10"));
 
// Trust only known client certificates and check them strictly.
server.Security.AutoAcceptUntrustedCertificates = false;
server.Security.CertificateValidationRules
        = OpcCertificateValidationRules.RejectSHA1Signed
        | OpcCertificateValidationRules.DisableBackwardsCompatibleValidation;
 
// No anonymous access, only known users.
server.Security.UserNameAcl.AddEntry("operator", operatorPassword);
server.Security.UserNameAcl.IsEnabled = true;
server.Security.AnonymousAcl.IsEnabled = false;
 
// Report operation limits to the clients.
server.OperationLimits.AutoDetermineLimits = true;
 
// Use a managed certificate instead of a generated one.
server.CertificateStores.AutoCreateCertificate = false;
server.Certificate = OpcCertificateManager.LoadCertificate(certificatePath, certificatePassword);
 
server.Start();
```

```vbnet
Dim server = New OpcServer("opc.tcp://192.168.0.10:4840/")

'Only encrypted endpoints with current algorithms (removes the implicit None endpoint).
server.Security.EndpointPolicies.Add(New OpcSecurityPolicy( _
        OpcSecurityMode.SignAndEncrypt, _
        OpcSecurityAlgorithm.Basic256Sha256))
server.Security.EndpointPolicies.Add(New OpcSecurityPolicy( _
        OpcSecurityMode.SignAndEncrypt, _
        OpcSecurityAlgorithm.Aes256_Sha256_RsaPss))

'Listen only on the network interface that clients use.
server.Binding.Addresses.Add(IPAddress.Parse("192.168.0.10"))

'Trust only known client certificates and check them strictly.
server.Security.AutoAcceptUntrustedCertificates = False
server.Security.CertificateValidationRules _
        = OpcCertificateValidationRules.RejectSHA1Signed _
        Or OpcCertificateValidationRules.DisableBackwardsCompatibleValidation

'No anonymous access, only known users.
server.Security.UserNameAcl.AddEntry("operator", operatorPassword)
server.Security.UserNameAcl.IsEnabled = True
server.Security.AnonymousAcl.IsEnabled = False

'Report operation limits to the clients.
server.OperationLimits.AutoDetermineLimits = True

'Use a managed certificate instead of a generated one.
server.CertificateStores.AutoCreateCertificate = False
server.Certificate = OpcCertificateManager.LoadCertificate(certificatePath, certificatePassword)

server.Start()
```
