AWS WAF String Inspection – Headers/QueryString/Body/Cookies
One of the features provided by AWS WAF is string match rule statements.
A string match statement indicates the string that you want AWS WAF to search for in a request, where in the request to search, and how. For example, you can look for a specific string at the start of any query string in the request or as an exact match for the request’s User-agent header.
String match rule statement
There are several objects that can be inspected by this rule, but in this case the following are covered
- header
- query string
- body
- cookie
Create a rule to block any incoming requests to the WAF that contain a specific string of characters in the above components.
Envorinment
Create a WAF Web ACL.
The Web ACL consists of rules defined by string matching statements.
Apply the created Web ACL to API Gateway.
Create an API Gateway and deploy Lambda on the backend.
When an HTTP request is received from a user, the API Gateway acts as an endpoint, calling a Lambda function instead and returning the result of the function’s execution to the user.
API Gateway is a REST API type.
The runtime environment for the Lambda function is Python 3.8.
The behavior of the function is simple, returning the contents of the event object when executed.
CloudFormation template files
The above configuration is built with CloudFormation.
The CloudFormation templates are placed at the following URL
https://github.com/awstut-an-r/awstut-soa/tree/main/05/003
Explanation of key points of template files
WAF Web ACL Rule Group
Resources:
RuleGroup:
Type: AWS::WAFv2::RuleGroup
Properties:
Capacity: 100
Name: !Sub "${Prefix}-ComponentInspectionRuleGroup"
Rules:
- Action:
Block: {}
Name: !Sub "${Prefix}-HeadersInspectionRule"
Priority: 0
Statement:
ByteMatchStatement:
FieldToMatch:
Headers:
MatchPattern:
All: {}
MatchScope: ALL
OversizeHandling: CONTINUE # default
PositionalConstraint: CONTAINS
SearchString: "awstut1"
TextTransformations:
- Priority: 0
Type: NONE
VisibilityConfig:
CloudWatchMetricsEnabled: true
MetricName: !Sub "${Prefix}-HeadersInspectionRule"
SampledRequestsEnabled: true
- Action:
Block: {}
Name: !Sub "${Prefix}-QueryStringInspectionRule"
Priority: 10
Statement:
ByteMatchStatement:
FieldToMatch:
QueryString: {}
PositionalConstraint: CONTAINS
SearchString: "awstut2"
TextTransformations:
- Priority: 0
Type: NONE
VisibilityConfig:
CloudWatchMetricsEnabled: true
MetricName: !Sub "${Prefix}-QueryStringInspectionRule"
SampledRequestsEnabled: true
- Action:
Block: {}
Name: !Sub "${Prefix}-BodyInspectionRule"
Priority: 20
Statement:
ByteMatchStatement:
FieldToMatch:
Body:
OversizeHandling: CONTINUE # default
PositionalConstraint: CONTAINS
SearchString: "awstut3"
TextTransformations:
- Priority: 0
Type: NONE
VisibilityConfig:
CloudWatchMetricsEnabled: true
MetricName: !Sub "${Prefix}-BodyInspectionRule"
SampledRequestsEnabled: true
- Action:
Block: {}
Name: !Sub "${Prefix}-CookieInspectionRule"
Priority: 30
Statement:
ByteMatchStatement:
FieldToMatch:
Cookies:
MatchPattern:
All: {}
MatchScope: ALL
OversizeHandling: CONTINUE # default
PositionalConstraint: CONTAINS
SearchString: "awstut4"
TextTransformations:
- Priority: 0
Type: NONE
VisibilityConfig:
CloudWatchMetricsEnabled: true
MetricName: !Sub "${Prefix}-CookieInspectionRule"
SampledRequestsEnabled: true
Scope: REGIONAL
VisibilityConfig:
CloudWatchMetricsEnabled: true
MetricName: !Sub "${Prefix}-ComponentInspectionRuleGroup"
SampledRequestsEnabled: true
Code language: YAML (yaml)
Define four rules for string checking in the rule group.
For basic information on WAF Web ACLs, please refer to the following pages.
For information on how to apply WAF Web ACLs to API Gateway, please refer to the following page.
String Inspection Rules for Headers
The first rule is a string inspection rule for headers.
The ByteMatchStatement property allows you to define a string match rule statement.
Specify the component to be inspected with the FieldToMatch property.
Since this rule targets headers, set the property with the same name.
All headers can be included in the inspection by enabling the All property in the MatchPattern property.
Specifying “ALL” in the MatchScope property makes the keys and values in the header subject to inspection.
The OversizeHandling property sets the action for headers that exceed the amount WAF can inspect.
AWS WAF doesn’t support inspecting very large contents for the web request components body, headers, or cookies. The underlying host service has count and size limits on what it forwards to AWS WAF for inspection.
Handling oversize web request components
Restrictions on headers are as follows
Headers – AWS WAF can inspect at most the first 8 KB (8,192 bytes) of the request headers and at most the first 200 headers.
Handling oversize web request components
This time, by specifying “CONTINUE” for this property, it is set so that inspection is performed only on data within the size limit.
The PositionalConstraint property allows you to specify how to search for strings.
In this case, we specify “CONTAINS” and the condition is satisfied if it contains the specified string, regardless of its position.
Specify the string to be examined with the SearchString property.
In this rule, the string “awstut1” is to be searched.
If the TextTransformations property is specified, it can be converted to BASE64, for example, and then inspected.
In this case, by specifying “NONE”, no transformation is performed.
String Inspection Rule for Query String
The second rule is a string inspection rule for the query string.
Set the QueryString property in the FieldToMatch property.
Specify “awstut2” as the string to be inspected.
String Inspection Rule for Body
The third rule is a rule that performs string inspection on the body.
Set the Body property in the FieldToMatch property.
The body also has an upper limit on the size that can be inspected.
Set the OversizeHandling property within this property to “CONTINUE” to inspect only data within the size limit.
Specify “awstut3” as the string to be inspected.
String Inspection Rule for Cookies
The fourth rule is a string inspection rule for cookies.
Set the Cookies property in the FieldToMatch property.
By activating the All property in the MatchPattern property, all cookies can be scanned.
Specifying “ALL” in the MatchScope property makes the keys and values in the cookie subject to inspection.
Set the OversizeHandling property to “CONTINUE” to inspect only data within the size limit.
Specify “awstut4” as the string to be inspected.
(Reference) Lambda function
Resources:
Function:
Type: AWS::Lambda::Function
Properties:
Architectures:
- !Ref Architecture
Code:
ZipFile: |
import json
def lambda_handler(event, context):
return {
'statusCode': 200,
'body': json.dumps(event)
}
FunctionName: !Sub "${Prefix}-function"
Handler: !Ref Handler
Runtime: !Ref Runtime
Role: !GetAtt FunctionRole.Arn
Code language: YAML (yaml)
The code to be executed by the Lambda function in inline format.
For more information, please see the following page.
The code to execute is as simple as returning an event object.
The content of the HTTP request can be confirmed from this object.
(Reference) REST type API Gateway
Resources:
RestApi:
Type: AWS::ApiGateway::RestApi
Properties:
EndpointConfiguration:
Types:
- EDGE
Name: !Ref Prefix
Deployment:
Type: AWS::ApiGateway::Deployment
DependsOn:
- Method
Properties:
RestApiId: !Ref RestApi
Resource:
Type: AWS::ApiGateway::Resource
Properties:
ParentId: !GetAtt RestApi.RootResourceId
PathPart: !Sub "${Prefix}-resource"
RestApiId: !Ref RestApi
Stage:
Type: AWS::ApiGateway::Stage
Properties:
DeploymentId: !Ref Deployment
RestApiId: !Ref RestApi
StageName: !Sub "${Prefix}-stage"
Method1:
Type: AWS::ApiGateway::Method
Properties:
AuthorizationType: NONE
HttpMethod: GET
Integration:
ConnectionType: INTERNET
Credentials: !GetAtt ApiGatewayRole.Arn
IntegrationHttpMethod: POST
Type: AWS_PROXY
Uri: !Sub "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${FunctionArn}/invocations"
ResourceId: !Ref Resource
RestApiId: !Ref RestApi
Method2:
Type: AWS::ApiGateway::Method
Properties:
AuthorizationType: NONE
HttpMethod: POST
Integration:
ConnectionType: INTERNET
Credentials: !GetAtt ApiGatewayRole.Arn
IntegrationHttpMethod: POST
Type: AWS_PROXY
Uri: !Sub "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${FunctionArn}/invocations"
ResourceId: !Ref Resource
RestApiId: !Ref RestApi
ApiGatewayRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Action: sts:AssumeRole
Principal:
Service:
- apigateway.amazonaws.com
Policies:
- PolicyName: !Sub "${Prefix}-InvokeFunctionPolicy"
PolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Action:
- lambda:InvokeFunction
Resource:
- !Ref FunctionArn
Code language: YAML (yaml)
For basic information on REST-type API Gateways, please refer to the following page.
The point is to define two Method resources.
One is for GET requests and the other is for POST requests.
Both are configured to invoke the aforementioned Lambda function.
Architecting
Use CloudFormation to build this environment and check its actual behavior.
Create CloudFormation stacks and check the resources in the stacks
Create CloudFormation stacks.
For information on how to create stacks and check each stack, please see the following page.
After reviewing the resources in each stack, information on the main resources created in this case is as follows
- WAF Web ACL: soa-05-003-WebACL
- WAF Web ACL Rule Group: soa-05-003-ComponentInspectionRuleGroup
- API Gateway: soa-05-003
- API Gateway endpoint: https://5qsyw758ji.execute-api.ap-northeast-1.amazonaws.com/soa-05-003-stage/soa-05-003-resource
- Lambda function: soa-05-003-function
WAF Web ACLs are also checked from the AWS Management Console.
You can see that this Web ACL is associated with API Gateway.
You can see that a rule group has been set up for the ACL.
Check the rule group.
Four rules have been created.
Review each rule.
These are the content rules for string checking for headers, query strings, bodies, and cookies, respectively.
Operation Check
Now that you are ready, check the operation of each rule.
String Inspection for Headers
Send HTTP requests with headers set with the curl command to the API Gateway.
Checking the rules for headers again, the content is to block traffic that contains “awstut1” in the header.
First, send a request that does not match the rule.
$ curl -H "test-header: awstut" https://5qsyw758ji.execute-api.ap-northeast-1.amazonaws.com/soa-05-003-stage/soa-05-003-resource | jq ...
{
"multiValueHeaders": {
"test-header": [
"awstut"
],
...
},
...
}
Code language: Bash (bash)
I sent a request with a header key name of “test-header” and a value of “awstut” and got a successful response.
This means that the traffic reached the API Gateway endpoint without being blocked by the WAF Web ACL and the backend Lambda function was executed.
Then send a rule matched request.
$ curl -H "test-header: awstut1" https://5qsyw758ji.execute-api.ap-northeast-1.amazonaws.com/soa-05-003-stage/soa-05-003-resource | jq
...
{
"message": "Forbidden"
}
Code language: Bash (bash)
The response was different from the previous one.
This means that the string inspection rules for headers were applied and the request was blocked.
String Inspection for Query String
Send an HTTP request with a query string to the API Gateway using the curl command.
Checking the rules for query strings again, the content is to block traffic that contains “awstut2” in the query string.
First, send a request that does not match the rule.
$ curl https://5qsyw758ji.execute-api.ap-northeast-1.amazonaws.com/soa-05-003-stage/soa-05-003-resource?test-query=awstut | jq
...
{
"queryStringParameters": {
"test-query": "awstut"
},
...
}
Code language: Bash (bash)
I sent a request with the query string key name “test-query” and value “awstut” and received a successful response.
Then send a request that matches the rule.
$ curl https://5qsyw758ji.execute-api.ap-northeast-1.amazonaws.com/soa-05-003-stage/soa-05-003-resource?test-query=awstut2 | jq
...
{
"message": "Forbidden"
}
Code language: Bash (bash)
The response was different from the previous one.
This means that the string inspection rules for the query string were applied and the request was blocked.
String Inspection for Body
Send HTTP request with body set by curl command to API Gateway.
Checking the rules for the body again, the content is to block traffic that contains “awstut3” in the body.
First, send a request that does not match the rule.
$ curl -X POST -d "test-data=awstut" https://5qsyw758ji.execute-api.ap-northeast-1.amazonaws.com/soa-05-003-stage/soa-05-003-resource | jq
...
{
"body": "test-data=awstut",
...
}
Code language: Bash (bash)
The body sent a request for “test-data=awstut” and received a successful response.
Then send a request that matches the rule.
$ curl -X POST -d "test-data=awstut3" https://5qsyw758ji.execute-api.ap-northeast-1.amazonaws.com/soa-05-003-stage/soa-05-003-resource | jq
...
{
"message": "Forbidden"
}
Code language: Bash (bash)
The response was different from the previous one.
This means that the string inspection rules for the body were applied and the request was blocked.
String Inspection for Cookies
Send HTTP requests with cookies set to the API Gateway with the curl command.
Checking the rules for cookies again, the content is to block traffic that contains “awstut4” in the cookie.
First, send a request that does not match the rule.
$ curl -b "test-cookie=awstut" https://5qsyw758ji.execute-api.ap-northeast-1.amazonaws.com/soa-05-003-stage/soa-05-003-resource | jq
...
{
"multiValueHeaders": {
"Cookie": [
"test-cookie=awstut"
],
...
},
...
}
Code language: Bash (bash)
I sent a request with a cookie of “test-cookie=awstut” and received a successful response.
Then send a request that matches the rule.
$ curl -b "test-cookie=awstut4" https://5qsyw758ji.execute-api.ap-northeast-1.amazonaws.com/soa-05-003-stage/soa-05-003-resource | jq ...
{
"message": "Forbidden"
}
Code language: Bash (bash)
The response was different from the previous one.
This means that the string inspection rules for cookies were applied and the request was blocked.
Summary
WAF Web ACL string checking rules were created and checked against four components (headers, query strings, bodies, and cookies).