001package com.streamconverter.security;
002
003import java.io.InputStream;
004import javax.xml.XMLConstants;
005import javax.xml.parsers.DocumentBuilder;
006import javax.xml.parsers.DocumentBuilderFactory;
007import javax.xml.parsers.ParserConfigurationException;
008import javax.xml.stream.XMLInputFactory;
009import javax.xml.transform.TransformerFactory;
010import javax.xml.validation.SchemaFactory;
011import org.slf4j.Logger;
012import org.slf4j.LoggerFactory;
013
014/**
015 * XML処理のセキュリティ設定を提供するユーティリティクラス
016 *
017 * <p>このクラスは、XXE(XML外部エンティティ)攻撃や その他のXML関連のセキュリティ脆弱性を防ぐため、 安全なXML処理設定を提供します。
018 *
019 * <p>主な機能:
020 *
021 * <ul>
022 *   <li>XXE攻撃防止のためのDocumentBuilderFactory設定
023 *   <li>安全なXMLInputFactory設定
024 *   <li>安全なTransformerFactory設定
025 *   <li>安全なSchemaFactory設定
026 *   <li>セキュリティ設定の動的調整
027 * </ul>
028 *
029 * @since 1.0.0
030 */
031public class SecureXmlConfiguration {
032
033  private static final Logger logger = LoggerFactory.getLogger(SecureXmlConfiguration.class);
034  private static final Logger securityLogger =
035      LoggerFactory.getLogger("com.streamConverter.security");
036
037  private SecureXmlConfiguration() {
038    // ユーティリティクラスのため、インスタンス化を禁止
039  }
040
041  /**
042   * XXE攻撃を防ぐように設定されたDocumentBuilderFactoryを作成します
043   *
044   * @return 安全に設定されたDocumentBuilderFactory
045   * @throws ParserConfigurationException 設定に失敗した場合
046   */
047  public static DocumentBuilderFactory createSecureDocumentBuilderFactory()
048      throws ParserConfigurationException {
049    DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
050
051    // XXE攻撃防止設定
052    try {
053      // DOCTYPE宣言を無効化
054      factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
055      securityLogger.debug("DOCTYPE declarations disabled");
056    } catch (ParserConfigurationException e) {
057      logger.warn("Failed to disable DOCTYPE declarations", e);
058    }
059
060    try {
061      // 外部一般エンティティを無効化
062      factory.setFeature("http://xml.org/sax/features/external-general-entities", false);
063      // 外部パラメータエンティティを無効化
064      factory.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
065      securityLogger.debug("External entities disabled");
066    } catch (ParserConfigurationException e) {
067      logger.warn("Failed to disable external entities", e);
068    }
069
070    // 追加のセキュリティ設定
071    factory.setNamespaceAware(true);
072    factory.setValidating(false);
073
074    // XMLリーダーのセキュリティ制限
075    factory.setXIncludeAware(false);
076    factory.setExpandEntityReferences(false);
077
078    securityLogger.info("Secure DocumentBuilderFactory created with XXE protection");
079    return factory;
080  }
081
082  /**
083   * 安全に設定されたDocumentBuilderを作成します
084   *
085   * @return 安全に設定されたDocumentBuilder
086   * @throws ParserConfigurationException 設定に失敗した場合
087   */
088  public static DocumentBuilder createSecureDocumentBuilder() throws ParserConfigurationException {
089    DocumentBuilderFactory factory = createSecureDocumentBuilderFactory();
090    return factory.newDocumentBuilder();
091  }
092
093  /**
094   * XXE攻撃を防ぐように設定されたXMLInputFactoryを作成します
095   *
096   * @return 安全に設定されたXMLInputFactory
097   */
098  public static XMLInputFactory createSecureXMLInputFactory() {
099    XMLInputFactory factory = XMLInputFactory.newInstance();
100
101    // 外部エンティティの処理を無効化
102    factory.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, false);
103    factory.setProperty(XMLInputFactory.SUPPORT_DTD, false);
104
105    // 追加のセキュリティ設定
106    factory.setProperty(XMLInputFactory.IS_REPLACING_ENTITY_REFERENCES, false);
107    factory.setProperty(XMLInputFactory.IS_VALIDATING, false);
108
109    securityLogger.info("Secure XMLInputFactory created with XXE protection");
110    return factory;
111  }
112
113  /**
114   * 安全に設定されたTransformerFactoryを作成します
115   *
116   * @return 安全に設定されたTransformerFactory
117   */
118  public static TransformerFactory createSecureTransformerFactory() {
119    TransformerFactory factory = TransformerFactory.newInstance();
120
121    try {
122      // XMLTransform攻撃を防ぐための設定
123      factory.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, "");
124      factory.setAttribute(XMLConstants.ACCESS_EXTERNAL_STYLESHEET, "");
125    } catch (Exception e) {
126      logger.warn("Failed to set external access attributes for TransformerFactory", e);
127    }
128
129    // 機能制限
130    try {
131      factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
132    } catch (Exception e) {
133      logger.warn("Failed to enable secure processing feature", e);
134    }
135
136    securityLogger.info("Secure TransformerFactory created");
137    return factory;
138  }
139
140  /**
141   * 安全に設定されたSchemaFactoryを作成します
142   *
143   * @return 安全に設定されたSchemaFactory
144   */
145  public static SchemaFactory createSecureSchemaFactory() {
146    SchemaFactory factory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
147
148    try {
149      // 外部リソースアクセスを制限
150      factory.setProperty(XMLConstants.ACCESS_EXTERNAL_DTD, "");
151      factory.setProperty(XMLConstants.ACCESS_EXTERNAL_SCHEMA, "");
152    } catch (Exception e) {
153      logger.warn("Failed to set external access properties for SchemaFactory", e);
154    }
155
156    // セキュアプロセシング機能を有効化
157    try {
158      factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
159    } catch (Exception e) {
160      logger.warn("Failed to enable secure processing feature for SchemaFactory", e);
161    }
162
163    securityLogger.info("Secure SchemaFactory created");
164    return factory;
165  }
166
167  /**
168   * InputStreamからのXML読み込みを安全に行います
169   *
170   * @param inputStream 読み込み対象のInputStream
171   * @return 安全に解析されたDocumentBuilder
172   * @throws ParserConfigurationException XML設定エラーが発生した場合
173   */
174  public static DocumentBuilder createSecureDocumentBuilderForStream(InputStream inputStream)
175      throws ParserConfigurationException {
176
177    // 入力検証
178    if (inputStream == null) {
179      throw new IllegalArgumentException("InputStream cannot be null");
180    }
181
182    DocumentBuilder builder = createSecureDocumentBuilder();
183
184    // エラーハンドラーの設定(セキュリティ上の理由により詳細エラー情報を制限)
185    builder.setErrorHandler(new SecurityAwareErrorHandler());
186
187    securityLogger.debug("Secure DocumentBuilder created for InputStream processing");
188    return builder;
189  }
190}
191
192/** セキュリティを考慮したXMLエラーハンドラー 詳細なエラー情報の漏洩を防ぐため、一般的なエラーメッセージのみを提供 */
193class SecurityAwareErrorHandler implements org.xml.sax.ErrorHandler {
194  private static final Logger logger = LoggerFactory.getLogger(SecurityAwareErrorHandler.class);
195  private static final Logger securityLogger =
196      LoggerFactory.getLogger("com.streamConverter.security");
197
198  @Override
199  public void warning(org.xml.sax.SAXParseException exception) {
200    logger.debug("XML parsing warning (details suppressed for security)");
201    securityLogger.warn("XML parsing warning detected during secure processing");
202  }
203
204  @Override
205  public void error(org.xml.sax.SAXParseException exception) {
206    logger.debug("XML parsing error (details suppressed for security)");
207    securityLogger.warn("XML parsing error detected during secure processing");
208  }
209
210  @Override
211  public void fatalError(org.xml.sax.SAXParseException exception) throws org.xml.sax.SAXException {
212    securityLogger.error("Fatal XML parsing error detected during secure processing");
213    throw new org.xml.sax.SAXException("XML processing failed due to security constraints");
214  }
215}