is it possible to add safe URLs in AWS WAF? - amazon-waf

I know that AWS WAF is pretty dumb and non-configurable, but last time it becomes stricter.
We can't send even request to backend like:
POST https://our.url/page_id
{
"data": "<a></a>"
}
In this case awswaf:managed:aws:core-rule-set:CrossSiteScripting_Body_RC_COUNT rule will be triggered.
I tried to find any ways how core-rule-set may be customized, but looks like it is impossible. But I want to trust that is my lack of search and it may be customized in some way. If not, current WAF is simply unusable. I can't imagine case when it may be used with these strict non-editable standard rules.
So the question is:
Is it possible to set some safe domains (like https://our.url) that will be passed without blocking? Or maybe some ways to allow <a> tag for example?

You can customize the action on the AWS managed rule in this way:
Edit the AWS managed core set and change the rule action to: "Override to Count". Take note of the aws label for this rule (something like "awswaf:managed:aws:core-rule-set:CrossSiteScripting_Body")
Create a new rule that you add the end of all existing rules. This new rule should trigger on conditions (1) Statement "has a label", where you specify the above label (2) url matches the url you want to let through. Specify the action as "Allow"
Create a new rule that you add after the previous rule. This rule should trigger on same condition (1). Specify the action as "Block"

Based on #Chris answer:
Add AWSManagedRulesCommonRuleSet and override Action to Count for desired Rule (CrossSiteScripting_BODY in my case) (if you have to allow safe URLs inside query parameters, CrossSiteScripting_QUERYARGUMENTS should be changed same way, etc.)
Add custom rule allow-safe-URLs with Block action that will block all requests with CrossSiteScripting_BODY label and with JSON body that don't match <[ ]*a[\-_a-zA-Z0-9 ='"()]*href[ ]*=[ "']https:\/\/([a-zA-Z0-9\-]+\.)?example\.com[ "']*>*.<[ ]*\/[ ]*a[ ]*> RegEx. (only <a href=https://example.com>xxx</a> or <a href=https://anysubdomain.example.com>xxx</a> will be detected as safe)
Last default action should be Allow.
Final JSON of the WEB ACL will be looks like:
{
"Name": "test-waf",
"Id": "some-uuid-of-web-acl",
"ARN": "arn:aws:wafv2:us-east-1:1234567890:regional/webacl/test-waf/some-uuid-of-web-acl",
"DefaultAction": {
"Allow": {}
},
"Description": "Web ACL for URL whitelisting tests",
"Rules": [
{
"Name": "AWS-AWSManagedRulesCommonRuleSet",
"Priority": 0,
"Statement": {
"ManagedRuleGroupStatement": {
"VendorName": "AWS",
"Name": "AWSManagedRulesCommonRuleSet",
"Version": "Version_1.5",
"RuleActionOverrides": [
{
"Name": "CrossSiteScripting_BODY",
"ActionToUse": {
"Count": {}
}
}
]
}
},
"OverrideAction": {
"None": {}
},
"VisibilityConfig": {
"SampledRequestsEnabled": true,
"CloudWatchMetricsEnabled": true,
"MetricName": "AWS-AWSManagedRulesCommonRuleSet"
}
},
{
"Name": "allow-safe-URLs",
"Priority": 1,
"Statement": {
"AndStatement": {
"Statements": [
{
"NotStatement": {
"Statement": {
"RegexMatchStatement": {
"RegexString": "<[ ]*a[\\-_a-zA-Z0-9 ='\"()]*href[ ]*=[ \"']https:\\/\\/([a-zA-Z0-9\\-]+\\.)?example\\.com[ \"']*>*.<[ ]*\\/[ ]*a[ ]*>",
"FieldToMatch": {
"JsonBody": {
"MatchPattern": {
"All": {}
},
"MatchScope": "VALUE",
"InvalidFallbackBehavior": "EVALUATE_AS_STRING",
"OversizeHandling": "MATCH"
}
},
"TextTransformations": [
{
"Priority": 0,
"Type": "NONE"
}
]
}
}
}
},
{
"LabelMatchStatement": {
"Scope": "LABEL",
"Key": "awswaf:managed:aws:core-rule-set:CrossSiteScripting_Body"
}
}
]
}
},
"Action": {
"Block": {}
},
"VisibilityConfig": {
"SampledRequestsEnabled": true,
"CloudWatchMetricsEnabled": true,
"MetricName": "allow-safe-URLs"
}
}
],
"VisibilityConfig": {
"SampledRequestsEnabled": true,
"CloudWatchMetricsEnabled": true,
"MetricName": "test-waf"
},
"Capacity": 707,
"ManagedByFirewallManager": false,
"LabelNamespace": "awswaf:1234567890:webacl:test-waf:"
}
WARNING: this JSON is just a simple reproducible example (Proof of concept). And this configuration vulnerable for attacks with body like: <a href=https://example.com></a><a href=https://www.evil-url.com></a>. RegEx also may be vulnerable. You have to check if ONLY safe URLs included in body. Actual checking (URL-matching) logic should be more complicated. Don't use it as is (by copy/paste).

Related

Why is Google App Engine throwing access forbidden errors?

Could really use some help here. I have a GAE NodeJS app in the standard environment. Until a few days ago (09/23) it was running just fine, it would respond to requests as expected, etc.
Today, the app responds with 403's when I try to make any request to my appspot url. I'm 100% certain this is not a code issue, as if I deploy the same code to GAE in another project, it works fine. Furthermore, the only firewall rule is a wildcard to allow all traffic.
Edit: adding the only relevant-looking log entry I see from the project:
{
"protoPayload": {
"#type": "type.googleapis.com/google.cloud.audit.AuditLog",
"status": {},
"authenticationInfo": {
"principalEmail": "address#domain.com"
},
"requestMetadata": {
"callerIp": "x.x.x.x",
"requestAttributes": {
"time": "2021-09-23T15:04:05.198927Z",
"auth": {}
},
"destinationAttributes": {}
},
"serviceName": "appengine.googleapis.com",
"methodName": "google.appengine.v1.Services.UpdateService",
"authorizationInfo": [
{
"resource": "apps/my-google-cloud-project-id/services/default",
"permission": "appengine.services.update",
"granted": true,
"resourceAttributes": {}
}
],
"resourceName": "apps/my-google-cloud-project-id/services/default",
"serviceData": {
"#type": "type.googleapis.com/google.appengine.v1.AuditData",
"updateService": {
"request": {
"name": "apps/my-google-cloud-project-id/services/default",
"service": {
"networkSettings": {
"ingressTrafficAllowed": "INGRESS_TRAFFIC_ALLOWED_INTERNAL_AND_LB"
}
},
"updateMask": "networkSettings"
}
}
},
"resourceLocation": {
"currentLocations": [
"us-east1"
]
}
},
"insertId": "an-id",
"resource": {
"type": "gae_app",
"labels": {
"project_id": "my-google-cloud-project-id",
"zone": "",
"module_id": "default",
"version_id": ""
}
},
"timestamp": "2021-09-23T15:04:05.131761Z",
"severity": "NOTICE",
"logName": "projects/my-google-cloud-project-id/logs/cloudaudit.googleapis.com%2Factivity",
"operation": {
"id": "some-operation-uuid",
"producer": "appengine.googleapis.com/admin",
"first": true
},
"receiveTimestamp": "2021-09-23T15:04:05.495890906Z"
}
I don't recall making this change, and I'm not sure what the ingressTrafficAllowed value was before.
Somehow the ingress setting on the GAE service got changed. I believe that issue was fixed by going to GCP console > App Engine > Services > select affected service(s) -> Edit ingress setting from the top, and select the appropriate value.
I say I believe this fixed the issue as I was still getting 403's on my appspot url after doing this, and ultimately I ended up deleting and re-creating the project from scratch, which got everything working again. Clearly there was some misconfiguration somewhere in my project, but GCP does not make it easy to diagnose what the issue might be.

ARM Template deployment - CosmosDB provision fails with - Request rate is large. More Request Units may be needed, so no changes were made

I tried deploying cosmosDB and when I first create it I get this error -
"Request rate is large. More Request Units may be needed, so no changes were made"
After redeploying it works.
But the initial creation doesn't.
I extended the throughput to 50000 (autoscale)
and 10000 with fixed size.
Is there another option to extend the RUs?
"type": "Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers",
"apiVersion": "2020-03-01",
"name": "
"dependsOn":
],
"properties": {
"resource": {
"id": "subscriptions",
"indexingPolicy": {
"indexingMode": "consistent",
"automatic": true,
"includedPaths": [
{
"path": "/*"
}
],
"excludedPaths": [
{
"path": "/\"_etag\"/?"
}
],
"spatialIndexes": [
{
"path": "/*",
"types": ["Point", "LineString", "Polygon", "MultiPolygon"]
}
]
},
"partitionKey": {
"paths": ["/partitionKey"],
"kind": "Hash"
},
"uniqueKeyPolicy": {
"uniqueKeys": []
},
"conflictResolutionPolicy": {
"mode": "LastWriterWins",
"conflictResolutionPath": "/_ts"
}
},
"options": {
"autoscaleSettings": {
"maxThroughput": 50000
}
}
}
The error message indicates that there was a large number of requests hitting the master partition for your account. This can be caused by other clients querying the database resource to list containers or querying containers to get it's properties. It can also happen when you are deploying a large number of Cosmos resources at the same time. This is mostly a transient issue and is hard to reproduce.
Your arm template for this container looks correct. I'm guessing you've purposely removed the name and depends on parameters.
I would update the api-version to 2020-04-01 however. There are issues with 2020-03-01 with autoscale and that api-version is going to be deprecated in the future.

