OPC UA Security: Certificates, Security Policies, User Authentication and Global Discovery Server (2026)

OPC UA Security: Certificates, Security Policies, User Authentication and Global Discovery Server (2026)

OPC UA Security: Certificates, Policies, User Auth and GDS (2026)

Walk a brownfield plant and ask how its OPC UA servers are secured. You will often hear “we use certificates”, and then find a server that accepts every certificate it is shown, a SecurityPolicy of None left enabled “for commissioning”, anonymous sessions with write access, and application certificates that expired two years ago and are now silently ignored. The protocol has one of the most complete security models in industrial automation. Most incidents come from how that model is configured, not from any weakness in it.

OPC UA security is not one mechanism. It is three stacked layers: a secure channel that protects messages, a session that binds a client application to a server application, and a user identity that authorizes a person or service. Around them sit certificate lifecycle management, which the Global Discovery Server (GDS) automates, and a separate key distribution scheme for PubSub. Each layer fails independently, and each needs its own settings.

This post explains how the layers fit together, what each SecurityPolicy actually commits you to, how trust lists work, how GDS push and pull management removes manual certificate handling, and how to configure a hardened server. A runnable Python asyncua example and a hardening checklist close it out.

What this covers: the three-layer model, SecurityMode and policies, application instance certificates, trust lists, GDS push and pull, user identity tokens, auditing, PubSub keys, common misconfigurations, and a hardening checklist.

Context and Background

OPC UA (Unified Architecture, IEC 62541) was designed after classic OPC’s reliance on DCOM exposed plants to the network-level problems that DCOM brought with it. Security was made a first-class part of the specification rather than an add-on. The security model is defined mainly in Part 2 (security model), Part 4 (services), Part 6 (mappings) and Part 7 (profiles and security policies), with certificate management in Part 12 and PubSub key distribution in Part 14, all published by the OPC Foundation reference site.

Three design decisions shape everything that follows. First, authentication of applications is mutual and certificate-based: the client verifies the server, and the server verifies the client. Second, application identity and user identity are separate. A trusted application can still be refused a particular user, and a valid user can still be refused from an untrusted application. Third, algorithms are not hard-wired. A SecurityPolicy is a named bundle of algorithms and key lengths, so the standard can retire weak cryptography without redefining the protocol.

For plant teams this model meets regulation. The EU Cyber Resilience Act, NIS2 and IEC 62443 all push toward authenticated, encrypted, patchable industrial communication, and OPC UA security features map onto several of their technical requirements. We cover that mapping in EU CRA vs NIS2 vs IEC 62443 compliance mapping for IoT and OT vendors. For the information-model side of machine-tool interoperability, which rides on this security layer, see umati vs MTConnect vs OPC UA.

The practical state of the field is uneven. Mature stacks such as the OPC Foundation .NET Standard stack, open62541, Unified Automation SDKs and Eclipse Milo implement the full model. Many embedded servers inside PLCs and gateways expose only a subset, often with older policies on by default. The first job in any assessment is therefore an inventory: which endpoints exist, which policies and modes each offers, and which user token types each accepts. A client can learn most of this unauthenticated, because GetEndpoints is a discovery call that returns the endpoint list including security modes, policy URIs and token policies.

One framing helps throughout. OPC UA gives you mechanism, not policy. It tells you how to prove who an application is and how to protect bytes in flight. It does not tell you which applications to trust, how often to rotate certificates, or whether anonymous reads are acceptable. Those are decisions you encode in trust lists, endpoint configuration and role mappings, and they are where deployments actually go wrong.

The OPC UA Security Architecture: Three Layers and Their Trust Anchors

The OPC UA security model has three layers. The secure channel provides confidentiality, integrity and application authentication for messages. The session ties a client application instance to a server application instance and carries the user identity. The user token, presented at session activation, authenticates the human or service and drives authorization. A request must pass all three.

OPC UA security architecture with secure channel, session and user token layers and their trust anchors

Figure 1: The three OPC UA security layers and what each one authenticates, with the trust anchor behind each.

