Zum Hauptinhalt springen

Sicherheitseinstellungen

Übersicht​

Dieses Dokument beschreibt die Konfiguration der Sicherheitseinstellungen der PlantPulse Platform. Durch Sicherheitseinstellungen auf Netzwerk-, Übertragungs-, Anwendungs- und Datenebene können Sie die Platform sicher betreiben.

Wichtig: Bei industriellen Steuerungssystemen (ICS) hat die Verfügbarkeit höchste Priorität. Testen Sie Änderungen an Sicherheitseinstellungen daher unbedingt in einer Testumgebung, bevor Sie sie im Produktivbetrieb anwenden.

Sicherheitsarchitektur​

Die Sicherheit von PlantPulse besteht aus 4 Schichten.

SchichtSicherheitsbereichHauptfunktion
1. NetzwerkFirewall, Segmentierung, VPNBlockierung externer Zugriffe, Netzwerktrennung
2. ÜbertragungHTTPS/TLS, VerschlüsselungVerschlüsselung der Kommunikationsdaten
3. AnwendungAuthentifizierung, Autorisierung, Filter, SessionBenutzerauthentifizierung und Berechtigungsverwaltung
4. DatenDB-Zugriffskontrolle, VerschlüsselungSchutz gespeicherter Daten

TLS / Zertifikate (automatische Verwaltung)​

Das Erstellen der Zertifikate übernimmt ein einziger plantpulse-certs-Container. Er läuft als Erstes, wenn der Stack hochfährt, erzeugt alle Materialien mit pd_ssl_prepare (plantpulse-datalake-cli von lib/lib-ssl.sh) und schreibt sie in das pp-security-Volume. Danach bleibt er aktiv und prüft alle 6 Stunden auf Ablauf; nur wenn der Ablauf in weniger als 30 Tagen bevorsteht, wird neu ausgestellt. Alle übrigen Container mounten dieses Volume read-only.

ElementWert
Ausstellerplantpulse-certs (dauerhaft aktiv — (healthy) ist normal)
MaterialpfadVolume pp-security, Pfad im Container /var/security/plantpulse
Lesende SeiteDatalake und Anwendung — alle :ro
AusnahmeNur plantpulse-plugin-opcua hat Lese-/Schreibzugriff, da beim Start ein sicheres temporäres Verzeichnis angelegt werden muss
KonfigurationUmgebungsvariable PP_TLS_* im compose
Bei einem Neustart werden keine neuen Zertifikate erzeugt

pd_ssl_prepare stellt nur dann neu aus, wenn der angeforderte SAN sich unterscheidet von dem, was in .certs_generated vermerkt ist. Daher ändert sich das Material bei einem Container-Neustart nicht. Falls eine Neuausstellung nötig ist, führen Sie dies mit dem unten beschriebenen Verfahren explizit durch.

Es handelt sich um restart: unless-stopped — falls ein Mensch den Vorgang absichtlich gestoppt hat, bleibt er auch nach einem Neustart des Docker-Daemons gestoppt. So bleibt ein während der Incident Response manuell eingespieltes Material erhalten.

env.sh TLS-Variablen​

VariableStandardBeschreibung
PP_TLS_ENABLEDtrueTLS aktivieren
PP_TLS_CERT_DIR/var/security/plantpulseZertifikatsverzeichnis
PP_TLS_DOMAINplantpulse.ioStandarddomäne
PP_TLS_KEYSTORE_PASSWORDkopens123! (muss geändert werden)Keystore-Passwort
PP_TLS_TRUSTSTORE_PASSWORD${PP_TLS_KEYSTORE_PASSWORD}Truststore-Passwort
PP_TLS_VALID_DAYS3650Gültigkeitsdauer (Tage)
PP_TLS_EC_GROUPsecp256r1ECDSA-Kurve
PP_TLS_SIGALGSHA256withECDSASignaturalgorithmus
PP_TLS_OPCUA_APP_URIurn:plantpulse:opcua:serverOPC-UA Application URI
PP_TLS_NODE_NAMESmaster worker-1 ... worker-5Cluster-Knotenname
PP_TLS_SAN_DNSlocalhost,<hostname>,<domain>SAN DNS
PP_TLS_SAN_IPS<HOST_IP>,<SERVICE_IP>,<PUBLIC_IP>,127.0.0.1SAN IP (NAT-/externe IP muss enthalten sein)
PP_TLS_FORCE_REGENERATEfalseBei true erzwungene Neuerstellung

Verfahren zur Zertifikatserstellung​

Da dies bei der Installation automatisch einmal durchgeführt wird, ist normalerweise keine weitere Aktion nötig. Führen Sie das Folgende nur aus, wenn sich der SAN geändert hat oder das Zertifikat abgelaufen bzw. beschädigt ist.

cd /opt/kopens/plantpulse-platform-docker