EventGrid Trigger - How to set clienttrackingid from triggerbody?

In a microservice environment where requests span multiple services including eventgrid i'd like to configure an end-to-end logging with correlationid.
Inspired by this blog https://toonvanhoutte.wordpress.com/2018/08/05/end-to-end-correlation-across-logic-apps/
How can i configure the EventGrid triggers clientTrackingId with my correlationnr from Events data payload?
Checkout my definition below which does not work.
If i substitute "#{coalesce(json(triggerBody().Data)?.CorrelationNr, guid())}" with a string value or even "#parameters('$connections')['azureeventgrid']['connectionId']" it works like a charm.
"triggers": {
"When_a_resource_event_occurs": {
"correlation": {
"clientTrackingId": "#{coalesce(json(triggerBody().Data)?.CorrelationNr, guid())}"
},
"inputs": {
"body": {
"properties": {
"destination": {
"endpointType": "webhook",
"properties": {
"endpointUrl": "#{listCallbackUrl()}"
}
},
"filter": {
"includedEventTypes": [
"webhook.sp.updated"
]
},
"topic": "/subscriptions/xxxx/resourceGroups/xxx/providers/Microsoft.EventGrid/topics/WebHookManager"
}
},
"host": {
"connection": {
"name": "#parameters('$connections')['azureeventgrid']['connectionId']"
}
},
"path": "/subscriptions/#{encodeURIComponent('xxx')}/providers/#{encodeURIComponent('Microsoft.EventGrid.Topics')}/resource/eventSubscriptions",
"queries": {
"x-ms-api-version": "2017-06-15-preview"
}
},
"splitOn": "#triggerBody()",
"type": "ApiConnectionWebhook"
}
}
Logic App does not trigger. No Error message.
Please check the description about clientTrackingId, and your logic app no runs history is because your triggerBody() doesn't have CorrelationNr with the definition you show.
Actually your Event Grid trigger has detected the event, it just couldn't run with the logic. You could go to the EVALUATION and check the trigger history. It's because the value is null, then it won't run.
If you use HTTP request trigger, you could set the x-my-custom-correlation-id header. or set any key-value in the json body, then set the clientTrackingId with like #{coalesce(json(triggerBody())['keyname'], guid())}.
And if you are using some trigger without header, you have to point the value with string or other parameter like you said the connectionid or the parameter value you custom like below.
So the point is the clientTrackingId must be set before it runs and value could be obatined.