Figure 1 shows the sequence a client walks. At the bottom is the transport (OPC UA binary over TCP, or HTTPS). On top of that, the client calls OpenSecureChannel, which negotiates a SecurityMode and SecurityPolicy and exchanges nonces and certificates. Above that the client calls CreateSession and ActivateSession, and in the second call presents its user identity token. Each layer has a different trust anchor: the secure channel trusts application instance certificates, the session trusts the proof that the client holds its private key, and the user layer trusts whatever identity source the server is configured with, whether a password store, an X.509 user certificate or an external token issuer.

Layer 1: the secure channel

The secure channel is established with a SecurityMode and SecurityPolicy chosen from the server’s endpoint list. SecurityMode has three values: None, Sign, and SignAndEncrypt. With None, messages are neither signed nor encrypted and certificates are not exchanged for protection. With Sign, every message carries a signature, so tampering is detected but content is readable. With SignAndEncrypt, messages are signed and encrypted.

During OpenSecureChannel, the asymmetric part of the SecurityPolicy protects the exchange of nonces, using the application instance certificates’ key pairs. Both sides derive symmetric keys from those nonces. After that, bulk traffic uses fast symmetric cryptography. The channel has a token with a lifetime, and the client renews it before it expires, which rotates the symmetric keys without a new handshake of the whole session. This means long-lived connections still get periodic key refresh, a property worth checking in your stack’s configuration.

Layer 2: the session

A secure channel authenticates applications but says nothing about sessions. CreateSession returns a server nonce and the server’s certificate, and the client proves possession of its private key by signing data that includes that nonce in ActivateSession. This binds the session to the channel and the application, and it prevents a session from being hijacked by a different client that merely knows the session identifier. Sessions have timeouts, and servers should limit how many a single client or address can open, since unlimited sessions are a denial-of-service route.

Layer 3: the user identity

User identity is carried in a UserIdentityToken. The specification defines four kinds: anonymous, user name and password, X.509 certificate, and issued token (such as a Kerberos ticket or JWT from an identity provider). The server advertises which kinds it accepts, and for each, the SecurityPolicy used to protect the secret. Authorization is then expressed as roles mapped from identities, with permissions attached to nodes. We return to the details in the user authentication section.

The key design implication

The layers are independent, which is the source of most confusion. A SignAndEncrypt channel with an anonymous user token is fully encrypted and still lets anyone who can present any trusted application certificate read the address space. A strong user password sent over a channel with SecurityPolicy None is, depending on the token policy, protected only by the token’s own policy, or not at all. Treat each layer as a separate control with its own review, and avoid answering “is it secure?” with a single yes.

What each layer defends against

It helps to map layers to threats, because it clarifies which configuration defends against which attack. Eavesdropping on process values is addressed by encryption on the secure channel. Modification of a setpoint in flight is addressed by signing. A rogue application impersonating a trusted historian is addressed by application certificate validation against the trust list. A trusted but compromised application acting beyond its authority is addressed by user identity and role-based permissions, which is why a service identity per application is better than one shared account. Replay of captured messages is addressed by sequence numbers and nonces inside the channel and session. No single layer covers all five, which is the case for configuring all of them.

Security Policies and the Secure Channel Handshake

A SecurityPolicy is a URI-named bundle of algorithms: asymmetric signature, asymmetric encryption, key derivation, symmetric signature and encryption, and minimum and maximum key lengths. Combined with a SecurityMode, it defines what an endpoint offers. A server publishes one endpoint per mode and policy combination, so a single server can offer several, and the client picks.

The policy catalogue in 2026

Policy URIs share the prefix http://opcfoundation.org/UA/SecurityPolicy#. Cross-checking the open62541 policy documentation and the Unified Automation SDK reference gives the following landscape. The algorithm detail for the RSA family is stated in those sources; for the ECC family they list curves, and the exact signature and key agreement primitives should be read from Part 7 before you rely on them.

Policy Family Stated primitives Status
None none no signing or encryption Avoid on any production endpoint
Basic128Rsa15 RSA RSA, AES-128 Marked deprecated by open62541
Basic256 RSA RSA, AES-256 Superseded by Basic256Sha256
Basic256Sha256 RSA RSA, SHA-256, AES-256 Widely deployed baseline
Aes128_Sha256_RsaOaep RSA RSA-OAEP, AES-128, SHA-256 Current RSA option
Aes256_Sha256_RsaPss RSA RSA-PSS, AES-256, SHA-256 Current, strongest RSA option
ECC_nistP256, ECC_nistP384 ECC NIST curves Newer, support still uneven
ECC_brainpoolP256r1, ECC_brainpoolP384r1 ECC Brainpool curves Newer, support still uneven
ECC_curve25519, ECC_curve448 ECC Edwards curves Listed as deprecated in the Unified Automation SDK reference
PubSub-Aes128-CTR, PubSub-Aes256-CTR PubSub AES in counter mode Symmetric only, for PubSub