# 1. compose 의 PP_TLS_* 검토 (외부 노출 IP/도메인 포함)
vi compose/docker-compose.yml

# 2. 강제 재생성 — 인증서 컨테이너를 다시 만듭니다
PP_TLS_FORCE_REGENERATE=true \
docker compose -f compose/docker-compose.yml up --force-recreate plantpulse-certs

# 3. 자재를 읽는 쪽을 재시작
bin/restart.sh
Ist auch nur eine ungültige IP im SAN enthalten, wird «kein einziges» Zertifikat erzeugt

PP_TLS_SAN_IPS wird aus PP_HOST_IP · PP_SERVICE_IP · PP_MASTER_IP · PP_PUBLIC_IP · 127.0.0.1 zusammengesetzt. Schon ein einziger Wert mit falschem Format führt dazu, dass openssl die gesamte Extension-Datei ablehnt. Füllen Sie DOCKER_PP_EXTERNAL_IP nicht auf einer Nicht-NAT-Box aus.

Fehlt der Name, den die Anwendung beim Verbinden zum Backend verwendet, im SAN, äußert sich das nur als «TimeoutException»

Nirgendwo im Log erscheint ein Hinweis auf das Zertifikat. Der Standardwert von PP_TLS_SAN_DNS umfasst einundzwanzig Einträge in drei Gruppen.

GruppeAnzahlNamen
Aktuell verwendete Containernamen8localhost · plantpulse-datalake · plantpulse-server-web · plantpulse-batch-web · plantpulse-warehouse-web · plantpulse-plugin-opcua · plantpulse-plugin-aasx · plantpulse-proxy
Alte Namen vor der Umbenennung8plantpulse-warehouse · plantpulse-server · plantpulse-batch · plantpulse-warehouse-s3 · plantpulse-opcua · plantpulse-aasx · plantpulse-plugin-opcua-server · plantpulse-plugin-aasx-server
Worker-Knoten5plantpulse-datalake-worker-1 ~ -5
Die Worker-Namen wurden am 2026-09-09 hinzugefügt

Bis dahin ausgestellte Zertifikate enthielten keinen einzigen Worker, und auch keine Worker-IPs im IP-SAN (dev 41 gemessen: unter 16 DNS-Namen 0 Worker). Dadurch hatten Clients beim TLS-Handshake mit einem Worker weder einen zu validierenden Namen noch eine Adresse. Cluster, die noch mit vorher erzeugten Zertifikaten arbeiten, müssen neu ausstellen.

Wenn Sie Containernamen geändert oder Backend-Hosts direkt angegeben haben, müssen Sie auch die SAN-Liste entsprechend anpassen. Wenn Sie auf sechs oder mehr Worker skaliert haben, müssen Sie diese Namen zu PP_TLS_SAN_DNS hinzufügen — der Standardwert deckt nur bis fünf ab. :::

Von pd_ssl_prepare erzeugte Artefakte:

/var/security/plantpulse/
├── ca.crt / ca.key # Self-signed CA
├── server.jks / server.crt # Tomcat (서버)
├── cassandra.jks # Cassandra internode + client
├── kafka.jks # Kafka broker
├── mqtt.jks # HiveMQ
├── opcua/ # OPC-UA
│ ├── server.pem / server.key
│ └── private/ rejected/ trusted/
└── client/ # 클라이언트 인증서 (선택)

Verwendung eines externen CA-Zertifikats​

Um statt Selbstsignierung ein firmeneigenes Zertifikat bzw. ein Let's Encrypt CA-Zertifikat zu verwenden:

# 1. CA 발급 인증서를 JKS keystore 로 변환
keytool -importcert -alias rootca -file company-ca.pem \
-keystore /var/security/plantpulse/server.jks \
-storepass ${PP_TLS_KEYSTORE_PASSWORD}

keytool -importkeystore -srckeystore company.p12 -srcstoretype PKCS12 \
-destkeystore /var/security/plantpulse/server.jks \
-deststoretype JKS -deststorepass ${PP_TLS_KEYSTORE_PASSWORD}

# 2. PP_TLS_FORCE_REGENERATE 가 false 인 상태로 재시작
./restart.sh

Ablaufüberwachung: Richten Sie eine externe Überwachung (openssl x509 -enddate) ein, damit 30 Tage vor Ablauf des Zertifikats ein Alarm ausgelöst wird.


Web-Sicherheitsfilter​

PlantPulse schützt Webanfragen über in web.xml definierte Sicherheitsfilter.

SecurityFilter​

Der zentrale Filter, der den Zugriff nicht authentifizierter Benutzer blockiert.

