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