Two points deserve emphasis. The Edwards-curve entries and their ChaCha20-Poly1305 variants appear in SDK listings with differing deprecation flags, which shows the ECC family is still settling. Verify the current state in the Part 7 revision your stack implements rather than inferring it from a table like this one. Second, the table is a snapshot: confirm it against the specification release you target.

Choosing a policy

The practical guidance is conservative. For new RSA deployments, prefer Aes256_Sha256_RsaPss or Aes128_Sha256_RsaOaep if every component on both ends supports them, and use Basic256Sha256 as the compatibility baseline. Disable Basic128Rsa15 and Basic256 unless a specific legacy device forces them, and record that exception with an owner and an end date. Treat ECC policies as the direction of travel, particularly for constrained devices where elliptic curve operations are cheaper than RSA at comparable strength, but only enable them after interoperability testing across every client that must connect.

Interoperability is the real constraint. A policy is only useful if both peers implement it. Many deployed PLC servers stop at Basic256Sha256, and some older ones only offer Basic128Rsa15 or Basic256. The honest options are to upgrade firmware, place a security-capable gateway or aggregating server in front, or segment the network so the legacy endpoint is reachable only from one controlled host.

What the handshake does

Sequence of OpenSecureChannel, CreateSession and ActivateSession showing certificate exchange and nonce signing

Figure 2: The OPC UA secure channel and session handshake, from GetEndpoints through ActivateSession.

Figure 2 walks the sequence. The client first calls GetEndpoints to learn modes, policies and token types. It then sends OpenSecureChannel, including its certificate and a client nonce, encrypted with the server’s public key when the mode requires encryption. The server validates the client certificate against its trust list, replies with its own nonce, and both derive symmetric keys. Next, CreateSession returns the server certificate and a server nonce, and the client validates that certificate against its own trust list. Finally, ActivateSession carries the client’s signature over the server nonce and the user identity token.

The point of the two certificate validations is symmetry. The server rejects unknown clients, and the client rejects unknown servers. Skipping either side, for instance an HMI that auto-trusts whatever server it meets, reopens the door to man-in-the-middle attacks even when the channel is encrypted, because an encrypted channel to an attacker is still a channel to an attacker.

Secure channel details that matter in operations

Three details repay attention. The channel’s security token has a lifetime, and the client should renew it at a fraction of that lifetime. Servers enforce a maximum, so a client that never renews is disconnected, which shows up as periodic drops in poorly written clients. Message size limits are also part of the negotiation, and large limits increase memory exposure on small devices. Finally, certificate validity is checked at channel open, not continuously. An established session on an expired or revoked certificate typically keeps running until the channel closes, so rotation requires forcing reconnection.

Application Instance Certificates and the OPC UA Trust List

Every OPC UA application has an application instance certificate, an X.509 v3 certificate with specific content rules. It identifies the application instance, not the machine and not the person. The matching private key must never leave the application’s trust boundary, and its protection is the single most important operational control in the whole model.

What goes inside the certificate

The certificate must carry the ApplicationUri in the Subject Alternative Name extension as a URI entry, and that value must exactly match the ApplicationUri in the application description the server or client reports. Servers should also list their hostname and IP addresses as DNS and IP SAN entries, since clients check that the endpoint URL’s host appears in the certificate. Key usage and extended key usage values are constrained by the specification, including digital signature and key encipherment, and the client and server authentication purposes. RSA key length must fall inside the range the chosen SecurityPolicy permits, so a 1,024-bit key is not acceptable for the modern RSA policies.

Mismatches here cause the most common first-day failures. A certificate generated by a generic tool without the URI SAN fails validation with errors such as BadCertificateUriInvalid or BadCertificateHostNameInvalid. A server reached by a new DNS alias fails because the alias is not in the SAN list. A virtual machine clone shares a certificate and private key with its source, so two distinct servers claim one identity.

