-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1422 from aligent/feature/header-change-detection
Header change detection construct
- Loading branch information
Showing
14 changed files
with
2,084 additions
and
15 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
{ | ||
"extends": ["../../.eslintrc.json"], | ||
"ignorePatterns": ["!**/*", "**/node_modules/**"], | ||
"overrides": [ | ||
{ | ||
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"], | ||
"rules": {} | ||
}, | ||
{ | ||
"files": ["*.ts", "*.tsx"], | ||
"rules": {} | ||
}, | ||
{ | ||
"files": ["*.js", "*.jsx"], | ||
"rules": {} | ||
} | ||
] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
# Aligent Header Change Detection Service | ||
|
||
## Overview | ||
|
||
Creates a Lambda function that periodically scans security headers and sends the results to SNS. | ||
|
||
### Diagram | ||
|
||
![diagram](docs/diagram.jpg) | ||
|
||
This service aims to comply with PCI DSS to cover the requirements outlined by section 11.6.1. | ||
|
||
**11.6.1**: A change- and tamper-detection mechanism is deployed as follows: | ||
> - To alert personnel to unauthorized modification (including indicators of compromise, changes, additions, and deletions) to the security-impacting HTTP headers and the script contents of payment pages as received by the consumer browser. | ||
> - The mechanism is configured to evaluate the received HTTP headers and payment pages. | ||
> - The mechanism functions are performed as follows: | ||
> - At least weekly | ||
> OR | ||
> - Periodically (at the frequency defined in the entity’s targeted risk analysis, which is performed according to all elements specified in Requirement 12.3.1) | ||
## Default config | ||
|
||
By default, the following headers are monitored: | ||
|
||
- Content-Security-Policy | ||
- Content-Security-Policy-Report-Only | ||
- Reporting-Endpoints | ||
- Strict-Transport-Security | ||
- X-Frame-Options | ||
- X-Content-Type-Options | ||
- Cross-Origin-Opener-Policy | ||
- Cross-Origin-Embedder-Policy | ||
- Cross-Origin-Resource-Policy | ||
- Referrer-Policy | ||
- Permission-Policy | ||
- Cache-Control | ||
- Set-Cookie | ||
|
||
## Usage | ||
|
||
To include this in your CDK stack, add the following: | ||
|
||
```typescript | ||
// Import required packages | ||
import { SnsTopic } from "aws-cdk-lib/aws-events-targets"; | ||
import { Topic } from "aws-cdk-lib/aws-sns"; | ||
import { HeaderChangeDetection } from "@aligent/cdk-header-change-detection"; | ||
|
||
// Create a new SNS topic | ||
const topic = new Topic(this, 'Topic'); | ||
const snsTopic = new SnsTopic(topic); | ||
|
||
// Pass the required props | ||
new HeaderChangeDetection(this, 'HeaderChangeDetection', { snsTopic }); | ||
``` | ||
|
||
## Local development | ||
|
||
[NPM link](https://docs.npmjs.com/cli/v7/commands/npm-link) can be used to develop the module locally. | ||
1. Pull this repository locally | ||
2. `cd` into this repository | ||
3. run `npm link` | ||
4. `cd` into the downstream repo (target project, etc) and run `npm link '@aligent/cdk-header-change-detection'` | ||
The downstream repository should now include a symlink to this module. Allowing local changes to be tested before pushing. You may want to update the version notation of the package in the downstream repository's `package.json`. |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
import { | ||
HeaderChangeDetection, | ||
HeaderChangeDetectionProps, | ||
} from "./lib/header-change-detection"; | ||
|
||
export { HeaderChangeDetection, HeaderChangeDetectionProps }; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
/* eslint-disable */ | ||
export default { | ||
displayName: "header-change-detection", | ||
preset: "../../jest.preset.js", | ||
testEnvironment: "node", | ||
transform: { | ||
"^.+\\.[tj]s$": ["ts-jest", { tsconfig: "<rootDir>/tsconfig.spec.json" }], | ||
}, | ||
moduleFileExtensions: ["ts", "js", "html"], | ||
coverageDirectory: "../../coverage/packages/header-change-detection", | ||
}; |
120 changes: 120 additions & 0 deletions
120
packages/header-change-detection/lib/header-change-detection.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,120 @@ | ||
import { DockerImage, Duration } from "aws-cdk-lib"; | ||
import { AttributeType, BillingMode, Table } from "aws-cdk-lib/aws-dynamodb"; | ||
import { Rule, RuleProps, Schedule } from "aws-cdk-lib/aws-events"; | ||
import { LambdaFunction, SnsTopic } from "aws-cdk-lib/aws-events-targets"; | ||
import { Architecture, Code, Function, Runtime } from "aws-cdk-lib/aws-lambda"; | ||
import { Construct } from "constructs"; | ||
import { join } from "path"; | ||
import { Esbuild } from "@aligent/cdk-esbuild"; | ||
|
||
export interface HeaderChangeDetectionProps { | ||
/** | ||
* List of URLs to monitor for header changes | ||
*/ | ||
urls: string[]; | ||
|
||
/** | ||
* Optional list of additional headers to monitor | ||
* | ||
* @default [] | ||
*/ | ||
additionalHeaders?: string[]; | ||
|
||
/** | ||
* Optionally disable all the default headers | ||
* | ||
* @default false | ||
*/ | ||
disableDefaults?: boolean; | ||
|
||
/** | ||
* SNS Topic to send change detection notifications to | ||
*/ | ||
snsTopic: SnsTopic; | ||
|
||
/** | ||
* The schedule for performing the header check | ||
* | ||
* @default Schedule.rate(Duration.hours(1)) | ||
*/ | ||
schedule?: Schedule; | ||
|
||
/** | ||
* Optionally pass any rule properties | ||
*/ | ||
ruleProps?: Partial<RuleProps>; | ||
} | ||
|
||
const command = [ | ||
"sh", | ||
"-c", | ||
'echo "Docker build not supported. Please install esbuild."', | ||
]; | ||
|
||
const defaultHeaders = [ | ||
"content-security-policy", | ||
"content-security-policy-report-only", | ||
"reporting-endpoints", | ||
"strict-transport-security", | ||
"x-frame-options", | ||
"x-content-type-options", | ||
"cross-origin-opener-policy", | ||
"cross-origin-embedder-policy", | ||
"cross-origin-resource-policy", | ||
"referrer-policy", | ||
"permission-policy", | ||
"cache-control", | ||
"set-cookie", | ||
]; | ||
|
||
export class HeaderChangeDetection extends Construct { | ||
constructor(scope: Construct, id: string, props: HeaderChangeDetectionProps) { | ||
super(scope, id); | ||
|
||
const headers = props.disableDefaults ? [] : defaultHeaders; | ||
|
||
headers.push( | ||
...(props.additionalHeaders?.map(header => header.toLowerCase()) || []) | ||
); | ||
|
||
const table = new Table(this, "Table", { | ||
partitionKey: { | ||
name: "Url", | ||
type: AttributeType.STRING, | ||
}, | ||
billingMode: BillingMode.PAY_PER_REQUEST, | ||
}); | ||
|
||
const schedule = new Rule(this, "EventRule", { | ||
schedule: props.schedule || Schedule.rate(Duration.hours(1)), | ||
...props.ruleProps, | ||
}); | ||
|
||
const lambda = new Function(this, "HeaderCheck", { | ||
architecture: Architecture.X86_64, | ||
runtime: Runtime.NODEJS_20_X, | ||
handler: "header-check.handler", | ||
code: Code.fromAsset(join(__dirname, "lambda"), { | ||
bundling: { | ||
command, | ||
image: DockerImage.fromRegistry("busybox"), | ||
local: new Esbuild({ | ||
entryPoints: [join(__dirname, "lambda/header-check.ts")], | ||
}), | ||
}, | ||
}), | ||
environment: { | ||
URLS: props.urls.join(","), | ||
HEADERS: headers.join(","), | ||
TABLE: table.tableName, | ||
TOPIC_ARN: props.snsTopic.topic.topicArn, | ||
}, | ||
}); | ||
|
||
schedule.addTarget(new LambdaFunction(lambda)); | ||
|
||
table.grantWriteData(lambda); | ||
table.grantReadData(lambda); | ||
props.snsTopic.topic.grantPublish(lambda); | ||
} | ||
} |
Oops, something went wrong.