構文的に正しい SOAP リクエストを送信しているにもかかわらず、以下のレスポンスが返ってくることがあります。
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<soap:Fault>
<faultcode>soap:MustUnderstand</faultcode>
<faultstring>
SOAP header Security was not understood.
</faultstring>
</soap:Fault>
</soap:Body>
</soap:Envelope>
MustUnderstand fault は、サーバーが mustUnderstand="1" と指定された必須ヘッダー要素を受信したものの、そのヘッダーを処理するハンドラーが登録されていないことを意味します。SOAP 仕様では、処理できない必須ヘッダーを含むメッセージは必ず拒否しなければならないと定められています。フォールバックもグレースフルデグラデーションもありません。
mustUnderstand 属性の意味
mustUnderstand は SOAP ヘッダー要素に付与されるバイナリ指示です。"1" (SOAP 1.1) または "true" (SOAP 1.2) に設定されると、受信側の SOAP プロセッサに「このヘッダーの処理方法を理解している必要がある。理解できない場合はメッセージ全体を拒否せよ」と伝えます。
<soap:Header>
<wsse:Security
xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"
soap:mustUnderstand="1">
<wsse:UsernameToken>
<wsse:Username>admin</wsse:Username>
<wsse:Password>secret</wsse:Password>
</wsse:UsernameToken>
</wsse:Security>
</soap:Header>
この例では、サーバーに WS-Security の処理が有効になっていない場合、MustUnderstand fault を返さなければなりません。Security ヘッダーを無視して本文を処理することは、セキュリティ違反になるためです。
MustUnderstand Fault を引き起こす一般的なヘッダー
1. WS-Security ヘッダー
最も頻繁に発生するケースです。クライアントの SOAP ライブラリが WS-Security ヘッダー (UsernameToken、X.509 証明書、SAML アサーション) を自動的に追加しますが、サーバーが WS-Security を実装していない、または別のセキュリティメカニズムを期待しています。
<wsse:Security soap:mustUnderstand="1"
xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
<wsu:Timestamp xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
<wsu:Created>2026-03-01T10:00:00Z</wsu:Created>
<wsu:Expires>2026-03-01T10:05:00Z</wsu:Expires>
</wsu:Timestamp>
</wsse:Security>
発生原因: WCF の wsHttpBinding はデフォルトで WS-Security ヘッダーを追加します。ターゲットサービスが basicHttpBinding や WCF 以外の SOAP スタックを使用している場合、WS-Security ハンドラーがなくヘッダーを拒否します。
2. WS-Addressing ヘッダー
WS-Addressing は To、Action、MessageID、ReplyTo などのルーティングメタデータを SOAP ヘッダーに追加します。
<wsa:Action soap:mustUnderstand="1"
xmlns:wsa="http://www.w3.org/2005/08/addressing">
http://example.com/IOrderService/GetOrder
</wsa:Action>
<wsa:To soap:mustUnderstand="1"
xmlns:wsa="http://www.w3.org/2005/08/addressing">
https://api.example.com/OrderService.svc
</wsa:To>
発生原因: WCF の wsHttpBinding はデフォルトで WS-Addressing を含みます。Java ベースの SOAP サービスでは WS-Addressing ハンドラーが設定されていないことが多く、古い ASP.NET ASMX サービスはこれをサポートしていません。
3. WS-ReliableMessaging ヘッダー
WS-ReliableMessaging は確認応答シーケンスによるメッセージ配信保証を提供します。
<wsrm:Sequence soap:mustUnderstand="1"
xmlns:wsrm="http://schemas.xmlsoap.org/ws/2005/02/rm">
<wsrm:Identifier>urn:uuid:abc123</wsrm:Identifier>
<wsrm:MessageNumber>1</wsrm:MessageNumber>
</wsrm:Sequence>
発生原因: WCF の wsHttpBinding で reliableSession enabled="true" を設定するとこれらのヘッダーが追加されます。WCF 以外のサービスはこれを処理できないことがほとんどです。
問題のヘッダーを特定する方法
fault メッセージにヘッダー名が含まれる場合もありますが、常にそうとは限りません。確実に特定するには、送信リクエストを調査する必要があります。
ステップ 1: 生のリクエストをキャプチャする
WCF メッセージログの場合:
<system.serviceModel>
<diagnostics>
<messageLogging logEntireMessage="true"
logMalformedMessages="true"
logMessagesAtServiceLevel="false"
logMessagesAtTransportLevel="true" />
</diagnostics>
</system.serviceModel>
ネットワークプロキシを使用する場合 (言語非依存):
# mitmproxy で SOAP トラフィックをインターセプト
mitmproxy --mode reverse:https://api.example.com --listen-port 8080
ステップ 2: mustUnderstand="1" のヘッダーをすべて確認する
キャプチャした XML で mustUnderstand を検索します。mustUnderstand="1" が付いたすべてのヘッダー要素が原因の候補です。
解決策
解決策 1: 不要なヘッダーを除去する
最も直接的な修正方法です。サーバーが WS-Security、WS-Addressing、WS-ReliableMessaging を必要としない場合、送信を停止します。
WCF (.NET) — wsHttpBinding から basicHttpBinding に切り替え:
<!-- 変更前: wsHttpBinding は WS-Security + WS-Addressing を追加 -->
<endpoint binding="wsHttpBinding" contract="IOrderService" />
<!-- 変更後: basicHttpBinding は WS-* ヘッダーなしの素の SOAP を送信 -->
<endpoint binding="basicHttpBinding" contract="IOrderService" />
WCF — wsHttpBinding を維持しつつ特定の機能を無効化:
<wsHttpBinding>
<binding name="NoWsExtensions">
<security mode="None" /> <!-- WS-Security ヘッダーを除去 -->
<reliableSession enabled="false" /> <!-- WS-RM ヘッダーを除去 -->
</binding>
</wsHttpBinding>
WS-Addressing を除去するにはカスタムバインディングを使用:
<customBinding>
<binding name="NoAddressing">
<textMessageEncoding messageVersion="Soap11" />
<httpTransport />
</binding>
</customBinding>
解決策 2: サーバーにヘッダー処理を設定する
ヘッダーがユースケースに必要な場合 (WS-Security が必須など)、サーバーにそれを処理するよう設定します。
Java (Apache CXF) — WS-Security インターセプターの追加:
import org.apache.cxf.ws.security.wss4j.WSS4JInInterceptor;
Map<String, Object> inProps = new HashMap<>();
inProps.put("action", "UsernameToken");
inProps.put("passwordType", "PasswordText");
inProps.put("passwordCallbackClass", "com.example.ServerPasswordCallback");
endpoint.getInInterceptors().add(new WSS4JInInterceptor(inProps));
解決策 3: カスタムヘッダーハンドラーを実装する
サーバーを制御している場合、ヘッダーを実際に処理せずに「理解済み」としてマークする no-op ハンドラーを登録できます。
WCF での実装:
public class CustomHeaderHandler : IDispatchMessageInspector
{
public object AfterReceiveRequest(
ref Message request,
IClientChannel channel,
InstanceContext instanceContext)
{
int headerIndex = request.Headers.FindHeader(
"CustomHeader",
"http://example.com/custom");
if (headerIndex >= 0)
{
request.Headers.UnderstoodHeaders.Add(
request.Headers[headerIndex]);
}
return null;
}
public void BeforeSendReply(
ref Message reply,
object correlationState) { }
}
警告: セキュリティヘッダーを処理せずに「理解済み」とマークすると、セキュリティ脆弱性が生じます。ヘッダーの目的を十分に理解した上でのみ使用してください。
解決策 4: クライアント側で mustUnderstand を 0 に設定する
ヘッダーが情報提供目的であり必須でない場合、mustUnderstand="0" に設定してオプションにできます。
# Python (zeep) — mustUnderstand なしのカスタムヘッダー追加
from lxml import etree
from zeep import Client
client = Client("https://api.example.com/service?wsdl")
header = etree.Element(
"{http://example.com/custom}TraceId",
nsmap={"custom": "http://example.com/custom"}
)
header.text = "req-12345"
# mustUnderstand は明示的に設定しない場合デフォルトで 0
response = client.service.GetOrder(
orderId="12345",
_soapheaders=[header]
)
デバッグワークフロー
1. 送信 SOAP リクエスト (生 XML) をキャプチャ
|
2. mustUnderstand="1" のすべての Header 要素をリストアップ
|
3. 各ヘッダーについて:
+-- ユースケースに必要か?
| +-- はい → サーバーに処理を設定する
| +-- いいえ → クライアント設定から除去する
+-- フレームワークのデフォルトで追加されたか?
+-- はい → よりシンプルなバインディングに切り替え (例: basicHttpBinding)
+-- いいえ → mustUnderstand を "0" に設定可能か確認
SOAPless による解決
MustUnderstand fault は、ほぼすべてのケースでフレームワークレベルのヘッダー自動挿入から発生します。SOAP ライブラリがターゲットサービスの想定外のヘッダーを追加してしまうのです。これは異なるプラットフォーム間の SOAP 連携 (WCF から Java サービス、PHP から .NET サービスなど) で特に頻発する問題です。
SOAPless はこれらの差異を吸収するプロトコルブリッジとして機能します。WSDL を登録すると、SOAPless はターゲットサービスが期待するヘッダーのみで SOAP リクエストを構築します。不要な WS-Addressing や予期しない WS-ReliableMessaging は追加されず、WS-Security は明示的に設定した場合のみ含まれます。アプリケーションからはクリーンな REST JSON API で通信し、SOAPless が上流サービスとの SOAP ヘッダーネゴシエーションを処理します。
認証情報は SOAPless ダッシュボードで AES-256-GCM 暗号化により管理され、本番デプロイ前にブラウザから直接操作をテストできます。