Self-signed versus CA-issued

An application can use a self-signed certificate, in which case trust is established by placing that exact certificate in the peer’s trusted store. This is simple at small scale and costly at large scale: every certificate is a separate trust decision on every peer, and renewal means touching every peer.

The alternative is a certificate issued by a CA. Peers trust the CA certificate once, and then any application certificate issued by it is accepted if it chains correctly and has not been revoked. This scales, and it is the model the GDS is built to automate. It also moves risk to the CA, whose key becomes the most valuable secret in the plant, so the CA should be offline or protected by a hardware security module, with a short list of people able to request issuance.

The trust list and its stores

Flow of certificate validation against trusted store, issuer store and revocation lists, ending in accept or reject

Figure 3: How an OPC UA application validates a peer certificate against its trust list stores.

An application keeps a trust list made of several stores: trusted certificates, trusted issuers (CAs), the CRLs for those issuers, and a rejected store. Figure 3 shows the decision sequence. A presented certificate is first checked for validity period and structural correctness. Then the application attempts to build a chain. If the certificate is itself in the trusted store, or chains to a CA in the trusted-issuers store, trust holds, subject to revocation status from the issuer’s CRL. If none of that is true, the certificate is rejected and stored in the rejected store so an administrator can inspect it.

The rejected store is a feature worth using. The correct commissioning workflow is that a new client connects, is refused, and appears in the rejected folder. An administrator verifies out of band that this really is the intended device, for example by comparing the SHA-1 or SHA-256 thumbprint to one displayed on the device, and then moves it to trusted. The workflow turns trust into an explicit human decision with an audit trail.

Why auto-accept is dangerous

Most SDKs offer a switch such as “accept untrusted certificates” for demos. In production this switch erases mutual authentication: any certificate is accepted, and encryption protects against passive listeners only. Teams switch it on during commissioning and forget it. The audit question is simple: for each server and client, can you list which certificates are in the trusted store, and does the auto-accept option equal false? If not, the trust list is decorative.

Revocation and validity

Certificates have validity periods, and the usual issued lifetime is one to a few years. Short lifetimes limit exposure but multiply renewal work unless the GDS automates it. Revocation uses CRLs distributed to the issuer store, so revocation only works if CRLs are pushed to every peer and refreshed. A peer without a current CRL either rejects everything or, worse, accepts a revoked certificate depending on configuration. The specification lets a validator treat a missing CRL as a distinct status, so be deliberate about whether your policy is fail-open or fail-closed. For OT, fail-closed on identity is right, but it makes CRL distribution a production dependency, which is a reason to use the GDS rather than file shares.

Suppressible validation errors

OPC UA classifies certificate validation failures and allows some to be suppressed by the application, such as an unknown revocation status or an out-of-date CRL, while others are not suppressible, such as an invalid signature or an untrusted certificate. A configuration that suppresses all suppressible errors to keep a plant running ends up effectively disabling validation. A narrower policy suppresses only the specific condition you understand, logs every suppression, and has an expiry date on the exception.

Global Discovery Server: Automating Certificate Lifecycle

A Global Discovery Server is an OPC UA server defined in Part 12 (Discovery and Global Services) that registers applications, issues or signs their certificates, and distributes trust lists. Its aim is to replace per-device manual certificate handling with a central, auditable process. The Part 12 reference currently shows Release 1.05.05 dated 2025-07-10.

Part 12 splits certificate management into two models, pull and push, plus the information model for CertificateGroups and TrustLists. The split matters because it decides who initiates contact, which in turn decides your firewall rules.

GDS pull and push certificate management flows between a GDS, an application and a certificate authority

Figure 4: GDS pull management, where the application asks, and push management, where the GDS or an admin tool updates the application.

Pull management

In pull management the application is the active party. It connects to the GDS, registers itself with RegisterApplication, and then requests a certificate. Two variants exist. In the first the application generates its own key pair, sends a certificate signing request through StartSigningRequest, and later collects the signed result with FinishRequest. In the second the GDS generates the key pair (StartNewKeyPairRequest) and returns the private key to the application, which is simpler for constrained devices but means the private key travels, so it requires a protected channel and a trusted GDS. The application also fetches its trust list by calling the GDS’s trust list method, so it learns which CAs and certificates to trust.

