diff --git a/lib/policyEvaluator/requestUtils.ts b/lib/policyEvaluator/requestUtils.ts index 5b07f9e54..bf14ac247 100644 --- a/lib/policyEvaluator/requestUtils.ts +++ b/lib/policyEvaluator/requestUtils.ts @@ -1,10 +1,12 @@ import * as ipCheck from '../ipCheck' import { IncomingMessage } from 'http' +import { TLSSocket } from 'tls' export interface S3Config { requests: { trustedProxyCIDRs: string[], - extractClientIPFromHeader: string + extractClientIPFromHeader: string, + extractProtocolFromHeader: string, } } @@ -37,3 +39,20 @@ export function getClientIp(request: IncomingMessage, s3config?: S3Config): stri } return clientIp; } + +/** + * getHttpProtocolSecurity - Dete²object + * @param s3config - s3 config + * @return {boolean} - returns true if the request is secure + */ +export function getHttpProtocolSecurity(request: IncomingMessage, s3config?: S3Config): boolean { + const requestConfig = s3config?.requests; + if (requestConfig) { + const { trustedProxyCIDRs } = requestConfig; + const clientIp = request.socket.remoteAddress?.toString() ?? ''; + if (ipCheck.ipMatchCidrList(trustedProxyCIDRs, clientIp)) { + return request.headers[requestConfig.extractProtocolFromHeader.toLowerCase()] === 'https'; + } + } + return request.socket instanceof TLSSocket && request.socket.encrypted; +} diff --git a/package.json b/package.json index e102dceaf..ad530b971 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "engines": { "node": ">=16" }, - "version": "8.1.144", + "version": "8.1.145", "description": "Common utilities for the S3 project components", "main": "build/index.js", "repository": { diff --git a/tests/unit/policyEvaluator/requestUtils.spec.js b/tests/unit/policyEvaluator/requestUtils.spec.js index ffa77df99..b7ba1854d 100644 --- a/tests/unit/policyEvaluator/requestUtils.spec.js +++ b/tests/unit/policyEvaluator/requestUtils.spec.js @@ -1,6 +1,7 @@ const assert = require('assert'); const DummyRequest = require('../../utils/DummyRequest'); const requestUtils = require('../../../lib/policyEvaluator/requestUtils'); +const { TLSSocket } = require('tls'); describe('requestUtils.getClientIp', () => { // s3 config with 'requests.viaProxy` enabled @@ -110,3 +111,79 @@ describe('requestUtils.getClientIp', () => { assert.strictEqual(result, dummyRemoteIP); }); }); + +describe('requestUtils.getHttpProtocolSecurity', () => { + const configWithProxy = require('../../utils/dummyS3ConfigProxy.json'); + const configWithoutProxy = require('../../utils/dummyS3Config.json'); + const testClientIp = '192.168.100.1'; + const testProxyIp = '192.168.100.2'; + + it('should return true if request comes via trusted proxy with https proto header', () => { + const request = new DummyRequest({ + headers: { + 'x-forwarded-proto': 'https', + }, + socket: { + remoteAddress: testProxyIp, + }, + }); + const result = requestUtils.getHttpProtocolSecurity(request, configWithProxy); + assert.strictEqual(result, true); + }); + + it('should return false if request comes via trusted proxy with http proto header', () => { + const request = new DummyRequest({ + headers: { + 'x-forwarded-proto': 'http', + }, + socket: { + remoteAddress: testProxyIp, + }, + }); + const result = requestUtils.getHttpProtocolSecurity(request, configWithProxy); + assert.strictEqual(result, false); + }); + + it('should check TLS when request not from trusted proxy', () => { + const request = new DummyRequest({ + headers: { + 'x-forwarded-proto': 'https', + }, + socket: new TLSSocket(null), + }); + request.socket.encrypted = true; + const result = requestUtils.getHttpProtocolSecurity(request, configWithoutProxy); + assert.strictEqual(result, true); + }); + + it('should return false for non-TLS socket', () => { + const request = new DummyRequest({ + headers: {}, + socket: { + remoteAddress: testClientIp, + }, + }); + const result = requestUtils.getHttpProtocolSecurity(request, configWithoutProxy); + assert.strictEqual(result, false); + }); + + it('should handle configured headers with uppercases', () => { + const request = new DummyRequest({ + headers: { + 'x-forwarded-proto': 'https', + }, + socket: { + remoteAddress: testProxyIp, + }, + }); + const result = requestUtils.getHttpProtocolSecurity(request, { + requests: { + viaProxy: true, + trustedProxyCIDRs: ['192.168.100.0/22'], + extractClientIPFromHeader: 'X-Forwarded-For', + extractProtocolFromHeader: 'X-Forwarded-Proto', + }, + }); + assert.strictEqual(result, true); + }); +}); diff --git a/tests/utils/dummyS3ConfigProxy.json b/tests/utils/dummyS3ConfigProxy.json index b29c05577..a8cc94b99 100644 --- a/tests/utils/dummyS3ConfigProxy.json +++ b/tests/utils/dummyS3ConfigProxy.json @@ -81,6 +81,7 @@ "requests": { "viaProxy": true, "trustedProxyCIDRs": ["192.168.100.0/22"], - "extractClientIPFromHeader": "x-forwarded-for" + "extractClientIPFromHeader": "x-forwarded-for", + "extractProtocolFromHeader": "x-forwarded-proto" } }