Detecting when user unlinks alexa skill

I'm implementing an Alexa Smart Home skill and I want to know if a user is still using the app after a while.
Google Home, for example, sends a request when I unlink my app from the Google Smarthome app. I need to know it to disable sending updates to Amazon Alexa gateway if a user isn't using the skill anymore.
What the best way of doing it? Alexa documentation doesn't talk about it.
Can I rely on just checking if the user has a expired OAuth tokens? E.g. if expired for more than a day, mark user as inactive.
Another thing I'm going to test out tomorrow is just see the gateway response after having unlinked the skill. But for my case it wouldn't be good option anyway, as I will only know the user state after a physical change and trying to submit it and have it possibly fail. Which can happen after days or weeks, so it isn't that reliable.
You can integrate with Alexa Skill Events and get notification when user disables the Skill.
https://developer.amazon.com/docs/smapi/skill-events-in-alexa-skills.html#skill-disabled-event.
The SkillDisabled event only contains user_id (i.e. no access token). So you would also need to listen for the SkillAccountLinked event so you can link that user_id with your own user identifier.
Your Smart Home Skill manifest should look like this:
{
"manifest": {
"publishingInformation": {
"locales": {
"en-US": {
"summary": "...",
"examplePhrases": [
"Alexa, ...",
"Alexa, ...",
"Alexa, ..."
],
"keywords": [],
"name": "...",
"smallIconUri": "...",
"description": "...",
"largeIconUri": "..."
}
},
"isAvailableWorldwide": false,
"testingInstructions": "...",
"category": "SMART_HOME",
"distributionCountries": [
"US"
]
},
"apis": {
"smartHome": {
"endpoint": {
"uri": "arn:aws:lambda:..."
},
"protocolVersion": "3"
}
},
"manifestVersion": "1.0",
"permissions": [
{
"name": "alexa::async_event:write"
}
],
"privacyAndCompliance": {
"allowsPurchases": false,
"locales": {
"en-US": {
"termsOfUseUrl": "...",
"privacyPolicyUrl": "..."
}
},
"isExportCompliant": true,
"containsAds": false,
"isChildDirected": false,
"usesPersonalInfo": false
},
"events": {
"endpoint": {
"uri": "arn:aws:lambda:..."
},
"subscriptions": [
{
"eventName": "SKILL_ENABLED"
},
{
"eventName": "SKILL_DISABLED"
},
{
"eventName": "SKILL_PERMISSION_ACCEPTED"
},
{
"eventName": "SKILL_PERMISSION_CHANGED"
},
{
"eventName": "SKILL_ACCOUNT_LINKED"
}
],
"regions": {
"NA": {
"endpoint": {
"uri": "arn:aws:lambda:..."
}
}
}
}
}
}

