セキュリティ設定
概要
この文書では、PlantPulseプラットフォームのセキュリティ設定方法について説明します。ネットワーク、転送、アプリケーション、データレベルのセキュリティ設定を通じて、プラットフォームを安全に運用できます。
重要: 産業用制御システム(ICS)は可用性が最優先されるため、セキュリティ設定を変更する際は、必ずテスト環境で検証してから運用環境に適用してください。
セキュリティアーキテクチャ
PlantPulseのセキュリティは4つのレイヤーで構成されます。
| レイヤー | セキュリティ領域 | 主な機能 |
|---|---|---|
| 1. ネットワーク | ファイアウォール、セグメンテーション、VPN | 外部アクセス遮断、ネットワーク分離 |
| 2. 転送 | HTTPS/TLS、暗号化 | 通信データの暗号化 |
| 3. アプリケーション | 認証、認可、フィルター、セッション | ユーザー認証および権限管理 |
| 4. データ | DBアクセス制御、暗号化 | 保存データの保護 |
TLS / 証明書 (自動管理)
証明書を発行するのは plantpulse-certs コンテナ1つです。スタックが起動するときに最初に実行され、pd_ssl_prepare(plantpulse-datalake-cli の lib/lib-ssl.sh)ですべてのマテリアルを作成し、pp-security ボリュームに書き込みます。その後は、起動したまま6時間ごとに有効期限を確認し、期限の30日以内に入った場合のみ再発行します。残りのコンテナはそのボリュームを読み取り専用でマウントして使用します。
| 項目 | 値 |
|---|---|
| 発行主体 | plantpulse-certs (常時動作 — (healthy) が正常) |
| マテリアルの場所 | ボリューム pp-security、コンテナ内パス /var/security/plantpulse |
| 読み取り側 | データレイクとアプリ — すべて :ro |
| 例外 | plantpulse-plugin-opcua のみ読み書き。起動時にセキュアな一時ディレクトリを作成する必要があるためです |
| 設定 | compose の PP_TLS_* 環境変数 |
pd_ssl_prepare は、要求されたSANが .certs_generated に記録されたものと異なる場合のみ再発行します。そのため
コンテナを再起動してもマテリアルは変わりません。再発行が必要な場合は、以下の手順で明示的に
実行してください。
restart: unless-stopped です — 人が意図的に停止した場合には、Dockerデーモンを再起動しても
停止したままになります。事故対応中に手動でマテリアルを配置した状態が保持されます。
env.sh TLS変数
| 変数 | デフォルト | 説明 |
|---|---|---|
PP_TLS_ENABLED | true | TLS有効化 |
PP_TLS_CERT_DIR | /var/security/plantpulse | 証明書ディレクトリ |
PP_TLS_DOMAIN | plantpulse.io | デフォルトドメイン |
PP_TLS_KEYSTORE_PASSWORD | kopens123! (変更必須) | keystoreパスワード |
PP_TLS_TRUSTSTORE_PASSWORD | ${PP_TLS_KEYSTORE_PASSWORD} | truststoreパスワード |
PP_TLS_VALID_DAYS | 3650 | 有効期間 (日) |
PP_TLS_EC_GROUP | secp256r1 | ECDSA曲線 |
PP_TLS_SIGALG | SHA256withECDSA | 署名アルゴリズム |
PP_TLS_OPCUA_APP_URI | urn:plantpulse:opcua:server | OPC-UA Application URI |
PP_TLS_NODE_NAMES | master worker-1 ... worker-5 | クラスタノード名 |
PP_TLS_SAN_DNS | localhost,<hostname>,<domain> | SAN DNS |
PP_TLS_SAN_IPS | <HOST_IP>,<SERVICE_IP>,<PUBLIC_IP>,127.0.0.1 | SAN IP (NAT/外部IPを含む必須) |
PP_TLS_FORCE_REGENERATE | false | trueの場合、強制再生成 |
証明書生成手順
インストール時に自動で一度実行されるため、通常は何もする必要はありません。SANが変更された場合、または有効期限切れ・破損した場合のみ、以下を実行します。
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
PP_TLS_SAN_IPS は PP_HOST_IP · PP_SERVICE_IP · PP_MASTER_IP · PP_PUBLIC_IP · 127.0.0.1 から組み立てられます。形式が合わない値が1つでもあると、opensslが拡張ファイル全体を拒否します。DOCKER_PP_EXTERNAL_IP をNATではないボックスで入力しないでください。
ログのどこにも証明書の記載は出てきません。PP_TLS_SAN_DNS のデフォルト値は21個で
3つのグループです。
| グループ | 個数 | 名前 |
|---|---|---|
| 現在使用中のコンテナ名 | 8 | localhost · plantpulse-datalake · plantpulse-server-web · plantpulse-batch-web · plantpulse-warehouse-web · plantpulse-plugin-opcua · plantpulse-plugin-aasx · plantpulse-proxy |
| 改名前の旧名 | 8 | plantpulse-warehouse · plantpulse-server · plantpulse-batch · plantpulse-warehouse-s3 · plantpulse-opcua · plantpulse-aasx · plantpulse-plugin-opcua-server · plantpulse-plugin-aasx-server |
| ワーカーノード | 5 | plantpulse-datalake-worker-1 ~ -5 |
それまでに発行された証明書にはワーカーが1つも含まれておらず、ワーカーIPもIP SANにありませんでした (dev 41実測: DNS名16個のうちワーカー0個)。そのため、ワーカーでTLSを終端する場合、 クライアントが検証する名前もアドレスも持っていませんでした。それ以前に発行された証明書を 使用しているクラスタでは、再発行が必要です。
コンテナ名を変更したり、バックエンドホストを直接指定した場合は、SANリストも同時に修正する必要があります。
ワーカーを6台以上に増やした場合は、PP_TLS_SAN_DNS にその名前も追加する必要があります —
デフォルト値は5つまでです。
:::
pd_ssl_prepare が生成する成果物:
/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/ # 클라이언트 인증서 (선택)
外部CA証明書の使用
自己署名の代わりに、会社 / Let's Encrypt CA証明書を使用する場合:
# 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
有効期限モニタリング: 証明書の有効期限30日前にアラームが発生するように、外部モニタリング (
openssl x509 -enddate) を設定してください。
Webセキュリティフィルター
PlantPulseは web.xml で定義されたセキュリティフィルターを通じてWebリクエストを保護します。
SecurityFilter
認証されていないユーザーのアクセスを遮断する中核フィルターです。
<!-- 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>
- すべてのリクエスト(
/*)に対して認証状態を確認します。 excludeUrlsで指定されたパスは認証なしでアクセスできます。- 認証されていないリクエストはログインページにリダイレクトされます。
XSSFilter
Cross-Site Scripting(XSS)攻撃を防ぐフィルターです。
<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>
- リクエストパラメータから
<script>、<iframe>などの危険なタグを除去します。 - APIパスは別途入力検証が適用されるため、フィルターから除外できます。
CSRF防御
Cross-Site Request Forgery(CSRF)攻撃を防ぐために、トークンベースの検証を実行します。
<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>
- POST、PUT、DELETEリクエストに対してCSRFトークンを検証します。
- フォームリクエスト時には
_csrfパラメータまたはX-CSRF-TOKENヘッダーが必要です。 - REST API(
/api/v5/)はトークン認証を使用するため、CSRFフィルターから除外されます。
EncodingFilter
文字エンコーディングを統一し、エンコーディング関連のセキュリティ脆弱性を防ぎます。
<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設定
ユーザーが見る 80 / 443 は plantpulse-proxy(nginx)が終端します。 Webサーバーコンテナはホストポートを開かないため、ブラウザー用証明書を変更する場所はTomcatではなくプロキシです。
プロキシ証明書 — デフォルトは自己署名
インストール直後、443は何の準備もなしにすぐ開きます。 プロキシが初回起動時に自己署名証明書を作成し、pp-proxy-certs ボリュームに格納するためです。
再起動によって顧客の実際の証明書が自己署名に戻ることは静かな事故であるため、ファイルが既に存在する場合、プロキシはそのまま使用します。
実際の証明書への交換
nginx設定を変更する必要はありません — パスが固定されているため、ファイルを交換するだけです。
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
| 項目 | 値 |
|---|---|
| ボリューム | pp-proxy-certs |
| コンテナ内パス | /etc/nginx/certs/server.crt · /etc/nginx/certs/server.key |
| 自己署名CN | PP_PROXY_CERT_CN (デフォルト plantpulse.local) |
証明書ファイルは中間証明書まで含むチェーン(fullchain) として配置してください。
プロキシは1884をパススルーで流し、ブローカーがTLSを終端します。したがって、プロキシ証明書を変更してもMQTTクライアントが検証する証明書は変わりません — そちらは plantpulse-certs が作成したマテリアルを使用します。
(参考) Webサーバーに直接証明書を付ける場合
以下は、Webサーバーをプロキシなしで直接公開する環境のための参考です。標準構成では上記のプロキシ証明書のみ変更すれば済みます。コンテナ内バックエンド区間のTLSマテリアルは plantpulse-certs が既に作成しています。
HTTPSを使用するには、SSL証明書をインストールする必要があります。
# 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コネクタ
Tomcatの server.xml にHTTPSコネクタを追加します。
<!-- 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" />
HTTPからHTTPSへのリダイレクト
すべてのHTTPリクエストを自動的にHTTPSへリダイレクトします。
<!-- 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バージョン管理
セキュリティのため、TLS 1.2以上のみを許可することを推奨します。
| TLSバージョン | 状態 | 推奨 |
|---|---|---|
| SSLv3 | 使用禁止 | POODLE脆弱性 |
| TLS 1.0 | 使用禁止 | 脆弱性存在 |
| TLS 1.1 | 使用禁止 | 脆弱性存在 |
| TLS 1.2 | 許可 | 推奨 |
| TLS 1.3 | 許可 | 最新で最も安全 |
API認証
PlantPulse REST API(/api/v5)は api_key をヘッダーに載せて呼び出します。api_key を取得する
経路は2つあり、その後の呼び出し方法は同じです。
/api/v5/auth は新しい文字列を生成しません。提出されたトークンが有効であれば、そのトークン値を
そのまま api_key として返します(APIManager — api_key = token.getToken())。トークンはUUID
形式であり、JWTではありません。
トークン認証 (外部連携 · 自動化)
事前に発行されたAPIトークンで api_key を受け取ります。運用連携で使用する経路です。
# 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>"
| レスポンス | 意味 |
|---|---|
200 | 発行成功 |
400 | 本文がJSONでない、または token がない |
401 (E1100) | トークンが無効 |
429 (E1101) | ブルートフォース遮断。Retry-After ヘッダーが付属 |
ブルートフォース防御はIP単位カウンター1種です。失敗するたびにそのIPのカウンターが上がり、
ウィンドウ内で上限を超えると429で遮断します。plantpulse-engine.properties で調整します。
| プロパティ | デフォルト値 |
|---|---|
engine.api.v5.auth.bruteforce.enabled | true |
engine.api.v5.auth.bruteforce.ip_limit | 30 |
engine.api.v5.auth.bruteforce.window.seconds | 60 — 存在する場合は window.minutes より優先 |
engine.api.v5.auth.bruteforce.window.minutes | 15 |
X-API-Key が第1優先で、なければ Authorization: Bearer <api_key> をフォールバックとして読み取ります。
Bearerフォールバックはそのヘッダーしか送信できないMCPクライアントのための標準経路であり、値は同じ
api_key です。Bearer 以外のスキームは無視します。
ID/Password認証 (コンソールSPA専用)
ID・パスワードでログインして api_key を受け取ります。コンソール画面が使用する経路であり、サーバー間 連携にはトークン認証を使用してください — 平文資格情報を毎回送信する形になり、ブルートフォース の対象が広がります。
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": "..."}
他のV5エンドポイントは {data, meta, errors} 形式ですが、/api/v5/auth/login は成功・失敗
ともに平文JSONです。パーサーを共用している場合は、この経路のみ分岐してください。
expires_at はエポックミリ秒で、永続トークンの場合は null です。
トークン発行・回収API (/api/v5/token)
コンソール System > APIトークン(/token/index)画面と同じ処理をRESTで行います。自動配線
スクリプトでトークンを作成・削除するときに使用します。画面の使用方法は
セキュリティ管理 を参照してください。
| HTTP | パス | 処理内容 |
|---|---|---|
GET | /api/v5/token | トークン一覧 — トークン値はマスキング(先頭8文字 + ****) |
GET | /api/v5/token/{token} | 単一取得 — マスキング |
POST | /api/v5/token | 発行 |
DELETE | /api/v5/token/{token} | 回収(revoke) — 実行主体は X-API-Key 所有者 |
POST | /api/v5/admin/tokens/{token}/revoke | ADMIN専用回収 — 他人のトークンを切断する場合 |
DELETE /api/v5/token/{token} は自分のトークンを整理する経路であり、
POST /api/v5/admin/tokens/{token}/revoke は管理者が他人のトークンを切断する経路です。
後者は ADMIN ロールのみ通過できます — API ロールのトークンで呼び出しても403です。
事故対応で使用するのは後者です。
両方とも revoked_at を記録し、トークンキャッシュを無効化するため即座にブロックされます。
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}'
| 本文フィールド | 説明 |
|---|---|
login_id | トークンを受け取るアカウント。省略すると呼び出し元(X-API-Key 所有者) に発行される |
ip | トークン使用を許可するIPパターン |
description | 用途メモ |
ttl_days | 有効期間(日)。省略するとグローバルデフォルト値(engine.api.token.default_ttl_days)、0 の場合は永続、正の数値ならその日数 |
リクエストアカウント(X-API-Key 所有者)のロールがADMINでない場合、403 E1200 となり、監査ログに
TOKEN_ISSUE_DENIED が1件記録されます。APIロールアカウントが自分または他人のトークンを発行する
権限昇格を防ぐためのものです。対象アカウント(login_id)のロールは関係ありません。
ttl_days はこの権限を拡大しません。
ttl_days がなぜ必要だったのかグローバルデフォルト値を 0(永続)に変更すると、顧客が画面で作成したトークンまですべて永続になります。
自動配線用のトークン1つだけを永続にしたいためにグローバルを変更する事態をなくすためのものです。
負の数や整数以外の値は 400 VALIDATION_FAILED です — 不正な値を飲み込んで静かに
永続トークンになる方がより危険だからです。
発行成功レスポンスにはトークン原文が1回だけ含まれます。一覧・取得には再び表示されないため、
このときに受け取って保管してください。発行は監査に TOKEN_ISSUED として記録され、永続トークンは有効期限の欄に
PERMANENT として記録されます。
パスワードセキュリティ
パスワードハッシュ化
PlantPulseはユーザーパスワードをSHA-256ハッシュで保存します。元のパスワードはサーバーに保存されません。
パスワードポリシー推奨値
| 項目 | 推奨値 | 説明 |
|---|---|---|
| 最小長 | 8文字以上 | 12文字以上を推奨 |
| 複雑性 | 3種類以上の組み合わせ | 英大/小文字、数字、特殊文字のうち3種類 |
| 変更周期 | 90日 | 四半期ごとに変更 |
| 履歴管理 | 直近3件 | 以前のパスワードの再利用を禁止 |
| 連続失敗ロック | 5回 | 5回連続失敗でアカウントロック |
| ロック解除 | 30分または管理者 | 自動解除または手動解除 |
HTTPセキュリティヘッダー
Webブラウザーのセキュリティ機能を有効にするために、HTTPレスポンスヘッダーを設定します。
推奨セキュリティヘッダー
| ヘッダー | 値 | 説明 |
|---|---|---|
X-XSS-Protection | 1; mode=block | ブラウザーXSSフィルター有効化 |
X-Content-Type-Options | nosniff | MIMEタイプスニッフィング防止 |
X-Frame-Options | SAMEORIGIN | クリックジャッキング(Clickjacking)防止 |
Content-Security-Policy | default-src 'self' | コンテンツソース制限 |
Strict-Transport-Security | max-age=31536000; includeSubDomains | HTTPS強制 (HSTS) |
Referrer-Policy | strict-origin-when-cross-origin | リファラー情報制限 |
Nginx設定例
リバースプロキシとしてNginxを使用する場合、以下のようにセキュリティヘッダーを追加します。
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;
}
HTTPメソッド制限
不要なHTTPメソッドをブロックし、情報露出を防ぎます。
<!-- 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>
案内: OPTIONSメソッドはCORSプリフライトリクエストに使用されます。外部システムとAPI連携が必要な場合、OPTIONSメソッドを許可する必要がある場合があります。
セッションセキュリティ
Cookieセキュリティ設定
<!-- 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>
| 設定 | 値 | 説明 |
|---|---|---|
http-only | true | XSSによるセッション奪取防止 |
secure | true | HTTPでCookieが送信されないように防止 (HTTPS必須) |
tracking-mode | COOKIE | URLにセッションIDが露出しないように防止 |
Session Fixation防止
PlantPulseはログイン成功時にセッションIDを再生成し、Session Fixation攻撃を防ぎます。この機能は SecurityFilter に組み込まれており、別途設定なしで自動的に適用されます。
データベースセキュリティ
PostgreSQL
pg_hba.conf ファイルでアクセス許可範囲を設定します。
# /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
追加のセキュリティ設定 (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
ネットワークセキュリティ
ファイアウォール設定
PlantPulseで外部アクセスが必要なポートのみをファイアウォールで許可してください。
# 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
ネットワークセグメンテーション
産業環境では、ネットワークを以下のように分離することを推奨します。
| ネットワーク領域 | 用途 | 例 |
|---|---|---|
| OTネットワーク | PLC、SCADA、センサー | 10.0.1.0/24 |
| DMZ | PlantPulse Webサーバー、API | 10.0.2.0/24 |
| ITネットワーク | ユーザーアクセス、管理 | 10.0.3.0/24 |
| データネットワーク | DB、メッセージング | 10.0.4.0/24 |
- OTネットワークとITネットワークは直接通信しないようにしてください。
- PlantPulseサーバーはDMZに配置し、両側のネットワークと限定的な通信のみ許可します。
- データベースはデータネットワークに配置し、PlantPulseサーバーからのみアクセスを許可します。
VPNアクセス
リモートから管理コンソールにアクセスする必要がある場合は、VPN経由で接続するよう構成します。
- 管理コンソールおよびSSHアクセスは、必ずVPN経由でのみ許可してください。
- VPNアカウントは個人ごとに発行し、共有アカウントを使用しないようにします。
- VPN接続ログを記録し、定期的にレビューします。
セキュリティ点検チェックリスト
日次点検
| 点検項目 | 確認方法 |
|---|---|
| 不正ログイン試行 | 監査ログでログイン失敗回数を確認 |
| システムエラーログ | セキュリティ関連のエラー/警告ログを確認 |
| サービス可用性 | ヘルスチェックAPIレスポンスを確認 |
週次点検
| 点検項目 | 確認方法 |
|---|---|
| ユーザーアカウント状況 | 未使用アカウント、非アクティブアカウントを点検 |
| 権限変更履歴 | 監査ログで権限変更を確認 |
| ファイアウォールログ | ブロックされたアクセス試行を確認 |
| SSL証明書有効期間 | 証明書の有効期限日を確認 |
月次点検
| 点検項目 | 確認方法 |
|---|---|
| OSセキュリティパッチ | 最新のセキュリティ更新の適用を確認 |
| パスワードポリシー遵守 | 長期未変更のパスワードを確認 |
| DBアクセス権限 | pg_hba.confなどのアクセス制御設定を点検 |
| バックアップデータセキュリティ | バックアップファイルの暗号化とアクセス権限を確認 |
| ポートスキャン | 不要に開いているポートを確認 |
| TLS設定 | 脆弱なプロトコル/暗号スイートを点検 |
セキュリティインシデント対応
セキュリティインシデントが発生した、または疑われる場合は、以下の手順に従って対応してください。
ステップ1: 検知および隔離
# 의심 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>"
ログインセッションを一覧表示したり、強制的に切断する画面もAPIもありません。セッションは有効期限を待つか、 サーバーを再起動しないと切断されません。即座にブロックする必要がある場合は、アカウントを無効化し、 APIトークンを回収してください — それらが現在使用できる手段です。
誰がいつ入ったかは監査ログと以下のステップ2の
system.log grepで確認します。
ステップ2: 分析
# 로그인 이력 조회
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
ステップ3: 復旧
- 侵害されたアカウントのパスワードを変更します。
- 影響を受けたシステムの整合性を確認します。
- 必要に応じてバックアップからデータを復元します。
ステップ4: 事後対応
- インシデントレポートを作成します。
- 再発防止策を策定します。
- セキュリティ設定を強化します。
- 関係者にセキュリティ教育を実施します。
連絡先: セキュリティ関連のお問い合わせは webmaster@kopens.com までご連絡ください。