Pull suits applications that can reach the GDS: PC-based servers, historians, MES connectors, and edge gateways with normal outbound connectivity. It matches firewall policy well, because connections go from the OT or edge segment to a single GDS host. Applications schedule their own renewal, which is simple but means a forgotten or misconfigured application never renews.

Push management

In push management the application exposes a ServerConfiguration object, and an administration tool or the GDS connects to it and updates it. The key methods are UpdateCertificate to install a new certificate (and private key if needed), and the standard TrustList operations such as OpenWithMasks, AddCertificate, RemoveCertificate and CloseAndUpdate to alter trusted certificates. Permissions for these operations are tightly role-controlled, and the application then applies the change, sometimes requiring a controlled restart of its secure channels.

Push suits devices that have no scheduled connectivity to a GDS or lack the logic to run a renewal client, such as a PLC. The administrator connects to it with a pre-trusted admin application. Push also gives central control over the timing of rollout, since you can stage rotation by plant area. The price is inbound connectivity to each device from the management host, and a high-value admin credential.

CertificateGroups and TrustLists

A CertificateGroup is a set of related certificates with a defined purpose, with the standard defining default groups such as one for application certificates and one for HTTPS certificates. Each group has its own TrustList. The TrustList object is read and written as a file-like object through Open, Read, Write and Close semantics, with TrustListMasks letting a caller request only certain parts, such as trusted certificates or CRLs. Keeping groups separate means the roots that you trust for OPC UA application authentication are not automatically the roots you trust for HTTPS.

Roles and permissions

Part 12 defines roles for certificate administration, such as an application self-administrator for an application managing its own registration and a more privileged administrator role. The scoping is deliberate: an application should be allowed to request a certificate for itself, not for another application, and the GDS should know which application URI maps to which identity. This is the place where a loose permission model defeats the whole scheme, so audit it.

When a GDS is worth it

For under a dozen servers on one site, a disciplined manual process with a private CA may be less work than operating a GDS. Past that, certificate expiry becomes an operations problem and the GDS earns its place. Be realistic about vendor support: GDS pull is more widely implemented in PC-class software than push in embedded devices, and push support in a given PLC firmware has to be verified from its documentation, not assumed from a product brochure. The GDS also becomes a critical dependency, so give it backup, a protected CA key and monitoring for both certificate expiry and its own availability. The Fraunhofer IOSB maintains a reference GDS implementation that is often used for evaluation.

User Authentication, Authorization and Auditing

Application trust says which software may connect. User identity says what that software, acting for someone, may do. Both are needed for least privilege.

The four identity token types

Token Secret Typical use Main risk
Anonymous none read-only public data Anyone with a trusted application gets access
UserName password operators, services Weak or shared passwords, plain transport on policy None
X509 certificate private key service accounts, automation Key sprawl, renewal workload
IssuedToken token from identity provider enterprise SSO Depends on provider integration and server support

The server’s endpoint description lists the allowed tokens and a SecurityPolicy for each. A password is encrypted with the server’s public key using that token policy. If the token policy is None, the password is not protected by that mechanism, and it depends on the channel to protect it. Combining a None token policy with a None channel puts a password in clear text on the wire. Always check both when auditing an endpoint, because a server can show a strong channel policy while a weaker token policy remains listed.

Prefer per-service identities

Shared operator accounts lose attribution. Give each client application its own account or user certificate, and give humans their own accounts. With separate identities, a compromised historian account does not hand an attacker the permissions of the control HMI, and audit trails say who did what. For automation clients, user X.509 tokens avoid password storage in configuration files, though they move the burden to protecting private keys.

Authorization with roles

OPC UA authorization maps identities to roles and roles to permissions on nodes. Well-known roles include Anonymous, AuthenticatedUser, Observer, Operator, Engineer, Supervisor, SecurityAdmin and ConfigureAdmin. Permissions cover browse, read, write, call, and history access, among others, and can be set per node or inherited by namespace. A good rule is that the default role for any unauthenticated or unmapped identity can browse and read only non-sensitive data, writes require Operator or above, and Call access to methods that change configuration needs an administrative role. Many servers ship with permissive defaults, so review the configured role mappings rather than assume.