JSON schema deeper object uniqueness

I'm trying to get into JSON schema definitions and wanted to find out, how to achieve a deeper object uniqueness in the schema definition. Please look at the following example definition, in this case a simple IO of a module.
{
"$schema": "http://json-schema.org/draft-06/schema#",
"type": "object",
"required": ["modulIOs"],
"properties": {
"modulIOs": {
"type": "array",
"uniqueItems": true,
"items": {
"allOf": [
{
"type": "object",
"required": ["ioPosition","ioType","ioFunction"],
"additionalProperties": false,
"properties": {
"ioPosition": {
"type": "integer"
},
"ioType": {
"type":"string",
"enum": ["in","out"]
},
"ioFunction": {
"type":"string"
}
}
}
]
}
}
}
}
When I validate the following with i.E. draft-06 I get a positive validation.
{"modulIOs":
[
{
"ioPosition":1,
"ioType":"in",
"ioFunction":"240 V AC in"
},
{
"ioPosition":1,
"ioType":"in",
"ioFunction":"24 V DC in"
}
]
}
I'm aware that the validation is successfull because the validator does what he's intended to - it checks the structure of a JSON-object, but is there a possibility to validate object value data in deeper objects or do i need to perform the check elsewhere?
This is not currently possible with JSON Schema (at draft-7).
There is an issue raised on the official spec repo github for this: https://github.com/json-schema-org/json-schema-spec/issues/538
If you (or anyone reading this) really wants this, please thumbsup the first issue comment.
It's currently unlikely to make it into the next draft, and even if it did, time to impleemntations picking it up may be slow.
You'll need to do this validation after your JSON Schema validation process.
You can validate data value of your object fields by using JSON schema validation.
For example, if you need to check if ioPosition is between 0 and 100 you can use:
"ioPosition": {
"type": "integer",
"minimum": 0,
"maximum": 100
}
If you need to validate ioFunction field you can use regualr expression such as:
"ioFunction": {
"type": "string",
"pattern": "^[0-9]+ V [A,D]C"
}
Take a look at json-schema-validation.

Resources