<!-- WEB-INF/web.xml inside the webapp (bundled in the WAR — reset on deploy, so change it in the source) -->
<filter>
<filter-name>SecurityFilter</filter-name>
<filter-class>com.kopens.plantpulse.server.web.filter.SecurityFilter</filter-class>
<init-param>
<param-name>excludeUrls</param-name>
<param-value>/api/v5/ping,/login,/css/,/js/,/images/</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>SecurityFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
  • Prüft bei jeder Anfrage (/*) den Authentifizierungsstatus.
  • Auf die in excludeUrls angegebenen Pfade kann ohne Authentifizierung zugegriffen werden.
  • Nicht authentifizierte Anfragen werden zur Login-Seite umgeleitet.

XSSFilter​

Ein Filter zur Verhinderung von Cross-Site-Scripting(XSS)-Angriffen.

<filter>
<filter-name>XSSFilter</filter-name>
<filter-class>com.kopens.plantpulse.server.web.filter.XSSFilter</filter-class>
<init-param>
<param-name>excludeUrls</param-name>
<param-value>/api/</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>XSSFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
  • Entfernt gefährliche Tags wie <script>, <iframe> aus den Anfrageparametern.
  • Da für API-Pfade eine separate Eingabevalidierung gilt, können diese vom Filter ausgeschlossen werden.

CSRF-Schutz​

Zur Abwehr von Cross-Site-Request-Forgery(CSRF)-Angriffen wird eine tokenbasierte Prüfung durchgeführt.

<filter>
<filter-name>CSRFFilter</filter-name>
<filter-class>com.kopens.plantpulse.server.web.filter.CSRFFilter</filter-class>
<init-param>
<param-name>excludeUrls</param-name>
<param-value>/api/v5/</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>CSRFFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
  • Prüft das CSRF-Token bei POST-, PUT- und DELETE-Anfragen.
  • Bei Formularanfragen ist der Parameter _csrf oder der Header X-CSRF-TOKEN erforderlich.
  • REST APIs (/api/v5/) verwenden Token-Authentifizierung und sind daher vom CSRF-Filter ausgenommen.

EncodingFilter​

Vereinheitlicht die Zeichenkodierung, um kodierungsbezogene Sicherheitslücken zu vermeiden.

<filter>
<filter-name>EncodingFilter</filter-name>
<filter-class>com.kopens.plantpulse.server.web.filter.EncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
<init-param>
<param-name>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>EncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

HTTPS-Einstellungen​

Die für Benutzer sichtbaren Ports 80/443 werden von plantpulse-proxy (nginx) terminiert. Der Webserver-Container öffnet keinen Host-Port; der Ort, an dem das für den Browser sichtbare Zertifikat geändert wird, ist daher der Proxy, nicht Tomcat.

Proxy-Zertifikat — Standard ist selbstsigniert​

Unmittelbar nach der Installation ist Port 443 ohne jede Vorbereitung sofort geöffnet. Der Proxy erzeugt beim ersten Start ein selbstsigniertes Zertifikat und legt es im Volume pp-proxy-certs ab.

Bestehende Zertifikate werden nicht überschrieben

Da es ein stiller Störfall wäre, wenn ein Neustart das echte Zertifikat des Kunden auf ein selbstsigniertes zurücksetzt, verwendet der Proxy die vorhandene Datei weiter, falls sie bereits existiert.

Austausch gegen ein echtes Zertifikat​

Die nginx-Konfiguration muss nicht angepasst werden — die Pfade sind fest, es genügt, die Dateien auszutauschen.

cd /opt/kopens/plantpulse-platform-docker

# pp-proxy-certs 볼륨의 server.crt / server.key 를 교체
docker cp /path/to/fullchain.crt plantpulse-proxy:/etc/nginx/certs/server.crt
docker cp /path/to/private.key plantpulse-proxy:/etc/nginx/certs/server.key

docker compose -f compose/docker-compose.yml restart plantpulse-proxy
ElementWert
Volumepp-proxy-certs
Pfad im Container/etc/nginx/certs/server.crt · /etc/nginx/certs/server.key
Selbstsignierter CNPP_PROXY_CERT_CN (Standard plantpulse.local)

Legen Sie die Zertifikatsdatei als Kette (Fullchain) inklusive Zwischenzertifikat ab.

MQTT TLS (1884) wird nicht vom Proxy terminiert

Der Proxy leitet 1884 als Passthrough weiter, und der Broker terminiert das TLS. Auch bei einem Wechsel des Proxy-Zertifikats ändert sich daher das vom MQTT-Client geprüfte Zertifikat nicht — dort wird das von plantpulse-certs erzeugte Material verwendet.

(Referenz) Direktes Zertifikat am Webserver​

In der Container-Auslieferung in der Regel nicht erforderlich

Das Folgende dient als Referenz für Umgebungen, in denen der Webserver ohne Proxy direkt exponiert wird. In der Standardkonfiguration genügt es, nur das Proxy-Zertifikat oben zu ändern. Das TLS-Material für die interne Backend-Strecke im Container wird bereits von plantpulse-certs erzeugt.

Um HTTPS zu verwenden, muss ein SSL-Zertifikat installiert werden.

# 1. 키스토어 생성 (자체 서명 인증서 - 테스트용)
keytool -genkeypair -alias plantpulse -keyalg RSA -keysize 2048 \
-validity 365 -keystore /opt/kopens/plantpulse-platform/plantpulse-server/server/conf/keystore.jks \
-storepass changeit -keypass changeit \
-dname "CN=plantpulse.example.com, OU=IT, O=Company, L=Seoul, ST=Seoul, C=KR"

# 2. 공인 인증서 가져오기 (운영 환경)
keytool -importcert -alias plantpulse -file /path/to/certificate.crt \
-keystore /opt/kopens/plantpulse-platform/plantpulse-server/server/conf/keystore.jks \
-storepass changeit

# 3. 중간 인증서(Chain) 가져오기
keytool -importcert -alias intermediate -file /path/to/intermediate.crt \
-keystore /opt/kopens/plantpulse-platform/plantpulse-server/server/conf/keystore.jks \
-storepass changeit

# 4. 인증서 확인
keytool -list -v -keystore /opt/kopens/plantpulse-platform/plantpulse-server/server/conf/keystore.jks \
-storepass changeit

Tomcat HTTPS-Connector​

Fügen Sie in server.xml von Tomcat einen HTTPS-Connector hinzu.

<!-- plantpulse-server/server/conf/server.xml -->
<!-- HTTP connector (redirects to HTTPS) -->
<Connector port="80" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="443" />

<!-- HTTPS connector -->
<Connector port="443" protocol="org.apache.coyote.http11.Http11NioProtocol"
maxThreads="200"
SSLEnabled="true"
scheme="https"
secure="true"
keystoreFile="/opt/kopens/plantpulse-platform/plantpulse-server/server/conf/keystore.jks"
keystorePass="changeit"
clientAuth="false"
sslProtocol="TLSv1.2"
sslEnabledProtocols="TLSv1.2,TLSv1.3"
ciphers="TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256" />

Weiterleitung von HTTP zu HTTPS​

Leitet alle HTTP-Anfragen automatisch zu HTTPS weiter.

<!-- WEB-INF/web.xml inside the webapp (bundled in the WAR — reset on deploy, so change it in the source) -->
<security-constraint>
<web-resource-collection>
<web-resource-name>Secure</web-resource-name>
<url-pattern>/*</url-pattern>
</web-resource-collection>
<user-data-constraint>
<transport-guarantee>CONFIDENTIAL</transport-guarantee>
</user-data-constraint>
</security-constraint>

TLS-Versionsverwaltung​

Aus Sicherheitsgründen wird empfohlen, nur TLS 1.2 oder höher zuzulassen.

TLS-VersionStatusEmpfehlung
SSLv3VerbotenPOODLE-Schwachstelle
TLS 1.0VerbotenVorhandene Schwachstellen
TLS 1.1VerbotenVorhandene Schwachstellen
TLS 1.2ErlaubtEmpfohlen
TLS 1.3ErlaubtAktuellster, sicherster Standard

API-Authentifizierung​

Die PlantPulse REST API (/api/v5) wird mit einem api_key im Header aufgerufen. Es gibt zwei Wege, einen api_key zu erhalten; die anschließende Aufrufweise ist identisch.

api_key ist gleichzeitig der Token-Wert

/api/v5/auth erzeugt keinen neuen String. Ist der übermittelte Token gültig, wird derselbe Token-Wert unverändert als api_key zurückgegeben (APIManager — api_key = token.getToken()). Der Token liegt im UUID-Format vor und ist kein JWT.

Token-Authentifizierung (externe Integration · Automatisierung)​

Erhält den api_key mit einem im Voraus ausgestellten API-Token. Dies ist der Weg für produktive Integrationen.

# 1. api_key 발급 — 인증 헤더 없이 호출 가능(V5_Filter bypass 경로)
curl -X POST http://localhost/api/v5/auth \
-H "Content-Type: application/json" \
-d '{"token": "<API_TOKEN>"}'
# 200: {"data": {"api_key": "..."}, "meta": {"status": "OK", ...}, "errors": null}

# 2. 이후 모든 호출에 헤더로 싣는다
curl -X GET http://localhost/api/v5/asset \
-H "X-API-Key: <api_key>"
AntwortBedeutung
200Ausstellung erfolgreich
400Body ist kein JSON oder token fehlt
401 (E1100)Token ungültig
429 (E1101)Brute-Force-Sperre. Der Header Retry-After wird mitgesendet

Der Brute-Force-Schutz besteht aus einem einzigen IP-basierten Zähler. Bei jedem Fehlversuch wird der Zähler für diese IP erhöht; wird der Grenzwert innerhalb des Zeitfensters überschritten, erfolgt eine Sperre mit 429. Einstellbar über plantpulse-engine.properties.

PropertyStandardwert
engine.api.v5.auth.bruteforce.enabledtrue
engine.api.v5.auth.bruteforce.ip_limit30
engine.api.v5.auth.bruteforce.window.seconds60 — hat, falls vorhanden, Vorrang vor window.minutes
engine.api.v5.auth.bruteforce.window.minutes15
Zwei Header-Varianten

X-API-Key hat Priorität; ist er nicht vorhanden, wird als Fallback Authorization: Bearer <api_key> ausgelesen. Der Bearer-Fallback ist der Standardweg für MCP-Clients, die nur diesen Header senden können; der Wert ist derselbe api_key. Andere Schemata als Bearer werden ignoriert.

ID/Passwort-Authentifizierung (nur für die Konsolen-SPA)​

Erhält den api_key durch Anmeldung mit Benutzername und Passwort. Dies ist der von der Konsolenoberfläche verwendete Weg — verwenden Sie für Server-zu-Server-Integrationen die Token-Authentifizierung, da sonst Klartext-Anmeldedaten bei jedem Aufruf mitgesendet werden und die Brute-Force-Angriffsfläche größer wird.

curl -X POST http://localhost/api/v5/auth/login \
-H "Content-Type: application/json" \
-d '{"userId": "admin", "password": "<PASSWORD>"}'
# 200: {"api_key": "...", "expires_at": 1790000000000, "login_id": "admin"}
# 401: {"error": "..."}
Nur diese Antwort verwendet kein Envelope

Andere V5-Endpunkte verwenden das {data, meta, errors}-Format, aber /api/v5/auth/login liefert sowohl bei Erfolg als auch bei Fehler reines JSON ohne Envelope. Falls Sie einen gemeinsamen Parser verwenden, behandeln Sie diesen Pfad gesondert. expires_at ist in Epoch-Millisekunden angegeben, bei einem permanenten Token ist es null.

API zur Ausstellung/zum Widerruf von Token (/api/v5/token)​

Führt per REST dieselben Vorgänge aus wie der Konsolenbereich System > API-Token (/token/index). Wird von automatisierten Provisionierungsskripten zum Erstellen und Löschen von Token verwendet. Zur Bedienung über die Oberfläche siehe Sicherheitsverwaltung.

HTTPPfadFunktion
GET/api/v5/tokenToken-Liste — der Wert von token ist maskiert (erste 8 Zeichen + ****)
GET/api/v5/token/{token}Einzelabfrage — maskiert
POST/api/v5/tokenAusstellung
DELETE/api/v5/token/{token}Widerruf (revoke) — ausführbar durch den X-API-Key-Eigentümer
POST/api/v5/admin/tokens/{token}/revokeWiderruf nur für ADMIN — zum Sperren fremder Token
Warum es zwei Widerrufspfade gibt

DELETE /api/v5/token/{token} dient dazu, eigene Token zu bereinigen, und POST /api/v5/admin/tokens/{token}/revoke dient dazu, dass ein Administrator fremde Token sperrt. Letzterer akzeptiert nur die Rolle ADMIN — mit einem Token der Rolle API ergibt der Aufruf 403. In der Incident Response wird Letzterer verwendet.

Beide vermerken revoked_at und invalidieren den Token-Cache, wodurch die Sperre sofort wirksam wird.

curl -X POST http://localhost/api/v5/token \
-H "X-API-Key: <ADMIN_API_KEY>" \
-H "Content-Type: application/json" \
-d '{"login_id": "svc-mes", "ip": "192.168.0.0/24",
"description": "MES", "ttl_days": 0}'
Body-FeldBeschreibung
login_idKonto, das den Token erhält. Wird es weggelassen, wird der Token für den Aufrufer (den Eigentümer von X-API-Key) ausgestellt
ipIP-Muster, für die die Verwendung des Tokens erlaubt ist
descriptionVerwendungszweck-Notiz
ttl_daysGültigkeitsdauer (Tage). Wird sie weggelassen, gilt der globale Standardwert (engine.api.token.default_ttl_days); bei 0 ist der Token permanent, bei einem positiven Wert gilt diese Anzahl Tage
Ausstellung ist nur mit ADMIN-Rolle möglich

Hat das anfragende Konto (der Eigentümer von X-API-Key) nicht die Rolle ADMIN, folgt 403 E1200, und im Audit-Log wird ein Eintrag TOKEN_ISSUE_DENIED vermerkt. Damit soll verhindert werden, dass ein API-Rollenkonto sich selbst oder fremde Konten durch Rechteausweitung Token ausstellen kann. Die Rolle des Zielkontos (login_id) spielt dabei keine Rolle. ttl_days erweitert diese Berechtigung nicht.

Warum ttl_days nötig war

Würde man den globalen Standardwert auf 0 (permanent) ändern, würden auch alle vom Kunden über die Oberfläche erstellten Token permanent. Dieses Feld existiert, damit man für die Provisionierung nur einen einzelnen permanenten Token benötigt, ohne den globalen Wert anfassen zu müssen. Negative oder nicht-ganzzahlige Werte führen zu 400 VALIDATION_FAILED — da es riskanter wäre, ungültige Werte stillschweigend zu akzeptieren und dadurch versehentlich permanente Token zu erzeugen.

Die erfolgreiche Ausstellungsantwort enthält den Token-Klartext genau einmal. In der Liste oder Einzelabfrage erscheint er nicht erneut — sichern Sie ihn also sofort. Die Ausstellung wird im Audit als TOKEN_ISSUED vermerkt; bei permanenten Token wird an der Stelle des Ablaufdatums PERMANENT eingetragen.


Passwortsicherheit​

Passwort-Hashing​

PlantPulse speichert Benutzerpasswörter als SHA-256-Hash. Das Originalpasswort wird nicht auf dem Server gespeichert.

Empfohlene Passwortrichtlinie​

ElementEmpfehlungBeschreibung
Mindestlängemindestens 8 Zeichen12 Zeichen oder mehr empfohlen
KomplexitätKombination aus mindestens 3 ZeichentypenGroß-/Kleinbuchstaben, Zahlen, Sonderzeichen — mindestens 3 davon
Änderungsintervall90 Tagevierteljährliche Änderung
Historienverwaltungletzte 3Wiederverwendung früherer Passwörter untersagt
Sperre nach Fehlversuchen5 VersucheKontosperre nach 5 aufeinanderfolgenden Fehlversuchen
Entsperrennach 30 Minuten oder durch Administratorautomatische oder manuelle Entsperrung

HTTP-Sicherheitsheader​

Zur Aktivierung der Sicherheitsfunktionen des Webbrowsers werden HTTP-Antwortheader konfiguriert.

Empfohlene Sicherheitsheader​

HeaderWertBeschreibung
X-XSS-Protection1; mode=blockAktiviert den XSS-Filter des Browsers
X-Content-Type-OptionsnosniffVerhindert MIME-Type-Sniffing
X-Frame-OptionsSAMEORIGINVerhindert Clickjacking
Content-Security-Policydefault-src 'self'Beschränkt Inhaltsquellen
Strict-Transport-Securitymax-age=31536000; includeSubDomainsErzwingt HTTPS (HSTS)
Referrer-Policystrict-origin-when-cross-originBeschränkt Referrer-Informationen

Nginx-Konfigurationsbeispiel​

Wenn Nginx als Reverse-Proxy verwendet wird, fügen Sie die Sicherheitsheader wie folgt hinzu.

server {
listen 443 ssl http2;
server_name plantpulse.example.com;

# SSL 인증서
ssl_certificate /etc/ssl/certs/plantpulse.crt;
ssl_certificate_key /etc/ssl/private/plantpulse.key;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;

# 보안 헤더
add_header X-XSS-Protection "1; mode=block" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src 'self' data:;" always;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;

location / {
proxy_pass http://127.0.0.1:80;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}

# HTTP → HTTPS 리다이렉트
server {
listen 80;
server_name plantpulse.example.com;
return 301 https://$host$request_uri;
}

Einschränkung von HTTP-Methoden​

Blockiert unnötige HTTP-Methoden, um Informationslecks zu verhindern.

<!-- WEB-INF/web.xml inside the webapp (bundled in the WAR — reset on deploy, so change it in the source) -->
<security-constraint>
<web-resource-collection>
<web-resource-name>RestrictMethods</web-resource-name>
<url-pattern>/*</url-pattern>
<http-method>OPTIONS</http-method>
<http-method>TRACE</http-method>
<http-method>HEAD</http-method>
</web-resource-collection>
<auth-constraint/>
</security-constraint>

Hinweis: Die Methode OPTIONS wird für CORS-Preflight-Anfragen verwendet. Bei erforderlicher API-Integration mit externen Systemen muss die OPTIONS-Methode ggf. zugelassen werden.


Sitzungssicherheit​

<!-- WEB-INF/web.xml inside the webapp (bundled in the WAR — reset on deploy, so change it in the source) -->
<session-config>
<session-timeout>30</session-timeout>
<cookie-config>
<http-only>true</http-only> <!-- block cookie access from JavaScript -->
<secure>true</secure> <!-- send cookies over HTTPS only -->
<max-age>1800</max-age> <!-- cookie lifetime (seconds) -->
</cookie-config>
<tracking-mode>COOKIE</tracking-mode>
</session-config>
EinstellungWertBeschreibung
http-onlytrueVerhindert Session-Diebstahl über XSS
securetrueVerhindert die Übertragung von Cookies über HTTP (HTTPS erforderlich)
tracking-modeCOOKIEVerhindert die Offenlegung der Session-ID in der URL

Schutz vor Session Fixation​

PlantPulse erzeugt bei erfolgreichem Login eine neue Session-ID, um Session-Fixation-Angriffe zu verhindern. Diese Funktion ist fest in SecurityFilter integriert und wird automatisch angewendet, ohne dass eine separate Konfiguration erforderlich ist.


Datenbanksicherheit​

PostgreSQL​

Legen Sie in der Datei pg_hba.conf fest, welche Zugriffe erlaubt sind.

# /opt/kopens/plantpulse-platform/plantpulse-storage/db/postgres/data/pg_hba.conf

# TYPE DATABASE USER ADDRESS METHOD
# 로컬 접근 (Unix 소켓)
local all all md5

# 로컬호스트 접근
host all all 127.0.0.1/32 md5
host all all ::1/128 md5

# PlantPulse 서버 접근 (특정 IP만 허용)
host plantpulse plantpulse 10.0.0.0/24 md5

# 그 외 접근 차단 (기본)
# host all all 0.0.0.0/0 reject

Zusätzliche Sicherheitseinstellungen (postgresql.conf):

# 접속 제한
listen_addresses = '127.0.0.1' # 로컬만 허용 (기본값: '*')
max_connections = 200

# 인증 타임아웃
authentication_timeout = 60 # 초

# SSL 활성화
ssl = on
ssl_cert_file = '/path/to/server.crt'
ssl_key_file = '/path/to/server.key'

# 로그
log_connections = on
log_disconnections = on
log_statement = 'ddl' # DDL 문만 로깅

Aster TSDB​

# /opt/kopens/plantpulse-platform/plantpulse-storage/db/cassandra/conf/cassandra.yaml

# 인증 활성화
authenticator: PasswordAuthenticator

# 인가 활성화
authorizer: CassandraAuthorizer

# 클라이언트 암호화 (TLS)
client_encryption_options:
enabled: true
optional: false
keystore: /opt/kopens/plantpulse-platform/plantpulse-storage/db/cassandra/conf/.keystore
keystore_password: changeit
truststore: /opt/kopens/plantpulse-platform/plantpulse-storage/db/cassandra/conf/.truststore
truststore_password: changeit
protocol: TLS
algorithm: SunX509
cipher_suites: [TLS_RSA_WITH_AES_256_CBC_SHA]

# 노드 간 암호화
server_encryption_options:
internode_encryption: all
keystore: /opt/kopens/plantpulse-platform/plantpulse-storage/db/cassandra/conf/.keystore
keystore_password: changeit
truststore: /opt/kopens/plantpulse-platform/plantpulse-storage/db/cassandra/conf/.truststore
truststore_password: changeit

Redis (Valkey)​

# /opt/kopens/plantpulse-platform/plantpulse-storage/db/valkey/conf/valkey.conf

# 비밀번호 설정
requirepass your_strong_password_here

# 바인드 주소 (로컬만 허용)
bind 127.0.0.1

# 보호 모드 활성화
protected-mode yes

# 위험 명령어 비활성화
rename-command FLUSHALL ""
rename-command FLUSHDB ""
rename-command CONFIG ""
rename-command DEBUG ""
rename-command SHUTDOWN PLANTPULSE_SHUTDOWN

Netzwerksicherheit​

Firewall-Konfiguration​

Lassen Sie in der Firewall nur die Ports zu, die für externe Zugriffe auf PlantPulse tatsächlich benötigt werden.

# firewalld 설정 예시

# 웹 서비스 (외부 접근 허용)
sudo firewall-cmd --permanent --add-port=80/tcp
sudo firewall-cmd --permanent --add-port=443/tcp

# 내부 서비스 (외부 접근 차단 - 기본값)
# 아래 포트들은 외부에서 접근하지 않도록 해 주세요
# PostgreSQL: 5432, Redis: 6379, Cassandra: 9042
# Kafka: 9092, MQTT: 1883

# OPC Agent (필요한 클라이언트 IP만 허용)
sudo firewall-cmd --permanent --add-rich-rule='
rule family="ipv4"
source address="10.0.1.0/24"
port protocol="tcp" port="60000"
accept'

# 적용
sudo firewall-cmd --reload

# 확인
sudo firewall-cmd --list-all

Netzwerksegmentierung​

In industriellen Umgebungen wird empfohlen, das Netzwerk wie folgt zu trennen.

NetzwerkbereichVerwendungszweckBeispiel
OT-NetzwerkPLC, SCADA, Sensoren10.0.1.0/24
DMZPlantPulse-Webserver, API10.0.2.0/24
IT-NetzwerkBenutzerzugriff, Verwaltung10.0.3.0/24
DatennetzwerkDB, Messaging10.0.4.0/24
  • Achten Sie darauf, dass OT-Netzwerk und IT-Netzwerk nicht direkt miteinander kommunizieren.
  • Platzieren Sie den PlantPulse-Server in der DMZ und erlauben Sie nur eingeschränkte Kommunikation mit beiden Netzwerken.
  • Platzieren Sie die Datenbank im Datennetzwerk und erlauben Sie den Zugriff nur vom PlantPulse-Server aus.

VPN-Zugriff​

Falls ein Fernzugriff auf die Verwaltungskonsole erforderlich ist, konfigurieren Sie den Zugriff über VPN.

  • Erlauben Sie den Zugriff auf die Verwaltungskonsole und SSH ausschließlich über VPN.
  • Vergeben Sie VPN-Konten pro Person und vermeiden Sie gemeinsam genutzte Konten.
  • Protokollieren Sie VPN-Zugriffe und überprüfen Sie diese regelmäßig.

Checkliste für Sicherheitsprüfungen​

Tägliche Prüfung​

PrüfpunktPrüfmethode
Ungewöhnliche AnmeldeversucheAnzahl fehlgeschlagener Logins im Audit-Log prüfen
SystemfehlerprotokolleSicherheitsrelevante Fehler-/Warnprotokolle prüfen
Verfügbarkeit der DiensteAntwort der Health-Check-API prüfen

Wöchentliche Prüfung​

PrüfpunktPrüfmethode
Status der BenutzerkontenUngenutzte und inaktive Konten prüfen
Verlauf von BerechtigungsänderungenBerechtigungsänderungen im Audit-Log prüfen
Firewall-ProtokolleBlockierte Zugriffsversuche prüfen
Gültigkeit der SSL-ZertifikateAblaufdatum der Zertifikate prüfen

Monatliche Prüfung​

PrüfpunktPrüfmethode
Sicherheitspatches des BetriebssystemsAnwendung der neuesten Sicherheitsupdates prüfen
Einhaltung der PasswortrichtlinieLange nicht geänderte Passwörter prüfen
DB-ZugriffsrechteZugriffskontrollen wie pg_hba.conf prüfen
Sicherheit der Backup-DatenVerschlüsselung und Zugriffsrechte der Backup-Dateien prüfen
Port-ScanUnnötig geöffnete Ports prüfen
TLS-EinstellungenSchwache Protokolle/Cipher-Suites prüfen

Reaktion auf Sicherheitsvorfälle​

Gehen Sie bei einem eingetretenen oder vermuteten Sicherheitsvorfall nach folgendem Verfahren vor.

Schritt 1: Erkennung und Isolation​

# 의심 IP 차단
sudo firewall-cmd --add-rich-rule='rule family="ipv4" source address="의심IP" drop' --permanent
sudo firewall-cmd --reload

# 의심 사용자 계정 비활성화 (관리 콘솔 또는 DB)
# 관리 콘솔: 보안 관리 > 사용자 관리 > 상태 변경

# 의심 계정의 API 토큰을 즉시 회수 (ADMIN 전용 — 아래 참고)
curl -X POST http://localhost/api/v5/admin/tokens/<TOKEN>/revoke \
-H "X-API-Key: <ADMIN_API_KEY>"
Es gibt keine Liste aktiver «Sessions» im Produkt

Weder eine Oberfläche noch eine API listen Login-Sessions auf oder beenden sie zwangsweise. Sessions enden erst durch Ablauf oder einen Server-Neustart. Falls eine sofortige Sperrung erforderlich ist, deaktivieren Sie das Konto und widerrufen Sie den API-Token — das sind derzeit die verfügbaren Mittel.

Wer sich wann angemeldet hat, ersehen Sie aus dem Audit-Log und mittels grep auf system.log im nachfolgenden Schritt 2.

Schritt 2: Analyse​

# 로그인 이력 조회
grep "LOGIN\|LOGOUT\|AUTH" /opt/kopens/plantpulse-platform/plantpulse-server/logs/system.log

# 접근 로그 분석
grep "의심IP" /opt/kopens/plantpulse-platform/plantpulse-server/server/logs/localhost_access_log.*.txt

# DB 접근 로그 (PostgreSQL)
grep "의심IP" /opt/kopens/plantpulse-platform/plantpulse-storage/db/postgres/data/log/postgresql-*.log

Schritt 3: Wiederherstellung​

  • Ändern Sie die Passwörter kompromittierter Konten.
  • Überprüfen Sie die Integrität der betroffenen Systeme.
  • Stellen Sie bei Bedarf Daten aus dem Backup wieder her.

Schritt 4: Nachsorge​

  • Erstellen Sie einen Vorfallbericht.
  • Legen Sie Maßnahmen zur Vermeidung erneuter Vorfälle fest.
  • Verstärken Sie die Sicherheitseinstellungen.
  • Führen Sie eine Sicherheitsschulung für die betroffenen Mitarbeiter durch.

Kontakt: Bei sicherheitsrelevanten Fragen wenden Sie sich bitte an webmaster@kopens.com.