Auditing

The standard defines audit event types: for channels, sessions, certificate events, node management, updates and method calls. A server that supports auditing emits events for session creation, failed activation, certificate validation failures and writes, and each includes the client’s application URI and the user. Two practices pay off. Forward audit events to a central log or SIEM, since a log on the same device is lost when the device is compromised. And alert on specific patterns: repeated activation failures from one client, a certificate rejected after previously being trusted, or writes from a role that normally only reads. Audit support varies across servers, so test it by causing a failure and watching for the event.

PubSub Security and the Security Key Service

Client-server security rests on a session between two peers. PubSub has no session: a publisher sends NetworkMessages to many subscribers, via UDP multicast or a broker such as MQTT or AMQP. So the security model changes. Messages are signed and optionally encrypted with symmetric keys, using PubSub policies such as PubSub-Aes128-CTR and PubSub-Aes256-CTR, and those keys are managed through SecurityGroups.

How keys reach publishers and subscribers

A Security Key Service (SKS) holds the keys for each SecurityGroup. Publishers and subscribers fetch keys by calling the GetSecurityKeys method with a SecurityGroupId over an ordinary encrypted client-server session. Part 14 states that unencrypted calls are refused with Bad_SecurityModeInsufficient, and that access follows RolePermissions on the SecurityGroup, otherwise Bad_UserAccessDenied is returned. The call returns the policy URI, the first token id, the keys, the time to the next key and the key lifetime. A publisher using a central SKS calls it with starting token id 0 about every half key lifetime. A subscriber that sees an unknown token id fetches keys again.

This design reuses the client-server security model for key distribution, so the SKS inherits the same certificate and user trust requirements. It also means PubSub security is only as good as the SKS: anyone permitted to call GetSecurityKeys for a group can decrypt and forge that group’s traffic.

Practical consequences

Use separate SecurityGroups per trust domain, not one group for the plant. Choose a key lifetime that balances SKS load and exposure. Because publishers refresh around half the lifetime, very short lifetimes put constant load on the SKS and make its availability critical, and requesting multiple future keys lets publishers ride through short SKS outages. Broker-based transport still needs its own transport security. If you run PubSub over MQTT, TLS to the broker protects the hop, and message-level PubSub signing and encryption protects the content end to end. Our guides to MQTT store-and-forward at the edge gateway and the TLS 1.3 handshake and mTLS for constrained devices cover the broker and transport side.

Hands-On: A Hardened Python asyncua Server

The example below uses the open-source asyncua library to start a server that offers only SignAndEncrypt on Basic256Sha256, uses a certificate with a URI SAN, and rejects anonymous users. It is a teaching sketch. The asyncua API has changed between releases, so check the names against the version you install (pip install asyncua cryptography) before relying on it. We did not run it against every release.

First generate a certificate with the right SAN entries. Replace the URI and hostname with your own.

openssl req -x509 -newkey rsa:2048 -sha256 -days 365 -nodes \
  -keyout server_key.pem -out server_cert.pem \
  -subj "/O=Example Plant/CN=plant-opcua-01" \
  -addext "subjectAltName=URI:urn:example:plant:opcua01,DNS:plant-opcua-01.local,IP:192.0.2.10" \
  -addext "keyUsage=digitalSignature,nonRepudiation,keyEncipherment,dataEncipherment,keyCertSign" \
  -addext "extendedKeyUsage=serverAuth,clientAuth"
openssl x509 -in server_cert.pem -outform der -out server_cert.der

Then the server, with a user manager that accepts only a known account.

import asyncio
from asyncua import Server, ua
from asyncua.server.user_managers import UserManager
from asyncua.crypto.permission_rules import SimpleRoleRuleset
from asyncua.crypto.validator import CertificateValidator, CertificateValidatorOptions
from asyncua.crypto.truststore import TrustStore

USERS = {"operator1": "change-me-long-passphrase"}   # use a real secret store

class PlantUserManager(UserManager):
    def get_user(self, iserver, username=None, password=None, certificate=None):
        if username is not None and USERS.get(username) == password:
            return username        # a real server maps this to a role
        return None                # anonymous and unknown users are refused

