@gftdcojp/ksqldb-orm
Version:
ksqldb-orm - Server-Side TypeScript ORM for ksqlDB with enterprise security extensions
216 lines • 8.27 kB
JavaScript
;
/**
* CORS設定のセキュリティ強化ユーティリティ
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.getSecureCorsOptions = getSecureCorsOptions;
exports.createCorsMiddleware = createCorsMiddleware;
exports.logCorsConfiguration = logCorsConfiguration;
const env_1 = require("./env");
const logger_1 = require("./logger");
/**
* セキュアなCORS設定を生成
* @param customOptions カスタムオプション
*/
function getSecureCorsOptions(customOptions = {}) {
// 環境変数からCORS許可オリジンを取得
const corsOriginsEnv = (0, env_1.getEnvVar)('GFTD_CORS_ORIGINS');
let allowedOrigins = [];
if (corsOriginsEnv) {
allowedOrigins = corsOriginsEnv
.split(',')
.map(origin => origin.trim())
.filter(origin => origin.length > 0);
}
else {
// デフォルトは開発環境でのみlocalhostを許可
if (!(0, env_1.isProduction)()) {
allowedOrigins = [
'http://localhost:3000',
'http://localhost:3001',
'http://localhost:8080',
'https://localhost:3000',
'https://localhost:3001',
'https://localhost:8080'
];
}
else {
// 本番環境ではオリジンを明示的に設定する必要がある
logger_1.log.error('CRITICAL SECURITY: GFTD_CORS_ORIGINS must be set in production environment');
allowedOrigins = []; // デフォルトで全て拒否
}
}
// 本番環境でワイルドカード使用をチェック
if ((0, env_1.isProduction)() && allowedOrigins.includes('*')) {
logger_1.log.error('CRITICAL SECURITY: Wildcard (*) in CORS origins is not allowed in production');
allowedOrigins = allowedOrigins.filter(origin => origin !== '*');
}
// HTTPSを推奨
if ((0, env_1.isProduction)()) {
const insecureOrigins = allowedOrigins.filter(origin => origin.startsWith('http://') && !origin.includes('localhost'));
if (insecureOrigins.length > 0) {
logger_1.log.warn('SECURITY WARNING: HTTP origins detected in production CORS settings', {
insecureOrigins
});
}
}
const defaultOptions = {
origins: allowedOrigins,
allowCredentials: true, // JWT認証用
maxAge: 86400, // 24時間
allowedHeaders: [
'Content-Type',
'Authorization',
'X-Requested-With',
'Accept',
'Origin',
'Cache-Control',
'X-File-Name'
],
allowedMethods: [
'GET',
'POST',
'PUT',
'DELETE',
'OPTIONS',
'HEAD'
],
exposedHeaders: [
'Content-Length',
'Content-Type',
'X-Total-Count'
],
preflightContinue: false,
optionsSuccessStatus: 204
};
// カスタムオプションでデフォルトを上書き
const finalOptions = {
...defaultOptions,
...customOptions
};
// セキュリティ検証
validateCorsOptions(finalOptions);
return finalOptions;
}
/**
* CORS設定の検証
* @param options CORS設定オプション
*/
function validateCorsOptions(options) {
const issues = [];
// オリジンチェック
if (!options.origins || options.origins.length === 0) {
issues.push('No allowed origins configured - this will block all cross-origin requests');
}
else if (options.origins.includes('*')) {
if ((0, env_1.isProduction)()) {
issues.push('CRITICAL: Wildcard origin (*) is dangerous in production');
}
else {
logger_1.log.warn('WARNING: Wildcard origin (*) should only be used in development');
}
}
// 認証情報許可チェック
if (options.allowCredentials && options.origins?.includes('*')) {
issues.push('CRITICAL: Cannot use allowCredentials: true with wildcard origin (*)');
}
// HTTPSチェック(本番環境)
if ((0, env_1.isProduction)() && options.origins) {
const httpOrigins = options.origins.filter(origin => origin.startsWith('http://') && !origin.includes('localhost'));
if (httpOrigins.length > 0) {
issues.push(`WARNING: HTTP origins in production: ${httpOrigins.join(', ')}`);
}
}
// ヘッダーチェック
if (options.allowedHeaders?.includes('*')) {
issues.push('WARNING: Wildcard allowed headers (*) may be too permissive');
}
// メソッドチェック
const dangerousMethods = ['TRACE', 'CONNECT'];
const allowedDangerous = options.allowedMethods?.filter(method => dangerousMethods.includes(method.toUpperCase()));
if (allowedDangerous && allowedDangerous.length > 0) {
issues.push(`WARNING: Potentially dangerous HTTP methods allowed: ${allowedDangerous.join(', ')}`);
}
// ログ出力
if (issues.length > 0) {
issues.forEach(issue => {
if (issue.startsWith('CRITICAL')) {
logger_1.log.error(issue);
}
else {
logger_1.log.warn(issue);
}
});
}
}
/**
* Express.js用のCORSミドルウェア関数を生成
* @param customOptions カスタムCORSオプション
*/
function createCorsMiddleware(customOptions = {}) {
const corsOptions = getSecureCorsOptions(customOptions);
return (req, res, next) => {
const origin = req.headers.origin;
// Origin検証
if (origin && corsOptions.origins) {
if (corsOptions.origins.includes('*') || corsOptions.origins.includes(origin)) {
res.header('Access-Control-Allow-Origin', origin);
}
else {
// 許可されていないオリジンからのリクエストをログ記録
logger_1.log.warn('CORS: Blocked request from unauthorized origin', {
origin,
userAgent: req.headers['user-agent'],
ip: req.ip
});
return res.status(403).json({
error: 'CORS policy violation',
message: 'Origin not allowed'
});
}
}
// 認証情報許可
if (corsOptions.allowCredentials) {
res.header('Access-Control-Allow-Credentials', 'true');
}
// プリフライトリクエスト処理
if (req.method === 'OPTIONS') {
if (corsOptions.allowedMethods) {
res.header('Access-Control-Allow-Methods', corsOptions.allowedMethods.join(', '));
}
if (corsOptions.allowedHeaders) {
res.header('Access-Control-Allow-Headers', corsOptions.allowedHeaders.join(', '));
}
if (corsOptions.maxAge) {
res.header('Access-Control-Max-Age', corsOptions.maxAge.toString());
}
return res.status(corsOptions.optionsSuccessStatus || 204).end();
}
// 実際のリクエストでの追加ヘッダー
if (corsOptions.exposedHeaders) {
res.header('Access-Control-Expose-Headers', corsOptions.exposedHeaders.join(', '));
}
next();
};
}
/**
* CORS設定のログ出力
*/
function logCorsConfiguration() {
const corsOptions = getSecureCorsOptions();
logger_1.log.info('CORS configuration initialized', {
allowedOrigins: corsOptions.origins?.length || 0,
allowCredentials: corsOptions.allowCredentials,
environment: (0, env_1.isProduction)() ? 'production' : 'development'
});
if (corsOptions.origins) {
// セキュリティ上の理由で、本番環境ではオリジンの詳細をログに出力しない
if (!(0, env_1.isProduction)()) {
logger_1.log.debug('CORS allowed origins', { origins: corsOptions.origins });
}
else {
logger_1.log.info('CORS allowed origins configured', { count: corsOptions.origins.length });
}
}
}
//# sourceMappingURL=cors.js.map