async def main():
    server = Server(user_manager=PlantUserManager())
    await server.init()
    server.set_endpoint("opc.tcp://0.0.0.0:4840/plant/")
    server.set_server_name("Plant OPC UA 01")
    server.set_application_uri("urn:example:plant:opcua01")  # must match the cert SAN URI

    await server.load_certificate("server_cert.der")
    await server.load_private_key("server_key.pem")

    server.set_security_policy(
        [ua.SecurityPolicyType.Basic256Sha256_SignAndEncrypt],
        permission_ruleset=SimpleRoleRuleset(),
    )

    # Trust only certificates placed in these folders, never auto-accept.
    trust = TrustStore([], [])
    await trust.load()   # see asyncua docs for loading trusted cert and CRL folders
    # validator = CertificateValidator(
    #     CertificateValidatorOptions.TRUSTED_VALIDATION
    #     | CertificateValidatorOptions.PEER_CLIENT, trust)
    # server.iserver.certificate_validator = validator  # attribute path varies by version

    idx = await server.register_namespace("urn:example:plant:ns")
    obj = await server.nodes.objects.add_object(idx, "Line1")
    temp = await obj.add_variable(idx, "Temperature", 21.5)
    await temp.set_writable(False)

    async with server:
        while True:
            await asyncio.sleep(1)

asyncio.run(main())

Several lines are version-sensitive on purpose. The trust store and validator wiring in particular differs across asyncua releases, so the commented lines show intent rather than guaranteed API. The security-relevant decisions are visible regardless: one endpoint policy, SignAndEncrypt only, a fixed application URI that matches the certificate, a user manager that refuses anonymous access, and a variable that is not writable.

To test, connect with a client configured for the same policy and the server’s certificate, and observe that a client with SecurityPolicyNone finds no matching endpoint. Then connect with an unknown client certificate and confirm it is rejected rather than silently accepted. That second test is the one teams skip, and it is the one that proves the trust list is working.

Trade-offs, Gotchas, and What Goes Wrong

Security in OPC UA fails in a small number of repeatable ways. Knowing them beats a long checklist.

Expired certificates. A certificate that expires on a Saturday takes a connection down, and if the server and client logs are not monitored, the first sign is lost process data. Track expiry dates for every application certificate, alert at thirty and at seven days, and rehearse renewal. This is the strongest argument for GDS-driven renewal.

Auto-accept and “temporary” exceptions. Accept-all switches, suppressed validation errors and a SecurityPolicy None endpoint that was added for commissioning tend to survive until the next audit. Put an expiry date and an owner on every exception, and scan for them regularly using a client that lists endpoints.

Trusting the same certificate everywhere. Copying one application certificate and key across a fleet gives you a fleet whose members cannot be told apart, and one compromised device compromises all. Issue one certificate per application instance and never include the private key in a golden image shared between machines.

Private key exposure. Keys stored world-readable on disk, committed to a repository or sent by email undermine everything above them. Use file permissions, a secrets store, or a TPM or HSM where the platform supports it.

Clock problems. Certificate validation depends on time. A device with a wrong clock, for instance after a battery failure, rejects valid certificates or accepts expired ones. Keep NTP or PTP healthy on OT networks.

Compatibility pressure. The most common reason for weak settings is a single legacy client that supports only an old policy. Resist enabling the old policy globally. Isolate that client through a gateway, or enable the old policy on a separate endpoint reachable only from its address, and plan its replacement.

Operational cost of security. Stronger policies cost CPU on small controllers. RSA handshakes can be noticeable on modest hardware, though bulk symmetric encryption is cheap. Measure handshake time and session count on the real device before deciding, and prefer session reuse over frequent reconnects.

Security by perimeter alone. An OPC UA server on a segmented network may feel safe, but lateral movement is exactly how plant incidents spread. Application-level authentication and encryption are your second line when segmentation fails, which is why IEC 62443 treats defence in depth as a requirement and not an option.

Practical Recommendations

Start with inventory. List every OPC UA endpoint, its modes, policies and token types, using GetEndpoints against each server. This single step usually turns up policy None endpoints and anonymous tokens, and it gives you a baseline to measure improvement against.

Then fix the three layers in order of impact. At the channel, offer only SignAndEncrypt with Aes256_Sha256_RsaPss, Aes128_Sha256_RsaOaep or Basic256Sha256, and remove the older policies except by documented exception. At the trust layer, switch off auto-accept, make the rejected-store workflow the commissioning path, and move to a private CA once you pass a handful of servers. At the identity layer, remove anonymous write, issue per-application accounts, and review role mappings.

Once the manual process works, automate. Adopt a GDS, using pull for PC-class software and push where the device supports it, and keep CRL distribution inside that process. Add PubSub SecurityGroups if you publish to a broker. Feed audit events to central logging and alert on rejections.

A short checklist you can paste into a review:

  • Every production endpoint offers SignAndEncrypt only; no SecurityPolicy None.
  • Basic128Rsa15 and Basic256 are disabled, or tracked as exceptions with an end date.
  • Auto-accept is off everywhere, verified by a test with an unknown certificate.
  • Each application has its own certificate with a matching ApplicationUri and correct DNS and IP SAN entries.
  • Certificate expiry is monitored with alerts at 30 and 7 days.
  • Private keys are protected by file permissions or hardware, and are not shared across machines.
  • Anonymous sessions are off or read-only on non-sensitive nodes; no shared operator accounts.
  • Roles and node permissions have been reviewed; configuration methods need an admin role.
  • CRLs are distributed and the fail-open or fail-closed decision is documented.
  • Audit events go to central logging with alerts for repeated rejections and activation failures.
  • PubSub uses separate SecurityGroups and SKS access is limited by role.

Frequently Asked Questions

What is the difference between SecurityMode and SecurityPolicy in OPC UA?

SecurityMode says what protection is applied to messages: None, Sign, or SignAndEncrypt. SecurityPolicy says which algorithms and key lengths are used to do it, for example Basic256Sha256 or Aes256_Sha256_RsaPss. An endpoint is defined by the pair. So Sign with Basic256Sha256 and SignAndEncrypt with Basic256Sha256 are two distinct endpoints. For production, choose SignAndEncrypt and a current policy, and make sure both client and server support the same combination.

Do I need a CA, or can I use self-signed OPC UA certificates?

Self-signed certificates work and are common in small installations, but each one must be added to every peer’s trusted store, and renewal repeats that work. A private CA lets peers trust one root and accept any application certificate it issues, which scales much better and supports revocation through CRLs. The trade-off is that the CA key becomes critical and must be protected. As a rule of thumb, move to a CA when manual trust changes become routine.

What is the OPC UA trust list?

The trust list is the set of certificate stores an application uses to decide whom to trust: trusted application certificates, trusted issuer CAs, their revocation lists, and a rejected store for refused certificates. A peer certificate is accepted if it is trusted directly or chains to a trusted issuer and is not revoked. In Part 12 the trust list is also an object that a GDS or admin tool can read and update remotely.

What does the Global Discovery Server do?

The GDS, defined in OPC UA Part 12, registers applications and manages their certificates and trust lists centrally. In pull management the application requests a certificate, for instance through StartSigningRequest and FinishRequest. In push management an admin tool or the GDS updates the application with UpdateCertificate and TrustList methods. Its value is automated renewal and consistent trust distribution. Check that your devices implement the model you plan to use.

Is anonymous access ever acceptable in OPC UA?

It can be, for read-only access to low-sensitivity data on a segmented network, with the application certificate still validated. It is not acceptable for write access or for access to configuration methods. Even for read-only use, consider that anonymous access removes attribution in audit logs. The safer default is to disable anonymous tokens and issue named accounts, then permit anonymous only where a documented reason exists.

How does OPC UA PubSub secure messages without sessions?

PubSub signs and optionally encrypts NetworkMessages using symmetric keys and policies such as PubSub-Aes256-CTR. Keys belong to SecurityGroups and are distributed by a Security Key Service. Publishers and subscribers call GetSecurityKeys over an encrypted client-server session to obtain current keys and rotation timing. Because anyone who can fetch a group’s keys can read and forge its messages, restrict SecurityGroup access by role and use separate groups per trust domain.

Further Reading

By Riju — about

Comments

No comments yet. Why don’t you start the discussion?

Leave a Reply

Your email address will not be published. Required fields are marked *