{attributes.check_metadata.remediation.recommendation.url && (
-
Learn more
-
+
)}
@@ -179,13 +178,12 @@ export const FindingDetail = ({
{/* Additional Resources section */}
{attributes.check_metadata.remediation.code.other && (
-
View documentation
-
+
)}
diff --git a/ui/components/integrations/forms/saml-config-form.tsx b/ui/components/integrations/forms/saml-config-form.tsx
index ecea01343f..c9e71d628d 100644
--- a/ui/components/integrations/forms/saml-config-form.tsx
+++ b/ui/components/integrations/forms/saml-config-form.tsx
@@ -1,6 +1,5 @@
"use client";
-import Link from "next/link";
import { Dispatch, SetStateAction, useEffect, useRef, useState } from "react";
import { useFormState } from "react-dom";
import { z } from "zod";
@@ -9,6 +8,7 @@ import { createSamlConfig, updateSamlConfig } from "@/actions/integrations";
import { AddIcon } from "@/components/icons";
import { useToast } from "@/components/ui";
import { CustomButton, CustomServerInput } from "@/components/ui/custom";
+import { CustomLink } from "@/components/ui/custom/custom-link";
import { SnippetChip } from "@/components/ui/entities";
import { FormButtons } from "@/components/ui/form";
import { apiBaseUrl } from "@/lib";
@@ -239,9 +239,9 @@ export const SamlConfigForm = ({
assign the user's role. If the role does not exist, one will
be created with minimal permissions. You can assign permissions to
roles on the{" "}
-
- Roles
- {" "}
+
+ Roles
+ {" "}
page.
diff --git a/ui/components/integrations/saml-integration-card.tsx b/ui/components/integrations/saml-integration-card.tsx
index ae3eb6f60d..6ad745aa85 100644
--- a/ui/components/integrations/saml-integration-card.tsx
+++ b/ui/components/integrations/saml-integration-card.tsx
@@ -1,13 +1,13 @@
"use client";
import { Card, CardBody, CardHeader } from "@nextui-org/react";
-import { Link } from "@nextui-org/react";
import { CheckIcon, Trash2Icon } from "lucide-react";
import { useState } from "react";
import { deleteSamlConfig } from "@/actions/integrations";
import { useToast } from "@/components/ui";
import { CustomAlertModal, CustomButton } from "@/components/ui/custom";
+import { CustomLink } from "@/components/ui/custom/custom-link";
import { SamlConfigForm } from "./forms";
@@ -73,14 +73,9 @@ export const SamlIntegrationCard = ({ samlConfig }: { samlConfig?: any }) => {
) : (
<>
Configure SAML Single Sign-On for secure authentication.{" "}
-
+
Read the docs
-
+
>
)}
diff --git a/ui/components/lighthouse/chat.tsx b/ui/components/lighthouse/chat.tsx
index c31c6a08b9..54ca5604e7 100644
--- a/ui/components/lighthouse/chat.tsx
+++ b/ui/components/lighthouse/chat.tsx
@@ -1,12 +1,12 @@
"use client";
import { useChat } from "@ai-sdk/react";
-import Link from "next/link";
import { useEffect, useRef } from "react";
import { useForm } from "react-hook-form";
import { MemoizedMarkdown } from "@/components/lighthouse/memoized-markdown";
import { CustomButton, CustomTextarea } from "@/components/ui/custom";
+import { CustomLink } from "@/components/ui/custom/custom-link";
import { Form } from "@/components/ui/form";
interface SuggestedAction {
@@ -138,12 +138,14 @@ export const Chat = ({ hasConfig, isActive }: ChatProps) => {
? "Please configure your OpenAI API key to use Lighthouse AI."
: "OpenAI API key is invalid. Please update your key to use Lighthouse AI."}
-
Configure API Key
-
+
)}
diff --git a/ui/components/providers/forms/muted-findings-config-form.tsx b/ui/components/providers/forms/muted-findings-config-form.tsx
index 52c5e2c7df..1a5241b330 100644
--- a/ui/components/providers/forms/muted-findings-config-form.tsx
+++ b/ui/components/providers/forms/muted-findings-config-form.tsx
@@ -1,7 +1,6 @@
"use client";
import { Textarea } from "@nextui-org/react";
-import Link from "next/link";
import { Dispatch, SetStateAction, useEffect, useState } from "react";
import { useFormState } from "react-dom";
@@ -14,6 +13,7 @@ import {
import { DeleteIcon } from "@/components/icons";
import { useToast } from "@/components/ui";
import { CustomButton } from "@/components/ui/custom";
+import { CustomLink } from "@/components/ui/custom/custom-link";
import { FormButtons } from "@/components/ui/form";
import { fontMono } from "@/config/fonts";
import { convertToYaml, parseYamlValidation } from "@/lib/yaml";
@@ -178,13 +178,9 @@ export const MutedFindingsConfigForm = ({
Learn more about configuring the Mutelist{" "}
-
+
here
-
+
.
diff --git a/ui/components/providers/workflow/forms/test-connection-form.tsx b/ui/components/providers/workflow/forms/test-connection-form.tsx
index 6fdc6e2215..0ac38c03a1 100644
--- a/ui/components/providers/workflow/forms/test-connection-form.tsx
+++ b/ui/components/providers/workflow/forms/test-connection-form.tsx
@@ -3,7 +3,6 @@
import { zodResolver } from "@hookform/resolvers/zod";
import { Icon } from "@iconify/react";
import { Checkbox } from "@nextui-org/react";
-import Link from "next/link";
import { useRouter } from "next/navigation";
import { useState } from "react";
import { useForm } from "react-hook-form";
@@ -18,6 +17,7 @@ import { getTask } from "@/actions/task/tasks";
import { CheckIcon, RocketIcon } from "@/components/icons";
import { useToast } from "@/components/ui";
import { CustomButton } from "@/components/ui/custom";
+import { CustomLink } from "@/components/ui/custom/custom-link";
import { Form } from "@/components/ui/form";
import { checkTaskStatus } from "@/lib/helper";
import { ProviderType } from "@/types";
@@ -295,8 +295,9 @@ export const TestConnectionForm = ({
{apiErrorMessage ? (
-
Back to providers
-
+
) : connectionStatus?.error ? (
router.back() : onResetCredentials}
diff --git a/ui/components/providers/workflow/forms/via-credentials/m365-credentials-form.tsx b/ui/components/providers/workflow/forms/via-credentials/m365-credentials-form.tsx
index 5fa8f2f9a1..13eb61710d 100644
--- a/ui/components/providers/workflow/forms/via-credentials/m365-credentials-form.tsx
+++ b/ui/components/providers/workflow/forms/via-credentials/m365-credentials-form.tsx
@@ -1,8 +1,8 @@
-import Link from "next/link";
import { Control } from "react-hook-form";
import { InfoIcon } from "@/components/icons";
import { CustomInput } from "@/components/ui/custom";
+import { CustomLink } from "@/components/ui/custom/custom-link";
import { M365Credentials } from "@/types";
export const M365CredentialsForm = ({
@@ -57,14 +57,12 @@ export const M365CredentialsForm = ({
{" "}
User and password authentication is being deprecated due to
Microsoft's on-going MFA enforcement across all tenants (see{" "}
-
Microsoft docs
-
+
).
@@ -76,14 +74,12 @@ export const M365CredentialsForm = ({
Due to that change, you must only{" "}
-
use application authentication
- {" "}
+ {" "}
to maintain all Prowler M365 scan capabilities.
{getProviderHelpText(providerType as string).text}
-
Read the docs
-
+
);
diff --git a/ui/components/ui/custom/custom-link.tsx b/ui/components/ui/custom/custom-link.tsx
new file mode 100644
index 0000000000..d52fbeaca2
--- /dev/null
+++ b/ui/components/ui/custom/custom-link.tsx
@@ -0,0 +1,51 @@
+import Link from "next/link";
+import React from "react";
+
+import { cn } from "@/lib";
+
+interface CustomLinkProps
+ extends React.AnchorHTMLAttributes {
+ href: string;
+ target?: "_self" | "_blank" | string;
+ ariaLabel?: string;
+ className?: string;
+ children: React.ReactNode;
+ scroll?: boolean;
+ size?: string;
+}
+
+export const CustomLink = React.forwardRef(
+ (
+ {
+ href,
+ target = "_blank",
+ ariaLabel,
+ className,
+ children,
+ scroll = true,
+ size = "xs",
+ ...props
+ },
+ ref,
+ ) => {
+ return (
+
+ {children}
+
+ );
+ },
+);
+
+CustomLink.displayName = "CustomLink";
diff --git a/ui/components/ui/user-nav/user-nav.tsx b/ui/components/ui/user-nav/user-nav.tsx
index ff441f0ec8..f4734e6384 100644
--- a/ui/components/ui/user-nav/user-nav.tsx
+++ b/ui/components/ui/user-nav/user-nav.tsx
@@ -1,7 +1,6 @@
"use client";
import { LogOut, User } from "lucide-react";
-import Link from "next/link";
import { useSession } from "next-auth/react";
import { logOut } from "@/actions/auth";
@@ -10,6 +9,8 @@ import {
AvatarFallback,
AvatarImage,
} from "@/components/ui/avatar/avatar";
+import { Button } from "@/components/ui/button/button";
+import { CustomLink } from "@/components/ui/custom/custom-link";
import {
DropdownMenu,
DropdownMenuContent,
@@ -26,8 +27,6 @@ import {
TooltipTrigger,
} from "@/components/ui/tooltip/tooltip";
-import { Button } from "../button/button";
-
export const UserNav = () => {
const { data: session } = useSession();
@@ -80,10 +79,14 @@ export const UserNav = () => {
-
+
Account
-
+
From 95791a9909b20a414607ba1d97de637dd2681986 Mon Sep 17 00:00:00 2001
From: Andoni Alonso <14891798+andoniaf@users.noreply.github.com>
Date: Thu, 24 Jul 2025 09:34:45 +0200
Subject: [PATCH 19/28] chore(aws): replace known errors with warnings (#8347)
Co-authored-by: Sergio Garcia
---
prowler/CHANGELOG.md | 5 +++-
.../elasticbeanstalk_service.py | 27 ++++++++++++++++---
.../providers/aws/services/iam/iam_service.py | 5 +++-
.../secretsmanager/secretsmanager_service.py | 16 +++++++++++
.../providers/aws/services/ssm/ssm_service.py | 15 +++++++++++
5 files changed, 62 insertions(+), 6 deletions(-)
diff --git a/prowler/CHANGELOG.md b/prowler/CHANGELOG.md
index bbb3ed5515..e8250cf07f 100644
--- a/prowler/CHANGELOG.md
+++ b/prowler/CHANGELOG.md
@@ -5,9 +5,12 @@ All notable changes to the **Prowler SDK** are documented in this file.
## [v5.10.0] (Prowler UNRELEASED)
### Added
-- Add `bedrock_api_key_no_administrative_privileges` check for AWS provider [(#8321)](https://github.com/prowler-cloud/prowler/pull/8321)
+- `bedrock_api_key_no_administrative_privileges` check for AWS provider [(#8321)](https://github.com/prowler-cloud/prowler/pull/8321)
- Support App Key Content in GitHub provider [(#8271)](https://github.com/prowler-cloud/prowler/pull/8271)
+### Changed
+- Handle some AWS errors as warnings instead of errors [(#8347)](https://github.com/prowler-cloud/prowler/pull/8347)
+
### Fixed
- False positives in SQS encryption check for ephemeral queues [(#8330)](https://github.com/prowler-cloud/prowler/pull/8330)
diff --git a/prowler/providers/aws/services/elasticbeanstalk/elasticbeanstalk_service.py b/prowler/providers/aws/services/elasticbeanstalk/elasticbeanstalk_service.py
index 0005177bdb..c8d821687d 100644
--- a/prowler/providers/aws/services/elasticbeanstalk/elasticbeanstalk_service.py
+++ b/prowler/providers/aws/services/elasticbeanstalk/elasticbeanstalk_service.py
@@ -1,5 +1,6 @@
from typing import Optional
+from botocore.client import ClientError
from pydantic.v1 import BaseModel
from prowler.lib.logger import logger
@@ -71,6 +72,17 @@ class ElasticBeanstalk(AWSService):
and option["OptionName"] == "StreamLogs"
):
environment.cloudwatch_stream_logs = option.get("Value", "false")
+ except ClientError as error:
+ if error.response["Error"]["Code"] in [
+ "InvalidParameterValue",
+ ]:
+ logger.warning(
+ f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
+ )
+ else:
+ logger.error(
+ f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
+ )
except Exception as error:
logger.error(
f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
@@ -84,10 +96,17 @@ class ElasticBeanstalk(AWSService):
"ResourceTags"
]
resource.tags = response
- except Exception as error:
- logger.error(
- f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
- )
+ except ClientError as error:
+ if error.response["Error"]["Code"] in [
+ "ResourceNotFoundException",
+ ]:
+ logger.warning(
+ f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
+ )
+ else:
+ logger.error(
+ f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
+ )
except Exception as error:
logger.error(
f"{regional_client.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
diff --git a/prowler/providers/aws/services/iam/iam_service.py b/prowler/providers/aws/services/iam/iam_service.py
index 914483081b..1ff3dbd07a 100644
--- a/prowler/providers/aws/services/iam/iam_service.py
+++ b/prowler/providers/aws/services/iam/iam_service.py
@@ -865,7 +865,10 @@ class IAM(AWSService):
SAMLProviderArn=resource.arn
).get("Tags", [])
except Exception as error:
- if error.response["Error"]["Code"] == "NoSuchEntityException":
+ if error.response["Error"]["Code"] in [
+ "NoSuchEntity",
+ "NoSuchEntityException",
+ ]:
logger.warning(
f"{self.region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
)
diff --git a/prowler/providers/aws/services/secretsmanager/secretsmanager_service.py b/prowler/providers/aws/services/secretsmanager/secretsmanager_service.py
index 8a85d33502..695072c878 100644
--- a/prowler/providers/aws/services/secretsmanager/secretsmanager_service.py
+++ b/prowler/providers/aws/services/secretsmanager/secretsmanager_service.py
@@ -2,6 +2,7 @@ import json
from datetime import datetime, timezone
from typing import Dict, List, Optional
+from botocore.client import ClientError
from pydantic.v1 import BaseModel, Field
from prowler.lib.logger import logger
@@ -67,6 +68,21 @@ class SecretsManager(AWSService):
)
if secret_policy.get("ResourcePolicy"):
secret.policy = json.loads(secret_policy["ResourcePolicy"])
+ except ClientError as error:
+ if error.response["Error"]["Code"] in [
+ "ResourceNotFoundException",
+ ]:
+ logger.warning(
+ f"{self.region} --"
+ f" {error.__class__.__name__}[{error.__traceback__.tb_lineno}]:"
+ f" {error}"
+ )
+ else:
+ logger.error(
+ f"{self.region} --"
+ f" {error.__class__.__name__}[{error.__traceback__.tb_lineno}]:"
+ f" {error}"
+ )
except Exception as error:
logger.error(
f"{self.region} --"
diff --git a/prowler/providers/aws/services/ssm/ssm_service.py b/prowler/providers/aws/services/ssm/ssm_service.py
index 33f1187993..689047551c 100644
--- a/prowler/providers/aws/services/ssm/ssm_service.py
+++ b/prowler/providers/aws/services/ssm/ssm_service.py
@@ -99,6 +99,21 @@ class SSM(AWSService):
"AccountIds"
]
+ except ClientError as error:
+ if error.response["Error"]["Code"] in [
+ "InvalidDocumentOperation",
+ ]:
+ logger.warning(
+ f"{regional_client.region} --"
+ f" {error.__class__.__name__}[{error.__traceback__.tb_lineno}]:"
+ f" {error}"
+ )
+ else:
+ logger.error(
+ f"{regional_client.region} --"
+ f" {error.__class__.__name__}[{error.__traceback__.tb_lineno}]:"
+ f" {error}"
+ )
except Exception as error:
logger.error(
f"{regional_client.region} --"
From 44d70f8467a555f6711169eef8b60c27591653c0 Mon Sep 17 00:00:00 2001
From: Chandrapal Badshah
Date: Thu, 24 Jul 2025 14:20:36 +0530
Subject: [PATCH 20/28] fix(lighthouse): update prompt and tool schema for
checks tool (#8265)
Co-authored-by: Chandrapal Badshah <12944530+Chan9390@users.noreply.github.com>
---
ui/lib/lighthouse/prompts.ts | 4 ++--
ui/types/lighthouse/checks.ts | 2 +-
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/ui/lib/lighthouse/prompts.ts b/ui/lib/lighthouse/prompts.ts
index 925640aa7f..3dfcc8c965 100644
--- a/ui/lib/lighthouse/prompts.ts
+++ b/ui/lib/lighthouse/prompts.ts
@@ -127,8 +127,8 @@ You operate in an agent loop, iterating through these steps:
- Fetches information related to:
- All findings data across providers. Supports filtering by severity, status, etc.
- Unique metadata values from findings
- - Remediation for checks
- - Check IDs supported by different provider types
+ - Available checks for a specific provider (aws, gcp, azure, kubernetes, etc)
+ - Details of a specific check including details about severity, risk, remediation, compliances that are associated with the check, etc
### roles_agent
diff --git a/ui/types/lighthouse/checks.ts b/ui/types/lighthouse/checks.ts
index 186eb6cae6..5c0aaaa455 100644
--- a/ui/types/lighthouse/checks.ts
+++ b/ui/types/lighthouse/checks.ts
@@ -10,5 +10,5 @@ export const checkSchema = z.object({
});
export const checkDetailsSchema = z.object({
- id: z.string(),
+ checkId: z.string(),
});
From 04749c1da1a4dbd4ecd5270e1a4f2aeb770ebb74 Mon Sep 17 00:00:00 2001
From: Andoni Alonso <14891798+andoniaf@users.noreply.github.com>
Date: Thu, 24 Jul 2025 12:03:30 +0200
Subject: [PATCH 21/28] fix(aws): `sns_topics_not_publicly_accessible` false
positive with aws:SourceArn conditions (#8340)
Co-authored-by: MrCloudSec
---
prowler/CHANGELOG.md | 1 +
.../providers/aws/services/iam/lib/policy.py | 252 +++++++++++++----
.../sns_topics_not_publicly_accessible.py | 58 ++--
...ingress_from_internet_to_all_ports_test.py | 2 +-
.../aws/services/iam/lib/policy_test.py | 265 ++++++++++++++++++
...sns_topics_not_publicly_accessible_test.py | 250 ++++++++++++++++-
6 files changed, 729 insertions(+), 99 deletions(-)
diff --git a/prowler/CHANGELOG.md b/prowler/CHANGELOG.md
index e8250cf07f..2bb9dac6e3 100644
--- a/prowler/CHANGELOG.md
+++ b/prowler/CHANGELOG.md
@@ -20,6 +20,7 @@ All notable changes to the **Prowler SDK** are documented in this file.
### Fixed
- Add more validations to Azure Storage models when some values are None to avoid serialization issues [(#8325)](https://github.com/prowler-cloud/prowler/pull/8325)
+- `sns_topics_not_publicly_accessible` false positive with `aws:SourceArn` conditions [(#8326)](https://github.com/prowler-cloud/prowler/issues/8326)
---
diff --git a/prowler/providers/aws/services/iam/lib/policy.py b/prowler/providers/aws/services/iam/lib/policy.py
index b560389feb..c3b6eb3dba 100644
--- a/prowler/providers/aws/services/iam/lib/policy.py
+++ b/prowler/providers/aws/services/iam/lib/policy.py
@@ -223,6 +223,108 @@ def check_full_service_access(service: str, policy: dict) -> bool:
return all_target_service_actions.issubset(actions_allowed_on_all_resources)
+def has_public_principal(statement: dict) -> bool:
+ """
+ Check if a policy statement has a public principal.
+
+ Args:
+ statement (dict): IAM policy statement
+
+ Returns:
+ bool: True if the statement has a public principal, False otherwise
+ """
+ principal = statement.get("Principal", "")
+ return (
+ "*" in principal
+ or "arn:aws:iam::*:root" in principal
+ or (
+ isinstance(principal, dict)
+ and (
+ "*" in principal.get("AWS", "")
+ or "arn:aws:iam::*:root" in principal.get("AWS", "")
+ or (
+ isinstance(principal.get("AWS"), list)
+ and (
+ "*" in principal["AWS"]
+ or "arn:aws:iam::*:root" in principal["AWS"]
+ )
+ )
+ or "*" in principal.get("CanonicalUser", "")
+ or "arn:aws:iam::*:root" in principal.get("CanonicalUser", "")
+ )
+ )
+ )
+
+
+def has_restrictive_source_arn_condition(
+ statement: dict, source_account: str = ""
+) -> bool:
+ """
+ Check if a policy statement has a restrictive aws:SourceArn condition.
+
+ A SourceArn condition is considered restrictive if:
+ 1. It doesn't contain overly permissive wildcards (like "*" or "arn:aws:s3:::*")
+ 2. When source_account is provided, the ARN either contains no account field (like S3 buckets)
+ or contains the source_account
+
+ Args:
+ statement (dict): IAM policy statement
+ source_account (str): The account to check restrictions for (optional)
+
+ Returns:
+ bool: True if the statement has a restrictive aws:SourceArn condition, False otherwise
+ """
+ if "Condition" not in statement:
+ return False
+
+ for condition_operator in statement["Condition"]:
+ for condition_key, condition_value in statement["Condition"][
+ condition_operator
+ ].items():
+ if condition_key.lower() == "aws:sourcearn":
+ arn_values = (
+ condition_value
+ if isinstance(condition_value, list)
+ else [condition_value]
+ )
+
+ for arn_value in arn_values:
+ if (
+ arn_value == "*" # Global wildcard
+ or arn_value.count("*")
+ >= 3 # Too many wildcards (e.g., arn:aws:*:*:*:*)
+ or (
+ isinstance(arn_value, str)
+ and (
+ arn_value.endswith(
+ ":::*"
+ ) # Service-wide wildcard (e.g., arn:aws:s3:::*)
+ or arn_value.endswith(
+ ":*"
+ ) # Resource wildcard (e.g., arn:aws:sns:us-east-1:123456789012:*)
+ )
+ )
+ ):
+ return False
+
+ if source_account:
+ arn_parts = arn_value.split(":")
+ if len(arn_parts) > 4 and arn_parts[4] and arn_parts[4] != "*":
+ if arn_parts[4].isdigit():
+ if source_account not in arn_value:
+ return False
+ else:
+ if arn_parts[4] != source_account:
+ return False
+ elif len(arn_parts) > 4 and arn_parts[4] == "*":
+ return False
+ # else: ARN doesn't contain account field (like S3 bucket), so it's restrictive
+
+ return True
+
+ return False
+
+
def is_condition_restricting_from_private_ip(condition_statement: dict) -> bool:
"""Check if the policy condition is coming from a private IP address.
@@ -303,61 +405,49 @@ def is_policy_public(
for statement in policy.get("Statement", []):
# Only check allow statements
if statement["Effect"] == "Allow":
+ has_public_access = has_public_principal(statement)
+
principal = statement.get("Principal", "")
- if (
- "*" in principal
- or "arn:aws:iam::*:root" in principal
- or (
- isinstance(principal, dict)
- and (
- "*" in principal.get("AWS", "")
- or "arn:aws:iam::*:root" in principal.get("AWS", "")
- or (
- isinstance(principal.get("AWS"), str)
- and source_account
- and not is_cross_account_allowed
- and source_account not in principal.get("AWS", "")
- )
- or (
- isinstance(principal.get("AWS"), list)
- and (
- "*" in principal["AWS"]
- or "arn:aws:iam::*:root" in principal["AWS"]
- or (
- source_account
- and not is_cross_account_allowed
- and not any(
- source_account in principal_aws
- for principal_aws in principal["AWS"]
- )
- )
- )
- )
- or "*" in principal.get("CanonicalUser", "")
- or "arn:aws:iam::*:root"
- in principal.get("CanonicalUser", "")
- or check_cross_service_confused_deputy
- and (
- # Check if function can be invoked by other AWS services if check_cross_service_confused_deputy is True
- (
- ".amazonaws.com" in principal.get("Service", "")
- or ".amazon.com" in principal.get("Service", "")
- or "*" in principal.get("Service", "")
- )
- and (
- "secretsmanager.amazonaws.com"
- not in principal.get(
- "Service", ""
- ) # AWS ensures that resources called by SecretsManager are executed in the same AWS account
- or "eks.amazonaws.com"
- not in principal.get(
- "Service", ""
- ) # AWS ensures that resources called by EKS are executed in the same AWS account
- )
- )
+ if not has_public_access and isinstance(principal, dict):
+ # Check for cross-account access when not allowed
+ if (
+ isinstance(principal.get("AWS"), str)
+ and source_account
+ and not is_cross_account_allowed
+ and source_account not in principal.get("AWS", "")
+ ) or (
+ isinstance(principal.get("AWS"), list)
+ and source_account
+ and not is_cross_account_allowed
+ and not any(
+ source_account in principal_aws
+ for principal_aws in principal["AWS"]
)
- )
- ) and (
+ ):
+ has_public_access = True
+
+ # Check for cross-service confused deputy
+ if check_cross_service_confused_deputy and (
+ # Check if function can be invoked by other AWS services if check_cross_service_confused_deputy is True
+ (
+ ".amazonaws.com" in principal.get("Service", "")
+ or ".amazon.com" in principal.get("Service", "")
+ or "*" in principal.get("Service", "")
+ )
+ and (
+ "secretsmanager.amazonaws.com"
+ not in principal.get(
+ "Service", ""
+ ) # AWS ensures that resources called by SecretsManager are executed in the same AWS account
+ or "eks.amazonaws.com"
+ not in principal.get(
+ "Service", ""
+ ) # AWS ensures that resources called by EKS are executed in the same AWS account
+ )
+ ):
+ has_public_access = True
+
+ if has_public_access and (
not not_allowed_actions # If not_allowed_actions is empty, the function will not consider the actions in the policy
or (
statement.get(
@@ -498,9 +588,29 @@ def is_condition_block_restrictive(
"aws:sourcevpc" != value
and "aws:sourcevpce" != value
):
- if source_account not in item:
- is_condition_key_restrictive = False
- break
+ if value == "aws:sourcearn":
+ # Use the specialized function to properly validate SourceArn restrictions
+ # Create a minimal statement to test with our function
+ test_statement = {
+ "Condition": {
+ condition_operator: {
+ value: condition_statement[
+ condition_operator
+ ][value]
+ }
+ }
+ }
+ is_condition_key_restrictive = (
+ has_restrictive_source_arn_condition(
+ test_statement, source_account
+ )
+ )
+ if not is_condition_key_restrictive:
+ break
+ else:
+ if source_account not in item:
+ is_condition_key_restrictive = False
+ break
if is_condition_key_restrictive:
is_condition_valid = True
@@ -516,11 +626,31 @@ def is_condition_block_restrictive(
if is_cross_account_allowed:
is_condition_valid = True
else:
- if (
- source_account
- in condition_statement[condition_operator][value]
- ):
- is_condition_valid = True
+ if value == "aws:sourcearn":
+ # Use the specialized function to properly validate SourceArn restrictions
+ # Create a minimal statement to test with our function
+ test_statement = {
+ "Condition": {
+ condition_operator: {
+ value: condition_statement[
+ condition_operator
+ ][value]
+ }
+ }
+ }
+ is_condition_valid = (
+ has_restrictive_source_arn_condition(
+ test_statement, source_account
+ )
+ )
+ else:
+ if (
+ source_account
+ in condition_statement[condition_operator][
+ value
+ ]
+ ):
+ is_condition_valid = True
return is_condition_valid
diff --git a/prowler/providers/aws/services/sns/sns_topics_not_publicly_accessible/sns_topics_not_publicly_accessible.py b/prowler/providers/aws/services/sns/sns_topics_not_publicly_accessible/sns_topics_not_publicly_accessible.py
index 1dc0776b64..b6c66dc702 100644
--- a/prowler/providers/aws/services/sns/sns_topics_not_publicly_accessible/sns_topics_not_publicly_accessible.py
+++ b/prowler/providers/aws/services/sns/sns_topics_not_publicly_accessible/sns_topics_not_publicly_accessible.py
@@ -1,5 +1,7 @@
from prowler.lib.check.models import Check, Check_Report_AWS
from prowler.providers.aws.services.iam.lib.policy import (
+ has_public_principal,
+ has_restrictive_source_arn_condition,
is_condition_block_restrictive,
is_condition_block_restrictive_organization,
is_condition_block_restrictive_sns_endpoint,
@@ -16,46 +18,26 @@ class sns_topics_not_publicly_accessible(Check):
report.status_extended = (
f"SNS topic {topic.name} is not publicly accessible."
)
+
if topic.policy:
for statement in topic.policy["Statement"]:
- # Only check allow statements
- if statement["Effect"] == "Allow":
- if (
- "*" in statement["Principal"]
- or (
- "AWS" in statement["Principal"]
- and "*" in statement["Principal"]["AWS"]
+ if statement["Effect"] == "Allow" and has_public_principal(
+ statement
+ ):
+ if has_restrictive_source_arn_condition(statement):
+ break
+ elif "Condition" in statement:
+ condition_account = is_condition_block_restrictive(
+ statement["Condition"], sns_client.audited_account
)
- or (
- "CanonicalUser" in statement["Principal"]
- and "*" in statement["Principal"]["CanonicalUser"]
+ condition_org = is_condition_block_restrictive_organization(
+ statement["Condition"]
)
- ):
- condition_account = False
- condition_org = False
- condition_endpoint = False
- if (
- "Condition" in statement
- and is_condition_block_restrictive(
- statement["Condition"],
- sns_client.audited_account,
+ condition_endpoint = (
+ is_condition_block_restrictive_sns_endpoint(
+ statement["Condition"]
)
- ):
- condition_account = True
- if (
- "Condition" in statement
- and is_condition_block_restrictive_organization(
- statement["Condition"],
- )
- ):
- condition_org = True
- if (
- "Condition" in statement
- and is_condition_block_restrictive_sns_endpoint(
- statement["Condition"],
- )
- ):
- condition_endpoint = True
+ )
if condition_account and condition_org:
report.status_extended = f"SNS topic {topic.name} is not public because its policy only allows access from the account {sns_client.audited_account} and an organization."
@@ -69,7 +51,11 @@ class sns_topics_not_publicly_accessible(Check):
report.status = "FAIL"
report.status_extended = f"SNS topic {topic.name} is public because its policy allows public access."
break
+ else:
+ # Public principal with no conditions = public
+ report.status = "FAIL"
+ report.status_extended = f"SNS topic {topic.name} is public because its policy allows public access."
+ break
findings.append(report)
-
return findings
diff --git a/tests/providers/aws/services/ec2/ec2_securitygroup_allow_ingress_from_internet_to_all_ports/ec2_securitygroup_allow_ingress_from_internet_to_all_ports_test.py b/tests/providers/aws/services/ec2/ec2_securitygroup_allow_ingress_from_internet_to_all_ports/ec2_securitygroup_allow_ingress_from_internet_to_all_ports_test.py
index 98bfe0907e..f08d4cfc44 100644
--- a/tests/providers/aws/services/ec2/ec2_securitygroup_allow_ingress_from_internet_to_all_ports/ec2_securitygroup_allow_ingress_from_internet_to_all_ports_test.py
+++ b/tests/providers/aws/services/ec2/ec2_securitygroup_allow_ingress_from_internet_to_all_ports/ec2_securitygroup_allow_ingress_from_internet_to_all_ports_test.py
@@ -404,7 +404,7 @@ class Test_ec2_securitygroup_allow_ingress_from_internet_to_all_ports:
new=EC2(aws_provider),
),
mock.patch(
- "prowler.providers.aws.services.vpc.vpc_service.VPC",
+ "prowler.providers.aws.services.ec2.ec2_securitygroup_allow_ingress_from_internet_to_all_ports.ec2_securitygroup_allow_ingress_from_internet_to_all_ports.vpc_client",
new=VPC(aws_provider),
),
mock.patch(
diff --git a/tests/providers/aws/services/iam/lib/policy_test.py b/tests/providers/aws/services/iam/lib/policy_test.py
index f8022d6f77..4687d38dab 100644
--- a/tests/providers/aws/services/iam/lib/policy_test.py
+++ b/tests/providers/aws/services/iam/lib/policy_test.py
@@ -6,6 +6,8 @@ from prowler.providers.aws.services.iam.lib.policy import (
check_full_service_access,
get_effective_actions,
has_codebuild_trusted_principal,
+ has_public_principal,
+ has_restrictive_source_arn_condition,
is_codebuild_using_allowed_github_org,
is_condition_block_restrictive,
is_condition_block_restrictive_organization,
@@ -2451,3 +2453,266 @@ def test_has_codebuild_trusted_principal_list():
],
}
assert has_codebuild_trusted_principal(trust_policy) is True
+
+
+class Test_has_public_principal:
+ """Tests for the has_public_principal function"""
+
+ def test_has_public_principal_wildcard_string(self):
+ """Test public principal detection with wildcard string"""
+ statement = {"Principal": "*"}
+ assert has_public_principal(statement) is True
+
+ def test_has_public_principal_root_arn_string(self):
+ """Test public principal detection with root ARN string"""
+ statement = {"Principal": "arn:aws:iam::*:root"}
+ assert has_public_principal(statement) is True
+
+ def test_has_public_principal_aws_dict_wildcard(self):
+ """Test public principal detection with AWS dict containing wildcard"""
+ statement = {"Principal": {"AWS": "*"}}
+ assert has_public_principal(statement) is True
+
+ def test_has_public_principal_aws_dict_root_arn(self):
+ """Test public principal detection with AWS dict containing root ARN"""
+ statement = {"Principal": {"AWS": "arn:aws:iam::*:root"}}
+ assert has_public_principal(statement) is True
+
+ def test_has_public_principal_aws_list_wildcard(self):
+ """Test public principal detection with AWS list containing wildcard"""
+ statement = {"Principal": {"AWS": ["arn:aws:iam::123456789012:user/test", "*"]}}
+ assert has_public_principal(statement) is True
+
+ def test_has_public_principal_aws_list_root_arn(self):
+ """Test public principal detection with AWS list containing root ARN"""
+ statement = {
+ "Principal": {
+ "AWS": ["arn:aws:iam::123456789012:user/test", "arn:aws:iam::*:root"]
+ }
+ }
+ assert has_public_principal(statement) is True
+
+ def test_has_public_principal_canonical_user_wildcard(self):
+ """Test public principal detection with CanonicalUser wildcard"""
+ statement = {"Principal": {"CanonicalUser": "*"}}
+ assert has_public_principal(statement) is True
+
+ def test_has_public_principal_canonical_user_root_arn(self):
+ """Test public principal detection with CanonicalUser root ARN"""
+ statement = {"Principal": {"CanonicalUser": "arn:aws:iam::*:root"}}
+ assert has_public_principal(statement) is True
+
+ def test_has_public_principal_no_principal(self):
+ """Test with statement that has no Principal field"""
+ statement = {"Effect": "Allow", "Action": "s3:GetObject"}
+ assert has_public_principal(statement) is False
+
+ def test_has_public_principal_empty_principal(self):
+ """Test with empty principal"""
+ statement = {"Principal": ""}
+ assert has_public_principal(statement) is False
+
+ def test_has_public_principal_specific_account(self):
+ """Test with specific account principal (not public)"""
+ statement = {"Principal": {"AWS": "arn:aws:iam::123456789012:root"}}
+ assert has_public_principal(statement) is False
+
+ def test_has_public_principal_service_principal(self):
+ """Test with service principal (not public)"""
+ statement = {"Principal": {"Service": "lambda.amazonaws.com"}}
+ assert has_public_principal(statement) is False
+
+ def test_has_public_principal_mixed_principals(self):
+ """Test with mixed principals including public one"""
+ statement = {
+ "Principal": {
+ "AWS": ["arn:aws:iam::123456789012:user/test"],
+ "Service": "lambda.amazonaws.com",
+ "CanonicalUser": "*",
+ }
+ }
+ assert has_public_principal(statement) is True
+
+
+class Test_has_restrictive_source_arn_condition:
+ """Tests for the has_restrictive_source_arn_condition function"""
+
+ def test_no_condition_block(self):
+ """Test statement without Condition block"""
+ statement = {"Effect": "Allow", "Principal": "*", "Action": "s3:GetObject"}
+ assert has_restrictive_source_arn_condition(statement) is False
+
+ def test_no_source_arn_condition(self):
+ """Test with condition block but no aws:SourceArn"""
+ statement = {
+ "Effect": "Allow",
+ "Principal": "*",
+ "Action": "s3:GetObject",
+ "Condition": {"StringEquals": {"aws:SourceAccount": "123456789012"}},
+ }
+ assert has_restrictive_source_arn_condition(statement) is False
+
+ def test_restrictive_source_arn_s3_bucket(self):
+ """Test restrictive SourceArn condition with S3 bucket"""
+ statement = {
+ "Effect": "Allow",
+ "Principal": "*",
+ "Action": "sns:Publish",
+ "Condition": {"ArnLike": {"aws:SourceArn": "arn:aws:s3:::my-bucket"}},
+ }
+ assert has_restrictive_source_arn_condition(statement) is True
+
+ def test_restrictive_source_arn_lambda_function(self):
+ """Test restrictive SourceArn condition with Lambda function"""
+ statement = {
+ "Effect": "Allow",
+ "Principal": "*",
+ "Action": "sns:Publish",
+ "Condition": {
+ "ArnEquals": {
+ "aws:SourceArn": "arn:aws:lambda:us-east-1:123456789012:function:MyFunction"
+ }
+ },
+ }
+ assert has_restrictive_source_arn_condition(statement) is True
+
+ def test_non_restrictive_global_wildcard(self):
+ """Test non-restrictive SourceArn with global wildcard"""
+ statement = {
+ "Effect": "Allow",
+ "Principal": "*",
+ "Action": "sns:Publish",
+ "Condition": {"ArnLike": {"aws:SourceArn": "*"}},
+ }
+ assert has_restrictive_source_arn_condition(statement) is False
+
+ def test_non_restrictive_service_wildcard(self):
+ """Test non-restrictive SourceArn with service wildcard"""
+ statement = {
+ "Effect": "Allow",
+ "Principal": "*",
+ "Action": "sns:Publish",
+ "Condition": {"ArnLike": {"aws:SourceArn": "arn:aws:s3:::*"}},
+ }
+ assert has_restrictive_source_arn_condition(statement) is False
+
+ def test_non_restrictive_multi_wildcard(self):
+ """Test non-restrictive SourceArn with multiple wildcards"""
+ statement = {
+ "Effect": "Allow",
+ "Principal": "*",
+ "Action": "sns:Publish",
+ "Condition": {"ArnLike": {"aws:SourceArn": "arn:aws:*:*:*:*"}},
+ }
+ assert has_restrictive_source_arn_condition(statement) is False
+
+ def test_non_restrictive_resource_wildcard(self):
+ """Test non-restrictive SourceArn with resource wildcard"""
+ statement = {
+ "Effect": "Allow",
+ "Principal": "*",
+ "Action": "sns:Publish",
+ "Condition": {
+ "ArnLike": {"aws:SourceArn": "arn:aws:lambda:us-east-1:123456789012:*"}
+ },
+ }
+ assert has_restrictive_source_arn_condition(statement) is False
+
+ def test_source_arn_list_with_valid_arn(self):
+ """Test SourceArn condition with list containing valid ARN"""
+ statement = {
+ "Effect": "Allow",
+ "Principal": "*",
+ "Action": "sns:Publish",
+ "Condition": {
+ "ArnLike": {
+ "aws:SourceArn": ["arn:aws:s3:::bucket1", "arn:aws:s3:::bucket2"]
+ }
+ },
+ }
+ assert has_restrictive_source_arn_condition(statement) is True
+
+ def test_source_arn_list_with_wildcard(self):
+ """Test SourceArn condition with list containing wildcard"""
+ statement = {
+ "Effect": "Allow",
+ "Principal": "*",
+ "Action": "sns:Publish",
+ "Condition": {"ArnLike": {"aws:SourceArn": ["arn:aws:s3:::bucket1", "*"]}},
+ }
+ assert has_restrictive_source_arn_condition(statement) is False
+
+ def test_source_arn_with_account_validation_match(self):
+ """Test SourceArn with account validation - matching account"""
+ statement = {
+ "Effect": "Allow",
+ "Principal": "*",
+ "Action": "sns:Publish",
+ "Condition": {
+ "ArnLike": {
+ "aws:SourceArn": "arn:aws:lambda:us-east-1:123456789012:function:MyFunction"
+ }
+ },
+ }
+ assert has_restrictive_source_arn_condition(statement, "123456789012") is True
+
+ def test_source_arn_with_account_validation_mismatch(self):
+ """Test SourceArn with account validation - non-matching account"""
+ statement = {
+ "Effect": "Allow",
+ "Principal": "*",
+ "Action": "sns:Publish",
+ "Condition": {
+ "ArnLike": {
+ "aws:SourceArn": "arn:aws:lambda:us-east-1:123456789012:function:MyFunction"
+ }
+ },
+ }
+ assert has_restrictive_source_arn_condition(statement, "987654321098") is False
+
+ def test_source_arn_with_account_wildcard(self):
+ """Test SourceArn with account wildcard"""
+ statement = {
+ "Effect": "Allow",
+ "Principal": "*",
+ "Action": "sns:Publish",
+ "Condition": {
+ "ArnLike": {
+ "aws:SourceArn": "arn:aws:lambda:us-east-1:*:function:MyFunction"
+ }
+ },
+ }
+ assert has_restrictive_source_arn_condition(statement, "123456789012") is False
+
+ def test_source_arn_s3_bucket_no_account_field(self):
+ """Test SourceArn with S3 bucket (no account field) - should be restrictive"""
+ statement = {
+ "Effect": "Allow",
+ "Principal": "*",
+ "Action": "sns:Publish",
+ "Condition": {"ArnLike": {"aws:SourceArn": "arn:aws:s3:::my-bucket"}},
+ }
+ assert has_restrictive_source_arn_condition(statement, "123456789012") is True
+
+ def test_source_arn_case_insensitive(self):
+ """Test SourceArn condition key is case insensitive"""
+ statement = {
+ "Effect": "Allow",
+ "Principal": "*",
+ "Action": "sns:Publish",
+ "Condition": {"ArnLike": {"AWS:SourceArn": "arn:aws:s3:::my-bucket"}},
+ }
+ assert has_restrictive_source_arn_condition(statement) is True
+
+ def test_source_arn_mixed_operators(self):
+ """Test SourceArn with multiple condition operators"""
+ statement = {
+ "Effect": "Allow",
+ "Principal": "*",
+ "Action": "sns:Publish",
+ "Condition": {
+ "ArnLike": {"aws:SourceArn": "arn:aws:s3:::my-bucket"},
+ "StringEquals": {"aws:SourceAccount": "123456789012"},
+ },
+ }
+ assert has_restrictive_source_arn_condition(statement) is True
diff --git a/tests/providers/aws/services/sns/sns_topics_not_publicly_accessible/sns_topics_not_publicly_accessible_test.py b/tests/providers/aws/services/sns/sns_topics_not_publicly_accessible/sns_topics_not_publicly_accessible_test.py
index 71aa6c4477..a868be5715 100644
--- a/tests/providers/aws/services/sns/sns_topics_not_publicly_accessible/sns_topics_not_publicly_accessible_test.py
+++ b/tests/providers/aws/services/sns/sns_topics_not_publicly_accessible/sns_topics_not_publicly_accessible_test.py
@@ -2,9 +2,10 @@ from typing import Any, Dict
from unittest import mock
from uuid import uuid4
+import pytest
+
from prowler.providers.aws.services.sns.sns_service import Topic
from tests.providers.aws.utils import AWS_ACCOUNT_NUMBER, AWS_REGION_EU_WEST_1
-import pytest
kms_key_id = str(uuid4())
topic_name = "test-topic"
@@ -98,6 +99,73 @@ test_policy_restricted_principal_account_organization = {
]
}
+test_policy_restricted_source_arn = {
+ "Version": "2012-10-17",
+ "Statement": [
+ {
+ "Effect": "Allow",
+ "Principal": {"AWS": "*"},
+ "Action": "SNS:Publish",
+ "Resource": f"arn:aws:sns:{AWS_REGION_EU_WEST_1}:{AWS_ACCOUNT_NUMBER}:{topic_name}",
+ "Condition": {
+ "ArnLike": {"aws:SourceArn": "arn:aws:s3:::test-bucket-name"}
+ },
+ }
+ ],
+}
+
+test_policy_invalid_source_arn = {
+ "Version": "2012-10-17",
+ "Statement": [
+ {
+ "Effect": "Allow",
+ "Principal": {"AWS": "*"},
+ "Action": "SNS:Publish",
+ "Resource": f"arn:aws:sns:{AWS_REGION_EU_WEST_1}:{AWS_ACCOUNT_NUMBER}:{topic_name}",
+ "Condition": {"ArnLike": {"aws:SourceArn": "invalid-arn-format"}},
+ }
+ ],
+}
+
+test_policy_unrestricted_source_arn_wildcard = {
+ "Version": "2012-10-17",
+ "Statement": [
+ {
+ "Effect": "Allow",
+ "Principal": {"AWS": "*"},
+ "Action": "SNS:Publish",
+ "Resource": f"arn:aws:sns:{AWS_REGION_EU_WEST_1}:{AWS_ACCOUNT_NUMBER}:{topic_name}",
+ "Condition": {"ArnLike": {"aws:SourceArn": "*"}},
+ }
+ ],
+}
+
+test_policy_unrestricted_source_arn_service_wildcard = {
+ "Version": "2012-10-17",
+ "Statement": [
+ {
+ "Effect": "Allow",
+ "Principal": {"AWS": "*"},
+ "Action": "SNS:Publish",
+ "Resource": f"arn:aws:sns:{AWS_REGION_EU_WEST_1}:{AWS_ACCOUNT_NUMBER}:{topic_name}",
+ "Condition": {"ArnLike": {"aws:SourceArn": "arn:aws:s3:::*"}},
+ }
+ ],
+}
+
+test_policy_unrestricted_source_arn_multi_wildcard = {
+ "Version": "2012-10-17",
+ "Statement": [
+ {
+ "Effect": "Allow",
+ "Principal": {"AWS": "*"},
+ "Action": "SNS:Publish",
+ "Resource": f"arn:aws:sns:{AWS_REGION_EU_WEST_1}:{AWS_ACCOUNT_NUMBER}:{topic_name}",
+ "Condition": {"ArnLike": {"aws:SourceArn": "arn:aws:*:*:*:*"}},
+ }
+ ],
+}
+
def generate_policy_restricted_on_sns_endpoint(endpoint: str) -> Dict[str, Any]:
return {
@@ -396,6 +464,78 @@ class Test_sns_topics_not_publicly_accessible:
assert result[0].region == AWS_REGION_EU_WEST_1
assert result[0].resource_tags == []
+ def test_topic_public_with_source_arn_restriction(self):
+ sns_client = mock.MagicMock
+ sns_client.audited_account = AWS_ACCOUNT_NUMBER
+ sns_client.topics = []
+ sns_client.topics.append(
+ Topic(
+ arn=topic_arn,
+ name=topic_name,
+ policy=test_policy_restricted_source_arn,
+ region=AWS_REGION_EU_WEST_1,
+ )
+ )
+ sns_client.provider = mock.MagicMock()
+ sns_client.provider.organizations_metadata = mock.MagicMock()
+ sns_client.provider.organizations_metadata.organization_id = org_id
+ with mock.patch(
+ "prowler.providers.aws.services.sns.sns_service.SNS",
+ sns_client,
+ ):
+ from prowler.providers.aws.services.sns.sns_topics_not_publicly_accessible.sns_topics_not_publicly_accessible import (
+ sns_topics_not_publicly_accessible,
+ )
+
+ check = sns_topics_not_publicly_accessible()
+ result = check.execute()
+ assert len(result) == 1
+ assert result[0].status == "PASS"
+ assert (
+ result[0].status_extended
+ == f"SNS topic {topic_name} is not publicly accessible."
+ )
+ assert result[0].resource_id == topic_name
+ assert result[0].resource_arn == topic_arn
+ assert result[0].region == AWS_REGION_EU_WEST_1
+ assert result[0].resource_tags == []
+
+ def test_topic_public_with_invalid_source_arn(self):
+ sns_client = mock.MagicMock
+ sns_client.audited_account = AWS_ACCOUNT_NUMBER
+ sns_client.topics = []
+ sns_client.topics.append(
+ Topic(
+ arn=topic_arn,
+ name=topic_name,
+ policy=test_policy_invalid_source_arn,
+ region=AWS_REGION_EU_WEST_1,
+ )
+ )
+ sns_client.provider = mock.MagicMock()
+ sns_client.provider.organizations_metadata = mock.MagicMock()
+ sns_client.provider.organizations_metadata.organization_id = org_id
+ with mock.patch(
+ "prowler.providers.aws.services.sns.sns_service.SNS",
+ sns_client,
+ ):
+ from prowler.providers.aws.services.sns.sns_topics_not_publicly_accessible.sns_topics_not_publicly_accessible import (
+ sns_topics_not_publicly_accessible,
+ )
+
+ check = sns_topics_not_publicly_accessible()
+ result = check.execute()
+ assert len(result) == 1
+ assert result[0].status == "PASS"
+ assert (
+ result[0].status_extended
+ == f"SNS topic {topic_name} is not publicly accessible."
+ )
+ assert result[0].resource_id == topic_name
+ assert result[0].resource_arn == topic_arn
+ assert result[0].region == AWS_REGION_EU_WEST_1
+ assert result[0].resource_tags == []
+
@pytest.mark.parametrize(
"endpoint",
[
@@ -443,6 +583,114 @@ class Test_sns_topics_not_publicly_accessible:
assert result[0].region == AWS_REGION_EU_WEST_1
assert result[0].resource_tags == []
+ def test_topic_public_with_unrestricted_source_arn_wildcard(self):
+ sns_client = mock.MagicMock
+ sns_client.audited_account = AWS_ACCOUNT_NUMBER
+ sns_client.topics = []
+ sns_client.topics.append(
+ Topic(
+ arn=topic_arn,
+ name=topic_name,
+ policy=test_policy_unrestricted_source_arn_wildcard,
+ region=AWS_REGION_EU_WEST_1,
+ )
+ )
+ sns_client.provider = mock.MagicMock()
+ sns_client.provider.organizations_metadata = mock.MagicMock()
+ sns_client.provider.organizations_metadata.organization_id = org_id
+ with mock.patch(
+ "prowler.providers.aws.services.sns.sns_service.SNS",
+ sns_client,
+ ):
+ from prowler.providers.aws.services.sns.sns_topics_not_publicly_accessible.sns_topics_not_publicly_accessible import (
+ sns_topics_not_publicly_accessible,
+ )
+
+ check = sns_topics_not_publicly_accessible()
+ result = check.execute()
+ assert len(result) == 1
+ assert result[0].status == "FAIL"
+ assert (
+ result[0].status_extended
+ == f"SNS topic {topic_name} is public because its policy allows public access."
+ )
+ assert result[0].resource_id == topic_name
+ assert result[0].resource_arn == topic_arn
+ assert result[0].region == AWS_REGION_EU_WEST_1
+ assert result[0].resource_tags == []
+
+ def test_topic_public_with_unrestricted_source_arn_service_wildcard(self):
+ sns_client = mock.MagicMock
+ sns_client.audited_account = AWS_ACCOUNT_NUMBER
+ sns_client.topics = []
+ sns_client.topics.append(
+ Topic(
+ arn=topic_arn,
+ name=topic_name,
+ policy=test_policy_unrestricted_source_arn_service_wildcard,
+ region=AWS_REGION_EU_WEST_1,
+ )
+ )
+ sns_client.provider = mock.MagicMock()
+ sns_client.provider.organizations_metadata = mock.MagicMock()
+ sns_client.provider.organizations_metadata.organization_id = org_id
+ with mock.patch(
+ "prowler.providers.aws.services.sns.sns_service.SNS",
+ sns_client,
+ ):
+ from prowler.providers.aws.services.sns.sns_topics_not_publicly_accessible.sns_topics_not_publicly_accessible import (
+ sns_topics_not_publicly_accessible,
+ )
+
+ check = sns_topics_not_publicly_accessible()
+ result = check.execute()
+ assert len(result) == 1
+ assert result[0].status == "FAIL"
+ assert (
+ result[0].status_extended
+ == f"SNS topic {topic_name} is public because its policy allows public access."
+ )
+ assert result[0].resource_id == topic_name
+ assert result[0].resource_arn == topic_arn
+ assert result[0].region == AWS_REGION_EU_WEST_1
+ assert result[0].resource_tags == []
+
+ def test_topic_public_with_unrestricted_source_arn_multi_wildcard(self):
+ sns_client = mock.MagicMock
+ sns_client.audited_account = AWS_ACCOUNT_NUMBER
+ sns_client.topics = []
+ sns_client.topics.append(
+ Topic(
+ arn=topic_arn,
+ name=topic_name,
+ policy=test_policy_unrestricted_source_arn_multi_wildcard,
+ region=AWS_REGION_EU_WEST_1,
+ )
+ )
+ sns_client.provider = mock.MagicMock()
+ sns_client.provider.organizations_metadata = mock.MagicMock()
+ sns_client.provider.organizations_metadata.organization_id = org_id
+ with mock.patch(
+ "prowler.providers.aws.services.sns.sns_service.SNS",
+ sns_client,
+ ):
+ from prowler.providers.aws.services.sns.sns_topics_not_publicly_accessible.sns_topics_not_publicly_accessible import (
+ sns_topics_not_publicly_accessible,
+ )
+
+ check = sns_topics_not_publicly_accessible()
+ result = check.execute()
+ assert len(result) == 1
+ assert result[0].status == "FAIL"
+ assert (
+ result[0].status_extended
+ == f"SNS topic {topic_name} is public because its policy allows public access."
+ )
+ assert result[0].resource_id == topic_name
+ assert result[0].resource_arn == topic_arn
+ assert result[0].region == AWS_REGION_EU_WEST_1
+ assert result[0].resource_tags == []
+
@pytest.mark.parametrize(
"endpoint",
[
From b99dce6a43f0e546468431aea054c40a232f7977 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pedro=20Mart=C3=ADn?=
Date: Thu, 24 Jul 2025 16:29:46 +0200
Subject: [PATCH 22/28] feat(azure): add CIS 4.0 (#7782)
---
README.md | 2 +-
dashboard/compliance/cis_4_0_azure.py | 25 +
prowler/CHANGELOG.md | 1 +
prowler/compliance/azure/cis_4.0_azure.json | 3087 +++++++++++++++++++
4 files changed, 3114 insertions(+), 1 deletion(-)
create mode 100644 dashboard/compliance/cis_4_0_azure.py
create mode 100644 prowler/compliance/azure/cis_4.0_azure.json
diff --git a/README.md b/README.md
index ea249d473d..cdf6fc29f4 100644
--- a/README.md
+++ b/README.md
@@ -88,7 +88,7 @@ prowler dashboard
|---|---|---|---|---|
| AWS | 567 | 82 | 36 | 10 |
| GCP | 79 | 13 | 10 | 3 |
-| Azure | 142 | 18 | 10 | 3 |
+| Azure | 142 | 18 | 11 | 3 |
| Kubernetes | 83 | 7 | 5 | 7 |
| GitHub | 16 | 2 | 1 | 0 |
| M365 | 69 | 7 | 3 | 2 |
diff --git a/dashboard/compliance/cis_4_0_azure.py b/dashboard/compliance/cis_4_0_azure.py
new file mode 100644
index 0000000000..9d33cc67a8
--- /dev/null
+++ b/dashboard/compliance/cis_4_0_azure.py
@@ -0,0 +1,25 @@
+import warnings
+
+from dashboard.common_methods import get_section_containers_cis
+
+warnings.filterwarnings("ignore")
+
+
+def get_table(data):
+
+ aux = data[
+ [
+ "REQUIREMENTS_ID",
+ "REQUIREMENTS_DESCRIPTION",
+ "REQUIREMENTS_ATTRIBUTES_SECTION",
+ "CHECKID",
+ "STATUS",
+ "REGION",
+ "ACCOUNTID",
+ "RESOURCEID",
+ ]
+ ].copy()
+
+ return get_section_containers_cis(
+ aux, "REQUIREMENTS_ID", "REQUIREMENTS_ATTRIBUTES_SECTION"
+ )
diff --git a/prowler/CHANGELOG.md b/prowler/CHANGELOG.md
index 2bb9dac6e3..12be1dda1c 100644
--- a/prowler/CHANGELOG.md
+++ b/prowler/CHANGELOG.md
@@ -7,6 +7,7 @@ All notable changes to the **Prowler SDK** are documented in this file.
### Added
- `bedrock_api_key_no_administrative_privileges` check for AWS provider [(#8321)](https://github.com/prowler-cloud/prowler/pull/8321)
- Support App Key Content in GitHub provider [(#8271)](https://github.com/prowler-cloud/prowler/pull/8271)
+- CIS 4.0 for the Azure provider [(#7782)](https://github.com/prowler-cloud/prowler/pull/7782)
### Changed
- Handle some AWS errors as warnings instead of errors [(#8347)](https://github.com/prowler-cloud/prowler/pull/8347)
diff --git a/prowler/compliance/azure/cis_4.0_azure.json b/prowler/compliance/azure/cis_4.0_azure.json
new file mode 100644
index 0000000000..ec6fae18eb
--- /dev/null
+++ b/prowler/compliance/azure/cis_4.0_azure.json
@@ -0,0 +1,3087 @@
+{
+ "Framework": "CIS",
+ "Version": "4.0",
+ "Provider": "Azure",
+ "Description": "The CIS Azure Foundations Benchmark provides prescriptive guidance for configuring security options for a subset of Azure with an emphasis on foundational, testable, and architecture agnostic settings.",
+ "Requirements": [
+ {
+ "Id": "2.2.1.1",
+ "Description": "Ensure public network access is Disabled",
+ "Checks": [],
+ "Attributes": [
+ {
+ "Section": "2 Common Reference Recommendations",
+ "SubSection": "2.2 Networking",
+ "Profile": "Level 1",
+ "AssessmentStatus": "Automated",
+ "Description": "Disable public network access to prevent exposure to the internet and reduce the risk of unauthorized access. Use private endpoints to securely manage access within trusted networks.",
+ "RationaleStatement": "Disabling public network access improves security by ensuring that a service is not exposed on the public internet.",
+ "ImpactStatement": "Disabling public network access restricts access to the service. This enhances security but may require the configuration of private endpoints for any services or users needing access within trusted networks.",
+ "RemediationProcedure": "",
+ "AuditProcedure": "",
+ "AdditionalInformation": "This Common Reference Recommendation is referenced in the following Service Recommendations: - Storage Services > Storage Accounts > Networking > **Ensure that 'Public Network Access' is 'Disabled' for storage accounts**",
+ "References": "",
+ "DefaultValue": ""
+ }
+ ]
+ },
+ {
+ "Id": "2.1.1.2.1",
+ "Description": "Ensure Critical Data is Encrypted with Customer Managed Keys (CMK)",
+ "Checks": [
+ "storage_ensure_encryption_with_customer_managed_keys"
+ ],
+ "Attributes": [
+ {
+ "Section": "2 Common Reference Recommendations",
+ "SubSection": "2.1 Secrets and Keys",
+ "Profile": "Level 2",
+ "AssessmentStatus": "Manual",
+ "Description": "Customer Managed Keys introduce additional depth to security by providing a means to manage access control for encryption keys. Where compliance and security frameworks indicate the need, and organizational capacity allows, sensitive data at rest can be encrypted using Customer Managed Keys (CMK) rather than Microsoft Managed keys.",
+ "RationaleStatement": "By default in Azure, data at rest tends to be encrypted using Microsoft Managed Keys. If your organization wants to control and manage encryption keys for compliance and defense-in-depth, Customer Managed Keys can be established. While it is possible to automate the assessment of this recommendation, the assessment status for this recommendation remains 'Manual' due to ideally limited scope. The scope of application - which workloads CMK is applied to - should be carefully considered to account for organizational capacity and targeted to workloads with specific need for CMK.",
+ "ImpactStatement": "If the key expires due to setting the 'activation date' and 'expiration date', the key must be rotated manually. Using Customer Managed Keys may also incur additional man-hour requirements to create, store, manage, and protect the keys as needed.",
+ "RemediationProcedure": "",
+ "AuditProcedure": "",
+ "AdditionalInformation": "",
+ "References": "https://docs.microsoft.com/en-us/azure/security/fundamentals/data-encryption-best-practices#protect-data-at-rest:https://learn.microsoft.com/en-us/security/benchmark/azure/mcsb-data-protection#dp-5-use-customer-managed-key-option-in-data-at-rest-encryption-when-required",
+ "DefaultValue": "By default, Encryption type is set to Microsoft Managed Keys."
+ }
+ ]
+ },
+ {
+ "Id": "3.1.1",
+ "Description": "Ensure that Azure Databricks is deployed in a customer-managed virtual network (VNet)",
+ "Checks": [
+ "databricks_workspace_vnet_injection_enabled"
+ ],
+ "Attributes": [
+ {
+ "Section": "3 Analytics Services",
+ "SubSection": "3.1 Azure Databricks",
+ "Profile": "Level 1",
+ "AssessmentStatus": "Automated",
+ "Description": "Networking for Azure Databricks can be set up in a few different ways. Using a customer-managed Virtual Network (VNet) (also known as VNet Injection) ensures that compute clusters and control planes are securely isolated within the organization’s network boundary. By default, Databricks creates a managed VNet, which provides limited control over network security policies, firewall configurations, and routing.",
+ "RationaleStatement": "Using a customer-managed VNet ensures better control over network security and aligns with zero-trust architecture principles. It allows for: - Restricted outbound internet access to prevent unauthorized data exfiltration. - Integration with on-premises networks via VPN or ExpressRoute for hybrid connectivity. - Fine-grained NSG policies to restrict access at the subnet level. - Private Link for secure API access, avoiding public internet exposure.",
+ "ImpactStatement": "- Requires additional configuration during Databricks workspace deployment. - Might increase operational overhead for network maintenance. - May impact connectivity if misconfigured (e.g., restrictive NSG rules or missing routes).",
+ "RemediationProcedure": "**Remediate from Azure Portal** 1. Delete the existing Databricks workspace (migration required). 1. Create a new Databricks workspace with VNet Injection: 1. Go to Azure Portal → Create Databricks Workspace. 1. Select Advanced Networking. 1. Choose Deploy into your own Virtual Network. 1. Specify a customer-managed VNet and associated subnets. 1. Enable Private Link for secure API access. **Remediate from Azure CLI** Deploy a new Databricks workspace in a custom VNet: ``` az databricks workspace create --name \\ --resource-group \\ --location \\ --managed-resource-group \\ --enable-no-public-ip true \\ --network-security-group-rule NoAzureServices \\ --public-network-access Disabled \\ --custom-virtual-network-id /subscriptions//resourceGroups//providers/Microsoft.Network/virtualNetworks/ ``` Ensure NSG Rules are correctly configured: ``` az network nsg rule create --resource-group \\ --nsg-name \\ --name DenyAllOutbound \\ --direction Outbound \\ --access Deny \\ --priority 4096 ``` **Remediate from PowerShell** ``` New-AzDatabricksWorkspace -ResourceGroupName -Name -Location -ManagedResourceGroupName -CustomVirtualNetworkId /subscriptions//resourceGroups//providers/Microsoft.Network/virtualNetworks/ ```",
+ "AuditProcedure": "**Audit from Azure Portal** 1. Go to Azure Portal → Search for Databricks Workspaces. 1. Select the Databricks Workspace to audit. 1. Under Networking, check if the workspace is deployed in a Customer-Managed VNet. 1. If the Virtual Network field shows Databricks-Managed VNet, it is non-compliant. 1. Verify NSG rules and Private Endpoints for fine-grained access control. **Audit from Azure CLI** Run the following command to check if Databricks is using a customer-managed VNet: ``` az network vnet show --resource-group --name ``` Ensure that Databricks subnets are present in the VNet configuration. Validate NSG rules attached to the Databricks subnets. **Audit from PowerShell** ``` Get-AzDatabricksWorkspace -ResourceGroupName -Name | Select-Object VirtualNetworkId ``` If VirtualNetworkId is null or shows a Databricks-Managed VNet, it is non-compliant. **Audit from Azure Policy** If referencing a digital copy of this Benchmark, clicking a Policy ID will open a link to the associated Policy definition in Azure. If referencing a printed copy, you can search Policy IDs from this URL: https://portal.azure.com/#view/Microsoft_Azure_Policy/PolicyMenuBlade/~/Definitions - **Policy ID:** [9c25c9e4-ee12-4882-afd2-11fb9d87893f](https://portal.azure.com/#view/Microsoft_Azure_Policy/PolicyDetailBlade/definitionId/%2Fproviders%2FMicrosoft.Authorization%2FpolicyDefinitions%9c25c9e4-ee12-4882-afd2-11fb9d87893f) **- Name:** 'Azure Databricks Workspaces should be in a virtual network'",
+ "AdditionalInformation": "",
+ "References": "",
+ "DefaultValue": "By default, Azure Databricks uses a Databricks-Managed VNet."
+ }
+ ]
+ },
+ {
+ "Id": "2.2.1.2",
+ "Description": "Ensure Network Access Rules are set to Deny-by-default",
+ "Checks": [
+ "storage_default_network_access_rule_is_denied"
+ ],
+ "Attributes": [
+ {
+ "Section": "2 Common Reference Recommendations",
+ "SubSection": "2.2 Networking",
+ "Profile": "Level 1",
+ "AssessmentStatus": "Automated",
+ "Description": "Restricting default network access provides a foundational level of security to networked resources. To limit access to selected networks, the default action must be changed.",
+ "RationaleStatement": "Resources using Virtual Network interfaces should be configured to deny-by-default all access from all networks (including internet traffic). Access can be granted to traffic from specific Azure Virtual networks, allowing a secure network boundary for specific applications to be built. If necessary, access can also be granted to public internet IP address ranges to enable connections from specific internet or on-premises clients. For all traffic inbound from- and outbound to- the internet, a NAT Gateway is recommended at minimum, and ideally all traffic flows through a security gateway device such as a firewall. Security gateway devices will provide an additional level of visibility to inbound and outbound traffic and usually perform advanced monitoring and response activity such as intrusion detection and prevention (IDP), and deep packet inspection (DPI) which help detect activity indicating vulnerabilities and threats.",
+ "ImpactStatement": "All allowed networks and protocols will need to be allow-listed which creates some administrative overhead. Implementing a deny-by-default rule may result in a loss of network connectivity. Careful planning and a scheduled implementation window allowing for downtime is highly recommended.",
+ "RemediationProcedure": "",
+ "AuditProcedure": "",
+ "AdditionalInformation": "This Common Reference Recommendation is referenced in the following Service Recommendations: - Storage Services > Storage Accounts > Networking > **Ensure default network access rule for storage accounts is set to deny**",
+ "References": "https://docs.microsoft.com/en-us/azure/storage/common/storage-network-security:https://learn.microsoft.com/en-us/security/benchmark/azure/mcsb-governance-strategy#gs-2-define-and-implement-enterprise-segmentationseparation-of-duties-strategy:https://learn.microsoft.com/en-us/security/benchmark/azure/mcsb-network-security#ns-2-secure-cloud-native-services-with-network-controls",
+ "DefaultValue": "By default, interfaces attached to virtual networks will accept connections from clients on any network and have a default outbound access rule which allows access to the internet. The default outbound access rule is scheduled for retirement on September 30th, 2025: https://azure.microsoft.com/en-us/updates?id=default-outbound-access-for-vms-in-azure-will-be-retired-transition-to-a-new-method-of-internet-access"
+ }
+ ]
+ },
+ {
+ "Id": "2.2.2.1",
+ "Description": "Ensure Private Endpoints are used to access {service}",
+ "Checks": [
+ "storage_ensure_private_endpoints_in_storage_accounts"
+ ],
+ "Attributes": [
+ {
+ "Section": "2 Common Reference Recommendations",
+ "SubSection": "2.2 Networking",
+ "Profile": "Level 2",
+ "AssessmentStatus": "Automated",
+ "Description": "Use private endpoints to allow clients and services to securely access data located over a network via an encrypted Private Link. To do this, the private endpoint uses an IP address from the VNet for each service. Network traffic between disparate services securely traverses encrypted over the VNet. This VNet can also link addressing space, extending your network and accessing resources on it. Similarly, it can be a tunnel through public networks to connect remote infrastructures together. This creates further security through segmenting network traffic and preventing outside sources from accessing it.",
+ "RationaleStatement": "Securing traffic between services through encryption protects the data from easy interception and reading.",
+ "ImpactStatement": "If an Azure Virtual Network is not implemented correctly, this may result in the loss of critical network traffic. Private endpoints are charged per hour of use. Refer to https://azure.microsoft.com/en-us/pricing/details/private-link/ and https://azure.microsoft.com/en-us/pricing/calculator/ to estimate potential costs.",
+ "RemediationProcedure": "",
+ "AuditProcedure": "",
+ "AdditionalInformation": "A NAT gateway is the recommended solution for outbound internet access. This Common Reference Recommendation is referenced in the following Service Recommendations: - Storage Services > Storage Accounts > Networking > **Ensure Private Endpoints are used to access Storage Accounts**",
+ "References": "https://docs.microsoft.com/en-us/azure/virtual-network/virtual-networks-overview:https://docs.microsoft.com/en-us/azure/private-link/create-private-endpoint-portal:https://docs.microsoft.com/en-us/azure/private-link/create-private-endpoint-cli?tabs=dynamic-ip:https://docs.microsoft.com/en-us/azure/private-link/create-private-endpoint-powershell?tabs=dynamic-ip:https://learn.microsoft.com/en-us/security/benchmark/azure/mcsb-network-security#ns-2-secure-cloud-native-services-with-network-controls",
+ "DefaultValue": "By default, Private Endpoints are not created for services."
+ }
+ ]
+ },
+ {
+ "Id": "3.1.8",
+ "Description": "Ensure that data at rest and in transit is encrypted in Azure Databricks using customer managed keys (CMK)",
+ "Checks": [
+ "databricks_workspace_cmk_encryption_enabled"
+ ],
+ "Attributes": [
+ {
+ "Section": "3 Analytics Services",
+ "SubSection": "3.1 Azure Databricks",
+ "Profile": "Level 2",
+ "AssessmentStatus": "Automated",
+ "Description": "Azure Databricks encrypts data in transit using TLS 1.2+ to secure API, workspace, and cluster communications. By default, data at rest is encrypted using Microsoft-managed keys.",
+ "RationaleStatement": "Organizations with stricter needs for control of encryption keys should enable customer-managed keys (CMK) for greater control over data encryption, auditing, and regulatory compliance. Azure Key Vault should be used to store and manage CMKs. Enforcing encryption at rest and in transit in Azure Databricks: - Protects sensitive data from unauthorized access. - Ensures regulatory compliance (ISO 27001, GDPR, HIPAA, SOC 2). - Allows key revocation and rotation control with customer-managed keys (CMK). - Mitigates insider threats by preventing unauthorized access to raw storage.",
+ "ImpactStatement": "Enabling CMK encryption requires additional configuration. Key management introduces maintenance overhead (rotation, revocation, lifecycle management). Potential access issues will be encountered if keys are deleted or rotated incorrectly.",
+ "RemediationProcedure": "**NOTE:** These remediations assume that an Azure KeyVault already exists in the subscription. **Remediate from Azure CLI** 1. Create a dedicated key: ``` az keyvault key create --vault-name --name --protection ``` 2. Assign permissions to Databricks: ``` az keyvault set-policy --name --resource-group --spn --key-permissions get wrapKey unwrapKey ``` 3. Enable encryption with CMK: ``` az databricks workspace update --name --resource-group --key-source Microsoft.KeyVault --key-name --keyvault-uri ``` **Remediate from PowerShell** ``` $Key = Add-AzKeyVaultKey -VaultName -Name -Destination Set-AzDatabricksWorkspace -ResourceGroupName -WorkspaceName -EncryptionKeySource Microsoft.KeyVault -KeyVaultUri $Key.Id ```",
+ "AuditProcedure": "**Audit from Azure Portal** 1. Go to Azure Portal → Databricks Workspaces. 1. Select a Databricks Workspace and go to Encryption settings. 1. Check if customer-managed keys (CMK) are enabled under Managed Disk Encryption. 1. If CMK is not enabled, the workspace is non-compliant. **Audit from Azure CLI** Run the following command to check encryption settings for Databricks workspace: ``` az databricks workspace show --name --resource-group --query encryption ``` Ensure that keySource is set to `Microsoft.KeyVault`. **Audit from PowerShell** ``` Get-AzDatabricksWorkspace -ResourceGroupName -Name | Select-Object Encryption ``` Verify that encryption is set to `Customer-Managed Keys (CMK)`. **Audit from Databricks CLI** ``` databricks workspace get-metadata --workspace-id ``` Ensure that encryption settings reflect a CMK setup.",
+ "AdditionalInformation": "",
+ "References": "",
+ "DefaultValue": "By default, Azure Databricks uses Microsoft-managed keys for encryption. Data in transit is always encrypted using TLS 1.2+. Customer-Managed Keys (CMK) must be manually enabled."
+ }
+ ]
+ },
+ {
+ "Id": "6.3.3",
+ "Description": "Ensure that use of the 'User Access Administrator' role is restricted",
+ "Checks": [
+ "iam_role_user_access_admin_restricted"
+ ],
+ "Attributes": [
+ {
+ "Section": "6 Identity Services",
+ "SubSection": "6.3 Periodic Identity Reviews",
+ "Profile": "Level 1",
+ "AssessmentStatus": "Automated",
+ "Description": "The User Access Administrator role grants the ability to view all resources and manage access assignments at any subscription or management group level within the tenant. Due to its high privilege level, this role assignment should be removed immediately after completing the necessary changes at the root scope to minimize security risks.",
+ "RationaleStatement": "The User Access Administrator role provides extensive access control privileges. Unnecessary assignments heighten the risk of privilege escalation and unauthorized access. Removing the role immediately after use minimizes security exposure.",
+ "ImpactStatement": "Increased administrative effort to manage and remove role assignments appropriately.",
+ "RemediationProcedure": "**Remediate from Azure Portal** 1. From Azure Home select the Portal Menu. 1. Select `Subscriptions`. 1. Select a subscription. 1. Select `Access control (IAM)`. 1. Look for the following banner at the top of the page: `Action required: X users have elevated access in your tenant. You should take immediate action and remove all role assignments with elevated access.` 1. Click `View role assignments`. 1. Click `Remove`. **Remediate from Azure CLI** Run the following command: ``` az role assignment delete --role User Access Administrator --scope / ```",
+ "AuditProcedure": "**Audit from Azure Portal** 1. From Azure Home select the Portal Menu. 1. Select `Subscriptions`. 1. Select a subscription. 1. Select `Access control (IAM)`. 1. Look for the following banner at the top of the page: `Action required: X users have elevated access in your tenant. You should take immediate action and remove all role assignments with elevated access.` If the banner is displayed, the `User Access Administrator` is assigned. **Audit from Azure CLI** Run the following command: ``` az role assignment list --role User Access Administrator --scope / ``` Ensure that the command does not return any `User Access Administrator` role assignment information.",
+ "AdditionalInformation": "",
+ "References": "https://learn.microsoft.com/en-us/azure/role-based-access-control/built-in-roles:https://learn.microsoft.com/en-us/azure/role-based-access-control/elevate-access-global-admin",
+ "DefaultValue": ""
+ }
+ ]
+ },
+ {
+ "Id": "7.1.2.11",
+ "Description": "Ensure that an Activity Log Alert exists for Service Health",
+ "Checks": [
+ "monitor_alert_service_health_exists"
+ ],
+ "Attributes": [
+ {
+ "Section": "7 Management and Governance Services",
+ "SubSection": "7.1 Logging and Monitoring",
+ "Profile": "Level 1",
+ "AssessmentStatus": "Automated",
+ "Description": "Create an activity log alert for Service Health.",
+ "RationaleStatement": "Monitoring for Service Health events provides insight into service issues, planned maintenance, security advisories, and other changes that may affect the Azure services and regions in use.",
+ "ImpactStatement": "There is no charge for creating activity log alert rules.",
+ "RemediationProcedure": "**Remediate from Azure Portal** 1. Go to `Monitor`. 1. Click `Alerts`. 1. Click `+ Create`. 1. Select `Alert rule` from the drop-down menu. 1. Choose a subscription. 1. Click `Apply`. 1. Select the `Condition` tab. 1. Click `See all signals`. 1. Select `Service health`. 1. Click `Apply`. 1. Open the drop-down menu next to `Event types`. 1. Check the box next to `Select all`. 1. Select the `Actions` tab. 1. Click `Select action groups` to select an existing action group, or `Create action group` to create a new action group. 1. Follow the prompts to choose or create an action group. 1. Select the `Details` tab. 1. Select a `Resource group`, provide an `Alert rule name` and an optional `Alert rule description`. 1. Click `Review + create`. 1. Click `Create`. 1. Repeat steps 1-19 for each subscription requiring remediation. **Remediate from Azure CLI** For each subscription requiring remediation, run the following command to create a `ServiceHealth` alert rule for a subscription: ``` az monitor activity-log alert create --subscription --resource-group --name --condition category=ServiceHealth and properties.incidentType=Incident --scope /subscriptions/ --action-group ``` **Remediate from PowerShell** Create the `Conditions` object: ``` $conditions = @() $conditions += New-AzActivityLogAlertAlertRuleAnyOfOrLeafConditionObject -Field category -Equal ServiceHealth $conditions += New-AzActivityLogAlertAlertRuleAnyOfOrLeafConditionObject -Field properties.incidentType -Equal Incident ``` Retrieve the `Action Group` information and store in a variable: ``` $actionGroup = Get-AzActionGroup -ResourceGroupName -Name ``` Create the `Actions` object: ``` $actionObject = New-AzActivityLogAlertActionGroupObject -Id $actionGroup.Id ``` Create the `Scope` object: ``` $scope = /subscriptions/ ``` Create the activity log alert rule: ``` New-AzActivityLogAlert -Name -ResourceGroupName -Condition $conditions -Scope $scope -Location global -Action $actionObject -Subscription -Enabled $true ``` Repeat for each subscription requiring remediation.",
+ "AuditProcedure": "**Audit from Azure Portal** 1. Go to `Monitor`. 1. Click `Alerts`. 1. Click `Alert rules`. 1. Ensure an alert rule exists for a subscription with `Condition` set to `Service names=All, Event types=All` and `Target resource type` set to `Subscription`. 1. If an alert rule is found for step 4, click the name of the alert rule. 1. Ensure the `Actions` panel displays an action group configured to notify appropriate personnel. 1. Repeat steps 1-6 for each subscription. **Audit from Azure CLI** Run the following command to list activity log alerts: ``` az monitor activity-log alert list --subscription ``` For each activity log alert, run the following command: ``` az monitor activity-log alert show --subscription --resource-group --activity-log-alert-name ``` Ensure an alert exists for `ServiceHealth` with `scopes` set to a subscription ID. Repeat for each subscription. **Audit from PowerShell** Run the following command to locate `ServiceHealth` alert rules for a subscription: ``` Get-AzActivityLogAlert -SubscriptionId | where-object {$_.ConditionAllOf.Equal -match ServiceHealth} | select-object Location,Name,Enabled,ResourceGroupName,ConditionAllOf ``` Ensure that at least one `ServiceHealth` alert rule is returned. Repeat for each subscription.",
+ "AdditionalInformation": "",
+ "References": "https://learn.microsoft.com/en-us/azure/service-health/overview:https://learn.microsoft.com/en-us/azure/service-health/alerts-activity-log-service-notifications-portal:https://azure.microsoft.com/en-gb/pricing/details/monitor/#faq:https://learn.microsoft.com/en-us/cli/azure/monitor/activity-log/alert:https://learn.microsoft.com/en-us/powershell/module/az.monitor/get-azactivitylogalert:https://learn.microsoft.com/en-us/powershell/module/az.monitor/new-azactivitylogalert",
+ "DefaultValue": "By default, no monitoring alerts are created."
+ }
+ ]
+ },
+ {
+ "Id": "8.8",
+ "Description": "Ensure that virtual network flow log retention days is set to greater than or equal to 90",
+ "Checks": [
+ "network_flow_log_more_than_90_days"
+ ],
+ "Attributes": [
+ {
+ "Section": "8 Networking Services",
+ "Profile": "Level 2",
+ "AssessmentStatus": "Automated",
+ "Description": "Ensure that virtual network flow logs are retained for greater than or equal to 90 days.",
+ "RationaleStatement": "Virtual network flow logs provide critical visibility into traffic patterns. Logs can be used to check for anomalies and give insight into suspected breaches.",
+ "ImpactStatement": "* Virtual network flow logs are charged per gigabyte of network flow logs collected and come with a free tier of 5 GB/month per subscription. * If traffic analytics is enabled with virtual network flow logs, traffic analytics pricing applies at per gigabyte processing rates. * The storage of logs is charged separately, and the cost will depend on the amount of logs and the retention period.",
+ "RemediationProcedure": "**Remediate from Azure Portal** 1. Go to `Network Watcher`. 1. Under `Logs`, select `Flow logs`. 1. Click `Add filter`. 1. From the `Filter` drop-down menu, select `Flow log type`. 1. From the `Value` drop-down menu, check `Virtual network` only. 1. Click `Apply`. 1. Click the name of a virtual network flow log. 1. Under `Storage Account`, set `Retention days` to `0`, `90`, or a number greater than 90. If `Retention days` is set to `0`, the logs are retained indefinitely with no retention policy. 1. Repeat steps 7 and 8 for each virtual network flow log requiring remediation. **Remediate from Azure CLI** Run the following command update the retention policy for a flow log in a network watcher, setting `retention` to `0`, `90`, or a number greater than 90: ``` az network watcher flow-log update --location --name --retention ``` Repeat for each virtual network flow log requiring remediation.",
+ "AuditProcedure": "**Audit from Azure Portal** 1. Go to `Network Watcher`. 1. Under `Logs`, select `Flow logs`. 1. Click `Add filter`. 1. From the `Filter` drop-down menu, select `Flow log type`. 1. From the `Value` drop-down menu, check `Virtual network` only. 1. Click `Apply`. 1. Click the name of a virtual network flow log. 1. Under `Storage Account`, ensure that `Retention days` is set to `0`, `90`, or a number greater than 90. If `Retention days` is set to `0`, the logs are retained indefinitely with no retention policy. 1. Repeat steps 7 and 8 for each virtual network flow log. **Audit from Azure CLI** Run the following command to list network watchers: ``` az network watcher list ``` Run the following command to list the name and retention policy of flow logs in a network watcher: ``` az network watcher flow-log list --location --query [*].[name,retentionPolicy] ``` For each flow log, ensure that `days` is set to `0`, `90`, or a number greater than 90. If `days` is set to `0`, the logs are retained indefinitely with no retention policy. Repeat for each network watcher.",
+ "AdditionalInformation": "As network security group flow logs are on the retirement path, Azure recommends migrating to virtual network flow logs.",
+ "References": "https://learn.microsoft.com/en-us/azure/network-watcher/vnet-flow-logs-portal:https://learn.microsoft.com/en-us/cli/azure/network/watcher/flow-log",
+ "DefaultValue": "When a virtual network flow log is created using the Azure CLI, retention days is set to 0 by default. When creating via the Azure Portal, retention days must be specified by the creator."
+ }
+ ]
+ },
+ {
+ "Id": "9.1.10",
+ "Description": "Ensure that Microsoft Defender for Cloud is configured to check VM operating systems for updates",
+ "Checks": [
+ "defender_ensure_system_updates_are_applied"
+ ],
+ "Attributes": [
+ {
+ "Section": "9 Security Services",
+ "SubSection": "9.1 Microsoft Defender for Cloud",
+ "Profile": "Level 1",
+ "AssessmentStatus": "Automated",
+ "Description": "Ensure that the latest OS patches for all virtual machines are applied.",
+ "RationaleStatement": "Windows and Linux virtual machines should be kept updated to: - Address a specific bug or flaw - Improve an OS or application’s general stability - Fix a security vulnerability Microsoft Defender for Cloud retrieves a list of available security and critical updates from Windows Update or Windows Server Update Services (WSUS), depending on which service is configured on a Windows VM. The security center also checks for the latest updates in Linux systems. If a VM is missing a system update, the security center will recommend system updates be applied.",
+ "ImpactStatement": "Running Microsoft Defender for Cloud incurs additional charges for each resource monitored. Please see attached reference for exact charges per hour.",
+ "RemediationProcedure": "Follow Microsoft Azure documentation to apply security patches from the security center. Alternatively, you can employ your own patch assessment and management tool to periodically assess, report, and install the required security patches for your OS.",
+ "AuditProcedure": "**Audit from Azure Portal** 1. From Azure Home select the Portal Menu 1. Select `Microsoft Defender for Cloud` 1. Then the `Recommendations` blade 1. Ensure that there are no recommendations for `System updates should be installed on your machines (powered by Update Center)` Alternatively, you can employ your own patch assessment and management tool to periodically assess, report and install the required security patches for your OS. **Audit from Azure Policy** If referencing a digital copy of this Benchmark, clicking a Policy ID will open a link to the associated Policy definition in Azure. If referencing a printed copy, you can search Policy IDs from this URL: https://portal.azure.com/#view/Microsoft_Azure_Policy/PolicyMenuBlade/~/Definitions - **Policy ID:** [f85bf3e0-d513-442e-89c3-1784ad63382b](https://portal.azure.com/#view/Microsoft_Azure_Policy/PolicyDetailBlade/definitionId/%2Fproviders%2FMicrosoft.Authorization%2FpolicyDefinitions%2Ff85bf3e0-d513-442e-89c3-1784ad63382b) **- Name:** 'System updates should be installed on your machines (powered by Update Center)' - **Policy ID:** [bd876905-5b84-4f73-ab2d-2e7a7c4568d9](https://portal.azure.com/#view/Microsoft_Azure_Policy/PolicyDetailBlade/definitionId/%2Fproviders%2FMicrosoft.Authorization%2FpolicyDefinitions%2Fbd876905-5b84-4f73-ab2d-2e7a7c4568d9) **- Name:** 'Machines should be configured to periodically check for missing system updates'",
+ "AdditionalInformation": "",
+ "References": "https://learn.microsoft.com/en-us/security/benchmark/azure/mcsb-posture-vulnerability-management#pv-6-rapidly-and-automatically-remediate-vulnerabilities:https://azure.microsoft.com/en-us/pricing/details/defender-for-cloud/:https://docs.microsoft.com/en-us/azure/defender-for-cloud/deploy-vulnerability-assessment-vm",
+ "DefaultValue": "By default, patches are not automatically deployed."
+ }
+ ]
+ },
+ {
+ "Id": "9.1.15",
+ "Description": "Ensure that 'Notify about attack paths with the following risk level (or higher)' is enabled",
+ "Checks": [
+ "defender_attack_path_notifications_properly_configured"
+ ],
+ "Attributes": [
+ {
+ "Section": "9 Security Services",
+ "SubSection": "9.1 Microsoft Defender for Cloud",
+ "Profile": "Level 1",
+ "AssessmentStatus": "Automated",
+ "Description": "Enables emailing attack paths to the subscription owner or other designated security contact.",
+ "RationaleStatement": "Enabling attack path emails ensures that attack path emails are sent by Microsoft. This ensures that the right people are aware of any potential security issues and can mitigate the risk.",
+ "ImpactStatement": "Enabling attack path emails can cause alert fatigue, increasing the risk of missing important alerts. Select an appropriate risk level to manage notifications. Azure aims to reduce alert fatigue by limiting the daily email volume per risk level. Learn more: https://learn.microsoft.com/en-us/azure/defender-for-cloud/configure-email-notifications#email-frequency.",
+ "RemediationProcedure": "**Remediate from Azure Portal** 1. From Azure Home select the Portal Menu. 1. Select `Microsoft Defender for Cloud`. 1. Under `Management`, select `Environment settings`. 1. Click on the appropriate Subscription. 1. Click on `Email notifications`. 1. Under Notification types, check the box next to `Notify about attack paths with the following risk level (or higher)`, and select an appropriate risk level from the drop-down menu. 1. Repeat steps 1-6 for each Subscription.",
+ "AuditProcedure": "**Audit from Azure Portal** 1. From Azure Home select the Portal Menu. 1. Select `Microsoft Defender for Cloud`. 1. Under `Management`, select `Environment settings`. 1. Click on the appropriate Subscription. 1. Click on `Email notifications`. 1. Under Notification types, ensure that the box next to `Notify about attack paths with the following risk level (or higher)` is checked, and an appropriate risk level is selected. 1. Repeat steps 1-6 for each Subscription. **Audit from Azure CLI** Including a Subscription ID at the `$0` in `/subscriptions/$0/providers`, ensure the below command returns `sourceType: AttackPath`, and that `minimalRiskLevel` is set to an appropriate risk level: ``` az account get-access-token --query {subscription:subscription,accessToken:accessToken} --out tsv | xargs -L1 bash -c 'curl -X GET -H Authorization: Bearer $1 -H Content-Type: application/json https://management.azure.com/subscriptions/$0/providers/Microsoft.Security/securityContacts?api-version=2023-12-01-preview' | jq '.|.[]' ```",
+ "AdditionalInformation": "",
+ "References": "https://learn.microsoft.com/en-us/azure/defender-for-cloud/configure-email-notifications:https://learn.microsoft.com/en-us/azure/defender-for-cloud/how-to-manage-attack-path:https://learn.microsoft.com/en-us/azure/defender-for-cloud/concept-attack-path",
+ "DefaultValue": ""
+ }
+ ]
+ },
+ {
+ "Id": "9.3.7",
+ "Description": "Ensure that Public Network Access when using Private Endpoint is disabled",
+ "Checks": [
+ "keyvault_access_only_through_private_endpoints"
+ ],
+ "Attributes": [
+ {
+ "Section": "9 Security Services",
+ "SubSection": "9.3 Key Vault",
+ "Profile": "Level 2",
+ "AssessmentStatus": "Automated",
+ "Description": "When Private endpoint is configured on a Key Vault, connections from Azure resources within the same subnet will use its private IP address. However, network traffic from the public internet can still flow connect to the Key Vault’s public endpoint (mykeyvault.vault.azure.net) using its public IP address unless Public network access is set to “Disabled”. Setting the Public network access to “Disabled” with a Private Endpoint will remove the Vault’s public endpoint from Azure public DNS, reducing its exposure to the public internet. Network traffic will use the Vault private endpoint IP address for all requests (mykeyvault.vault.privatelink.azure.net).",
+ "RationaleStatement": "Removing a point of interconnection from the internet edge to your Key Vault can strengthen the network security boundary of your system and reduce the risk of exposing the control plane or vault objects to untrusted clients. Although Azure resources are never truly isolated from the public internet, disabling the public endpoint removes a line of sight from the public internet and increases the effort required for an attack.",
+ "ImpactStatement": "Implementation needs to be properly designed from the ground up, as this is a fundamental change to the network architecture of your system. It will increase the configuration effort and decrease the usability of the Key Vault, and is appropriate for workloads where security is the primary consideration.",
+ "RemediationProcedure": "**Remediate from Azure Portal** Key Vaults can be configured to use `Azure role-based access control` on creation. For existing Key Vaults: 1. From Azure Home open the Portal Menu in the top left corner 2. Select `Key Vaults` 3. Select a Key Vault to audit 4. Select `Networking` 5. NEXT **Remediate from Azure CLI** To disable Public network access for each Key Vault, run the following Azure CLI command: ``` az keyvault update --resource-group --name --public-network-access Disabled ``` **Remediate from PowerShell** To enable RBAC authorization on each Key Vault, run the following PowerShell command: ``` Update-AzKeyVault -ResourceGroupName -VaultName -PublicNetworkAccess Disabled ```",
+ "AuditProcedure": "**Audit from Azure Portal** 1. From Azure Home open the Portal Menu in the top left corner 2. Select Key Vaults 3. Select a Key Vault to audit 4. Select Networking 5. Ensure that Public network access is Disabled. 6. Ensure that a Private endpoint is provisioned and connected. **Audit from Azure CLI** Run the following command for each Key Vault in each Resource Group: ``` az keyvault show --resource-group --name ``` Ensure the `publicNetworkSetting` setting is set to `Disabled` within the output of the above command. **Audit from PowerShell** Run the following PowerShell command: ``` Get-AzKeyVault -Vaultname -ResourceGroupName ``` Ensure the `Public network access` setting is set to `Disabled` **Audit from Azure Policy** If referencing a digital copy of this Benchmark, clicking a Policy ID will open a link to the associated Policy definition in Azure. If referencing a printed copy, you can search Policy IDs from this URL: https://portal.azure.com/#view/Microsoft_Azure_Policy/PolicyMenuBlade/~/Definitions - **Policy ID:** [405c5871-3e91-4644-8a63-58e19d68ff5b](https://portal.azure.com/#view/Microsoft_Azure_Policy/PolicyDetailBlade/definitionId/%2Fproviders%2FMicrosoft.Authorization%2FpolicyDefinitions%2F405c5871-3e91-4644-8a63-58e19d68ff5b) **- Name:** 'Azure Key Vault should disable public network access'",
+ "AdditionalInformation": "",
+ "References": "https://learn.microsoft.com/en-us/azure/key-vault/general/network-security:https://learn.microsoft.com/en-us/azure/key-vault/general/private-link-service",
+ "DefaultValue": "The default value for Access control in Key Vaults is Vault Policy."
+ }
+ ]
+ },
+ {
+ "Id": "10.1.1",
+ "Description": "Ensure soft delete for Azure File Shares is Enabled",
+ "Checks": [
+ "storage_ensure_file_shares_soft_delete_is_enabled"
+ ],
+ "Attributes": [
+ {
+ "Section": "10 Storage Services",
+ "SubSection": "10.1 Azure Files",
+ "Profile": "Level 1",
+ "AssessmentStatus": "Automated",
+ "Description": "Azure Files offers soft delete for file shares, allowing you to easily recover your data when it is mistakenly deleted by an application or another storage account user.",
+ "RationaleStatement": "Important data could be accidentally deleted or removed by a malicious actor. With soft delete enabled, the data is retained for the defined retention period before permanent deletion, allowing for recovery of the data.",
+ "ImpactStatement": "When a file share is soft-deleted, the used portion of the storage is charged for the indicated soft-deleted period. All other meters are not charged unless the share is restored.",
+ "RemediationProcedure": "**Remediate from Azure Portal** 1. Go to `Storage Accounts`. 1. For each storage account with file shares, under `Data storage`, click `File shares`. 1. Under `File share settings`, click the value next to `Soft delete`. 1. Under `Soft delete for all file shares`, click the toggle to set it to `Enabled`. 1. Under `Retention policies`, set an appropriate number of days to retain soft deleted data between 1 and 365, inclusive. 1. Click `Save`. **Remediate from Azure CLI** For each storage account requiring remediation, run the following command to enable soft delete for file shares and set an appropriate number of days for deleted data to be retained, between 1 and 365, inclusive: ``` az storage account file-service-properties update --account-name --enable-delete-retention true --delete-retention-days ``` **Remediate from PowerShell** For each storage account requiring remediation, run the following command to enable soft delete for file shares and set an appropriate number of days for deleted data to be retained, between 1 and 365, inclusive: ``` Update-AzStorageFileServiceProperty -ResourceGroupName -AccountName -EnableShareDeleteRetentionPolicy $true -ShareRetentionDays ```",
+ "AuditProcedure": "**Audit from Azure Portal** 1. Go to `Storage Accounts`. 1. For each storage account with file shares, under `Data storage`, click on `File shares`. 1. Under `File share settings`, ensure the value for `Soft delete` shows a number of days between 1 and 365, inclusive. **Audit from Azure CLI** Run the following command to list storage accounts: ``` az storage account list ``` Run the following command to determine if a storage account has file shares: ``` az storage share list --account-name ``` For each storage account with file shares, run the following command: ``` az storage account file-service-properties show --resource-group --account-name ``` Ensure that under `shareDeleteRetentionPolicy`, `enabled` is set to `true`, and `days` is set to an appropriate value between 1 and 365, inclusive. **Audit from PowerShell** Run the following command to list storage accounts: ``` Get-AzStorageAccount -ResourceGroupName ``` With a storage account context set, run the following command to determine if a storage account has file shares: ``` Get-AzStorageShare ``` For each storage account with file shares, run the following command: ``` Get-AzStorageFileServiceProperty -ResourceGroupName -AccountName ``` Ensure that `ShareDeleteRetentionPolicy.Enabled` is set to `True` and `ShareDeleteRetentionPolicy.Days` is set to an appropriate value between 1 and 365, inclusive.",
+ "AdditionalInformation": "",
+ "References": "https://learn.microsoft.com/en-us/azure/storage/files/storage-files-enable-soft-delete:https://learn.microsoft.com/en-us/cli/azure/storage/account/file-service-properties:https://learn.microsoft.com/en-us/powershell/module/az.storage/get-azstoragefileserviceproperty:https://learn.microsoft.com/en-us/powershell/module/az.storage/update-azstoragefileserviceproperty:https://learn.microsoft.com/en-us/azure/storage/files/storage-files-prevent-file-share-deletion",
+ "DefaultValue": "Soft delete is enabled by default at the storage account file share setting level."
+ }
+ ]
+ },
+ {
+ "Id": "4.1.1",
+ "Description": "Ensure only MFA enabled identities can access privileged Virtual Machine",
+ "Checks": [
+ "entra_user_with_vm_access_has_mfa"
+ ],
+ "Attributes": [
+ {
+ "Section": "4 Compute Services",
+ "SubSection": "4.1 Virtual Machines",
+ "Profile": "Level 2",
+ "AssessmentStatus": "Manual",
+ "Description": "Verify identities without MFA that can log in to a privileged virtual machine using separate login credentials. An adversary can leverage the access to move laterally and perform actions with the virtual machine's managed identity. Make sure the virtual machine only has necessary permissions, and revoke the admin-level permissions according to the principle of least privilege.",
+ "RationaleStatement": "Integrating multi-factor authentication (MFA) as part of the organizational policy can greatly reduce the risk of an identity gaining control of valid credentials that may be used for additional tactics such as initial access, lateral movement, and collecting information. MFA can also be used to restrict access to cloud resources and APIs. An Adversary may log into accessible cloud services within a compromised environment using Valid Accounts that are synchronized to move laterally and perform actions with the virtual machine's managed identity. The adversary may then perform management actions or access cloud-hosted resources as the logged-on managed identity.",
+ "ImpactStatement": "This recommendation requires the Entra ID P2 license to implement. Ensure that identities provisioned to a virtual machine utilize an RBAC/ABAC group and are allocated a role using Azure PIM, and that the role settings require MFA or use another third-party PAM solution for accessing virtual machines.",
+ "RemediationProcedure": "**Remediate from Azure Portal** 1. Log in to the Azure portal. 2. This can be remediated by enabling MFA for user, Removing user access or Reducing access of managed identities attached to virtual machines. - Case I : Enable MFA for users having access on virtual machines. 1. Go to `Microsoft Entra ID`. 1. For `Per-user MFA`: 1. Under `Manage`, click `Users`. 1. Click `Per-user MFA`. 1. For each user requiring remediation, check the box next to their name. 1. Click `Enable MFA`. 1. Click `Enable`. 1. For `Conditional Access`: 1. Under `Manage`, click `Security`. 1. Under `Protect`, click `Conditional Access`. 1. Update the Conditional Access policy requiring MFA for all users, removing each user requiring remediation from the `Exclude` list. - Case II : Removing user access on a virtual machine. 1. Select the `Subscription`, then click on `Access control (IAM)`. 2. Select `Role assignments` and search for `Virtual Machine Administrator Login` or `Virtual Machine User Login` or any role that provides access to log into virtual machines. 3. Click on `Role Name`, Select `Assignments`, and remove identities with no MFA configured. - Case III : Reducing access of managed identities attached to virtual machines. 1. Select the `Subscription`, then click on `Access control (IAM)`. 2. Select `Role Assignments` from the top menu and apply filters on `Assignment type` as `Privileged administrator roles` and `Type` as `Virtual Machines`. 3. Click on `Role Name`, Select `Assignments`, and remove identities access make sure this follows the least privileges principal.",
+ "AuditProcedure": "**Audit from Azure Portal** 1. Log in to the Azure portal. 1. Select the `Subscription`, then click on `Access control (IAM)`. 1. Click `Role : All` and click `All` to display the drop-down menu. 1. Type `Virtual Machine Administrator Login` and select `Virtual Machine Administrator Login`. 1. Review the list of identities that have been assigned the `Virtual Machine Administrator Login` role. 1. Go to `Microsoft Entra ID`. 1. For `Per-user MFA`: 1. Under `Manage`, click `Users`. 1. Click `Per-user MFA`. 1. Ensure that none of the identities assigned the `Virtual Machine Administrator Login` role from step 4 have `Status` set to `disabled`. 1. For `Conditional Access`: 1. Under `Manage`, click `Security`. 1. Under `Protect`, click `Conditional Access`. 1. Ensure that none of the identities assigned the `Virtual Machine Administrator Login` role from step 4 are exempt from a Conditional Access policy requiring MFA for all users.",
+ "AdditionalInformation": "",
+ "References": "",
+ "DefaultValue": ""
+ }
+ ]
+ },
+ {
+ "Id": "6.4",
+ "Description": "Ensure that 'Restrict non-admin users from creating tenants' is set to 'Yes'",
+ "Checks": [
+ "entra_policy_ensure_default_user_cannot_create_tenants"
+ ],
+ "Attributes": [
+ {
+ "Section": "6 Identity Services",
+ "Profile": "Level 1",
+ "AssessmentStatus": "Automated",
+ "Description": "Require administrators or appropriately delegated users to create new tenants.",
+ "RationaleStatement": "It is recommended to only allow an administrator to create new tenants. This prevent users from creating new Microsoft Entra ID or Azure AD B2C tenants and ensures that only authorized users are able to do so.",
+ "ImpactStatement": "Enforcing this setting will ensure that only authorized users are able to create new tenants.",
+ "RemediationProcedure": "**Remediate from Azure Portal** 1. From Azure Home select the Portal Menu 1. Select `Microsoft Entra ID` 1. Under `Manage`, select `Users` 1. Under `Manage`, select `User settings` 1. Set `Restrict non-admin users from creating tenants ` to `Yes` 1. Click `Save` **Remediate from PowerShell** ``` Import-Module Microsoft.Graph.Identity.SignIns Connect-MgGraph -Scopes 'Policy.ReadWrite.Authorization' Select-MgProfile -Name beta $params = @{ DefaultUserRolePermissions = @{ AllowedToCreateTenants = $false } } Update-MgPolicyAuthorizationPolicy -AuthorizationPolicyId -BodyParameter $params ```",
+ "AuditProcedure": "**Audit from Azure Portal** 1. From Azure Home select the Portal Menu 1. Select `Microsoft Entra ID` 1. Under `Manage`, select `Users` 1. Under `Manage`, select `User settings` 1. Ensure that `Restrict non-admin users from creating tenants` is set to `Yes` **Audit from PowerShell** ``` Import-Module Microsoft.Graph.Identity.SignIns Connect-MgGraph -Scopes 'Policy.ReadWrite.Authorization' Get-MgPolicyAuthorizationPolicy | Select-Object -ExpandProperty DefaultUserRolePermissions | Format-List ``` Review the DefaultUserRolePermissions section of the output. Ensure that `AllowedToCreateTenants` is not `True`.",
+ "AdditionalInformation": "",
+ "References": "https://learn.microsoft.com/en-us/azure/active-directory/fundamentals/users-default-permissions:https://learn.microsoft.com/en-us/azure/active-directory/roles/permissions-reference#tenant-creator:https://blog.admindroid.com/disable-users-creating-new-azure-ad-tenants-in-microsoft-365/",
+ "DefaultValue": ""
+ }
+ ]
+ },
+ {
+ "Id": "10.1.2",
+ "Description": "Ensure 'SMB protocol version' is set to 'SMB 3.1.1' or higher for SMB file shares",
+ "Checks": [
+ "storage_smb_protocol_version_is_latest"
+ ],
+ "Attributes": [
+ {
+ "Section": "10 Storage Services",
+ "SubSection": "10.1 Azure Files",
+ "Profile": "Level 1",
+ "AssessmentStatus": "Automated",
+ "Description": "Ensure that SMB file shares are configured to use the latest supported SMB protocol version. Keeping the SMB protocol updated helps mitigate risks associated with older SMB versions, which may contain vulnerabilities and lack essential security controls.",
+ "RationaleStatement": "Using the latest supported SMB protocol version enhances the security of SMB file shares by preventing the exploitation of known vulnerabilities in outdated SMB versions.",
+ "ImpactStatement": "Using the latest SMB protocol version may impact client compatibility.",
+ "RemediationProcedure": "**Remediate from Azure Portal** 1. Go to `Storage accounts`. 1. Click the name of a storage account. 1. Under `Data storage`, click `File shares`. 1. Under `File share settings`, click the link next to `Security`. 1. If `Profile` is set to `Maximum compatibility`, click the drop-down menu and select `Maximum security` or `Custom`. 1. If selecting `Custom`, under `SMB protocol versions`, uncheck the boxes next to `SMB 2.1` and `SMB 3.0`. 1. Click `Save`. 1. Repeat steps 1-7 for each storage account requiring remediation. **Remediate from Azure CLI** For each storage account requiring remediation, run the following command to set the SMB protocol version: ``` az storage account file-service-properties update --resource-group --account-name --versions SMB3.1.1 ``` **Remediate from PowerShell** For each storage account requiring remediation, run the following command to set the SMB protocol version: ``` Update-AzStorageFileServiceProperty -ResourceGroupName -StorageAccountName -SmbProtocolVersion SMB3.1.1 ```",
+ "AuditProcedure": "**Audit from Azure Portal** 1. Go to `Storage accounts`. 1. Click the name of a storage account. 1. Under `Data storage`, click `File shares`. 1. Under `File share settings`, click the link next to `Security`. 1. Under `SMB protocol versions`, ensure that `SMB3.1.1` is the only checked protocol version. 1. Repeat steps 1-5 for each storage account. **Audit from Azure CLI** Run the following command to list storage accounts: ``` az storage account list ``` For each storage account, run the following command: ``` az storage account file-service-properties show --resource-group --account-name ``` Ensure that under `protocolSettings` > `smb`, `versions` is set to `SMB3.1.1;` only. **Audit from PowerShell** Run the following command to list storage accounts: ``` Get-AzStorageAccount ``` Run the following command to get the file service properties for a storage account in a resource group with a given name: ``` $storageaccountfileservice = Get-AzStorageFileServiceProperty -ResourceGroupName -AccountName ``` Run the following command to get the SMB protocol version setting: ``` $storageaccountfileservice.ProtocolSettings.Smb.Versions ``` Ensure that the command returns `SMB3.1.1` only. Repeat for each storage account.",
+ "AdditionalInformation": "",
+ "References": "https://learn.microsoft.com/en-us/azure/well-architected/service-guides/azure-files#recommendations-for-smb-file-shares:https://learn.microsoft.com/en-us/azure/storage/files/files-smb-protocol#smb-security-settings:https://learn.microsoft.com/en-us/cli/azure/storage/account/file-service-properties:https://learn.microsoft.com/en-us/powershell/module/az.storage/get-azstoragefileserviceproperty:https://learn.microsoft.com/en-us/powershell/module/az.storage/update-azstoragefileserviceproperty",
+ "DefaultValue": "By default, all SMB versions are allowed."
+ }
+ ]
+ },
+ {
+ "Id": "10.1.3",
+ "Description": "Ensure 'SMB channel encryption' is set to 'AES-256-GCM' or higher for SMB file shares",
+ "Checks": [
+ "storage_smb_channel_encryption_with_secure_algorithm"
+ ],
+ "Attributes": [
+ {
+ "Section": "10 Storage Services",
+ "SubSection": "10.1 Azure Files",
+ "Profile": "Level 1",
+ "AssessmentStatus": "Automated",
+ "Description": "Implement SMB channel encryption with AES-256-GCM for SMB file shares to ensure data confidentiality and integrity in transit. This method offers strong protection against eavesdropping and man-in-the-middle attacks, safeguarding sensitive information.",
+ "RationaleStatement": "AES-256-GCM encryption enhances the security of data transmitted over SMB channels by safeguarding it from unauthorized interception and tampering.",
+ "ImpactStatement": "Using the AES-256-GCM SMB channel encryption may impact client compatibility.",
+ "RemediationProcedure": "**Remediate from Azure Portal** 1. Go to `Storage accounts`. 1. Click the name of a storage account. 1. Under `Data storage`, click `File shares`. 1. Under `File share settings`, click the link next to `Security`. 1. If `Profile` is set to `Maximum compatibility`, click the drop-down menu and select `Maximum security` or `Custom`. 1. If selecting `Custom`, under `SMB channel encryption`, uncheck the boxes next to `AES-128-CCM` and `AES-128-GCM`. 1. Click `Save`. 1. Repeat steps 1-7 for each storage account requiring remediation. **Remediate from Azure CLI** For each storage account requiring remediation, run the following command to set the SMB channel encryption: ``` az storage account file-service-properties update --resource-group --account-name --channel-encryption AES-256-GCM ``` **Remediate from PowerShell** For each storage account requiring remediation, run the following command to set the SMB channel encryption: ``` Update-AzStorageFileServiceProperty -ResourceGroupName -StorageAccountName -SmbChannelEncryption AES-256-GCM ```",
+ "AuditProcedure": "**Audit from Azure Portal** 1. Go to `Storage accounts`. 1. Click the name of a storage account. 1. Under `Data storage`, click `File shares`. 1. Under `File share settings`, click the link next to `Security`. 1. Under `SMB channel encryption`, ensure that `AES-256-GCM`, or higher, is the only checked SMB channel encryption setting. 1. Repeat steps 1-5 for each storage account. **Audit from Azure CLI** Run the following command to list storage accounts: ``` az storage account list ``` For each storage account, run the following command: ``` az storage account file-service-properties show --resource-group --account-name ``` Ensure that under `protocolSettings` > `smb`, `channelEncryption` is set to `AES-256-GCM;`, or higher, only. **Audit from PowerShell** Run the following command to list storage accounts: ``` Get-AzStorageAccount ``` Run the following command to get the file service properties for a storage account in a resource group with a given name: ``` $storageaccountfileservice = Get-AzStorageFileServiceProperty -ResourceGroupName -AccountName ``` Run the following command to get the SMB channel encryption setting: ``` $storageaccountfileservice.ProtocolSettings.Smb.ChannelEncryption ``` Ensure that the command returns `AES-256-GCM`, or higher, only. Repeat for each storage account.",
+ "AdditionalInformation": "",
+ "References": "https://learn.microsoft.com/en-us/azure/well-architected/service-guides/azure-files#recommendations-for-smb-file-shares:https://learn.microsoft.com/en-us/azure/storage/files/files-smb-protocol?tabs=azure-portal#smb-security-settings:https://learn.microsoft.com/en-us/cli/azure/storage/account/file-service-properties:https://learn.microsoft.com/en-us/powershell/module/az.storage/get-azstoragefileserviceproperty:https://learn.microsoft.com/en-us/powershell/module/az.storage/update-azstoragefileserviceproperty",
+ "DefaultValue": "By default, the following SMB channel encryption algorithms are allowed: - AES-128-CCM - AES-128-GCM - AES-256-GCM"
+ }
+ ]
+ },
+ {
+ "Id": "10.2.1",
+ "Description": "Ensure that soft delete for blobs on Azure Blob Storage storage accounts is Enabled",
+ "Checks": [
+ "storage_ensure_soft_delete_is_enabled"
+ ],
+ "Attributes": [
+ {
+ "Section": "10 Storage Services",
+ "SubSection": "10.2 Azure Blob Storage",
+ "Profile": "Level 1",
+ "AssessmentStatus": "Automated",
+ "Description": "Blobs in Azure storage accounts may contain sensitive or personal data, such as ePHI or financial information. Data that is erroneously modified or deleted by an application or a user can lead to data loss or unavailability. It is recommended that soft delete be enabled on Azure storage accounts with blob storage to allow for the preservation and recovery of data when blobs or blob snapshots are deleted.",
+ "RationaleStatement": "Blobs can be deleted incorrectly. An attacker or malicious user may do this deliberately in order to cause disruption. Deleting an Azure storage blob results in immediate data loss. Enabling this configuration for Azure storage accounts ensures that even if blobs are deleted from the storage account, the blobs are recoverable for a specific period of time, which is defined in the Retention policies, ranging from 7 to 365 days.",
+ "ImpactStatement": "All soft-deleted data is billed at the same rate as active data. Additional costs may be incurred for deleted blobs until the soft delete period ends and the data is permanently removed.",
+ "RemediationProcedure": "**Remediate from Azure Portal** 1. Go to `Storage Accounts`. 1. For each Storage Account with blob storage, under `Data management`, go to `Data protection`. 1. Check the box next to `Enable soft delete for blobs`. 1. Set the retention period to a sufficient length for your organization. 1. Click `Save`. **Remediate from Azure CLI** For each storage account requiring remediation, run the following command to enable soft delete for blobs: ``` az storage blob service-properties delete-policy update --days-retained --account-name --enable true ```",
+ "AuditProcedure": "**Audit from Azure Portal** 1. Go to `Storage Accounts`. 1. For each Storage Account with blob storage, under `Data management`, go to `Data protection`. 1. Ensure that `Enable soft delete for blobs` is checked. 1. Ensure that the retention period is a sufficient length for your organization. **Audit from Azure CLI** Run the following command to list storage accounts: ``` az storage account list ``` Run the following command to determine if a storage account has containers: ``` az storage container list --account-name ``` For each storage account with containers, ensure that the output of the below command contains `enabled: true` and `days` is not `null`: ``` az storage blob service-properties delete-policy show --account-name ```",
+ "AdditionalInformation": "",
+ "References": "https://learn.microsoft.com/en-us/azure/storage/blobs/soft-delete-blob-overview",
+ "DefaultValue": "Soft delete for blob storage is **enabled** by default on storage accounts created via the Azure Portal, and **disabled** by default on storage accounts created via Azure CLI or PowerShell."
+ }
+ ]
+ },
+ {
+ "Id": "10.2.2",
+ "Description": "Ensure 'Versioning' is set to 'Enabled' on Azure Blob Storage storage accounts",
+ "Checks": [
+ "storage_blob_versioning_is_enabled"
+ ],
+ "Attributes": [
+ {
+ "Section": "10 Storage Services",
+ "SubSection": "10.2 Azure Blob Storage",
+ "Profile": "Level 2",
+ "AssessmentStatus": "Automated",
+ "Description": "Enabling blob versioning allows for the automatic retention of previous versions of objects. With blob versioning enabled, earlier versions of a blob are accessible for data recovery in the event of modifications or deletions.",
+ "RationaleStatement": "Blob versioning safeguards data integrity and enables recovery by retaining previous versions of stored objects, facilitating quick restoration from accidental deletion, modification, or malicious activity.",
+ "ImpactStatement": "Enabling blob versioning for a storage account creates a new version with each write operation to a blob, which can increase storage costs. To control these costs, a lifecycle management policy can be applied to automatically delete older versions.",
+ "RemediationProcedure": "**Remediate from Azure Portal** 1. Go to `Storage accounts`. 1. Click the name of a storage account with blob storage. 1. In the `Overview` page, on the `Properties` tab, under `Blob service`, click `Disabled` next to `Versioning`. 1. Under `Tracking`, check the box next to `Enable versioning for blobs`. 1. Select the radio button next to `Keep all versions` or `Delete versions after (in days)`. 1. If selecting to delete versions, enter a number of in the box after which to delete blob versions. 1. Click `Save`. 1. Repeat steps 1-7 for each storage account with blob storage. **Remediate from Azure CLI** For each storage account requiring remediation, run the following command to enable blob versioning: ``` az storage account blob-service-properties update --account-name --enable-versioning true ``` **Remediate from PowerShell** For each storage account requiring remediation, run the following command to enable blob versioning: ``` Update-AzStorageBlobServiceProperty -ResourceGroupName -StorageAccountName -IsVersioningEnabled $true ```",
+ "AuditProcedure": "**Audit from Azure Portal** 1. Go to `Storage accounts`. 1. Click the name of a storage account with blob storage. 1. In the `Overview` page, on the `Properties` tab, under `Blob service`, ensure `Versioning` is set to `Enabled`. 1. Repeat steps 1-3 for each storage account with blob storage. **Audit from Azure CLI** Run the following command to list storage accounts: ``` az storage account list ``` Run the following command to determine if a storage account has containers: ``` az storage container list --account-name ``` For each storage account with containers, ensure that the output of the below command contains `isVersioningEnabled: true`: ``` az storage account blob-service-properties show --account-name ``` **Audit from PowerShell** Run the following command to list storage accounts: ``` Get-AzStorageAccount ``` Run the following command to create an Azure Storage context for a storage account: ``` $context = New-AzStorageContext -StorageAccountName ``` Run the following command to list containers for the storage account: ``` Get-AzStorageContainer -Context $context ``` If the storage account has containers, run the following command to get the blob service properties of the storage account: ``` $account = Get-AzStorageBlobServiceProperty -ResourceGroupName -AccountName ``` Run the following command to get the blob versioning setting for the storage account: ``` $account.IsVersioningEnabled ``` Ensure that the command returns `True`. Repeat for each storage account. **Audit from Azure Policy** If referencing a digital copy of this Benchmark, clicking a Policy ID will open a link to the associated Policy definition in Azure. If referencing a printed copy, you can search Policy IDs from this URL: https://portal.azure.com/#view/Microsoft_Azure_Policy/PolicyMenuBlade/~/Definitions - **Policy ID:** [c36a325b-ae04-4863-ad4f-19c6678f8e08](https://portal.azure.com/#view/Microsoft_Azure_Policy/PolicyDetailBlade/definitionId/%2Fproviders%2FMicrosoft.Authorization%2FpolicyDefinitions%2Fc36a325b-ae04-4863-ad4f-19c6678f8e08) **- Name:** 'Configure your Storage account to enable blob versioning'",
+ "AdditionalInformation": "",
+ "References": "https://learn.microsoft.com/en-us/cli/azure/storage/account:https://learn.microsoft.com/en-us/cli/azure/storage/account/blob-service-properties:https://learn.microsoft.com/en-us/powershell/module/az.storage/get-azstorageaccount:https://learn.microsoft.com/en-us/powershell/module/az.storage/new-azstoragecontext:https://learn.microsoft.com/en-us/powershell/module/az.storage/get-azstoragecontainer:https://learn.microsoft.com/en-us/powershell/module/az.storage/get-azstorageblobserviceproperty:https://learn.microsoft.com/en-us/powershell/module/az.storage/update-azstorageblobserviceproperty:https://learn.microsoft.com/en-us/azure/storage/blobs/versioning-overview:https://learn.microsoft.com/en-us/azure/storage/blobs/lifecycle-management-overview",
+ "DefaultValue": "Blob versioning is disabled by default on storage accounts."
+ }
+ ]
+ },
+ {
+ "Id": "10.3.8",
+ "Description": "Ensure 'Cross Tenant Replication' is not enabled",
+ "Checks": [
+ "storage_cross_tenant_replication_disabled"
+ ],
+ "Attributes": [
+ {
+ "Section": "10 Storage Services",
+ "SubSection": "10.3 Storage Accounts",
+ "Profile": "Level 1",
+ "AssessmentStatus": "Automated",
+ "Description": "Cross Tenant Replication in Azure allows data to be replicated across multiple Azure tenants. While this feature can be beneficial for data sharing and availability, it also poses a significant security risk if not properly managed. Unauthorized data access, data leakage, and compliance violations are potential risks. Disabling Cross Tenant Replication ensures that data is not inadvertently replicated across different tenant boundaries without explicit authorization.",
+ "RationaleStatement": "Disabling Cross Tenant Replication minimizes the risk of unauthorized data access and ensures that data governance policies are strictly adhered to. This control is especially critical for organizations with stringent data security and privacy requirements, as it prevents the accidental sharing of sensitive information.",
+ "ImpactStatement": "Disabling Cross Tenant Replication may affect data availability and sharing across different Azure tenants. Ensure that this change aligns with your organizational data sharing and availability requirements.",
+ "RemediationProcedure": "**Remediate from Azure Portal** 1. Go to `Storage Accounts`. 1. For each storage account, under `Data management`, click `Object replication`. 1. Click `Advanced settings`. 1. Uncheck `Allow cross-tenant replication`. 1. Click `OK`. **Remediate from Azure CLI** Replace the information within <> with appropriate values: ``` az storage account update --name --resource-group --allow-cross-tenant-replication false ```",
+ "AuditProcedure": "**Audit from Azure Portal** 1. Go to `Storage Accounts`. 1. For each storage account, under `Data management`, click `Object replication`. 1. Click `Advanced settings`. 1. Ensure `Allow cross-tenant replication` is not checked. **Audit from Azure CLI** ``` az storage account list --query [*].[name,allowCrossTenantReplication] ``` The value of `false` should be returned for each storage account listed. **Audit from Azure Policy** If referencing a digital copy of this Benchmark, clicking a Policy ID will open a link to the associated Policy definition in Azure. If referencing a printed copy, you can search Policy IDs from this URL: https://portal.azure.com/#view/Microsoft_Azure_Policy/PolicyMenuBlade/~/Definitions - **Policy ID:** [92a89a79-6c52-4a7e-a03f-61306fc49312](https://portal.azure.com/#view/Microsoft_Azure_Policy/PolicyDetailBlade/definitionId/%2Fproviders%2FMicrosoft.Authorization%2FpolicyDefinitions%2F92a89a79-6c52-4a7e-a03f-61306fc49312) **- Name:** 'Storage accounts should prevent cross tenant object replication'",
+ "AdditionalInformation": "",
+ "References": "https://learn.microsoft.com/en-us/azure/storage/blobs/object-replication-prevent-cross-tenant-policies?tabs=portal",
+ "DefaultValue": "For new storage accounts created after Dec 15, 2023 cross tenant replication is not enabled."
+ }
+ ]
+ },
+ {
+ "Id": "10.3.9",
+ "Description": "Ensure that 'Allow Blob Anonymous Access' is set to 'Disabled'",
+ "Checks": [
+ "storage_blob_public_access_level_is_disabled"
+ ],
+ "Attributes": [
+ {
+ "Section": "10 Storage Services",
+ "SubSection": "10.3 Storage Accounts",
+ "Profile": "Level 1",
+ "AssessmentStatus": "Automated",
+ "Description": "The Azure Storage setting ‘Allow Blob Anonymous Access’ (aka allowBlobPublicAccess) controls whether anonymous access is allowed for blob data in a storage account. When this property is set to True, it enables public read access to blob data, which can be convenient for sharing data but may carry security risks. When set to False, it disallows public access to blob data, providing a more secure storage environment.",
+ "RationaleStatement": "If Allow Blob Anonymous Access is enabled, blobs can be accessed by adding the blob name to the URL to see the contents. An attacker can enumerate a blob using methods, such as brute force, and access them. Exfiltration of data by brute force enumeration of items from a storage account may occur if this setting is set to 'Enabled'.",
+ "ImpactStatement": "Additional consideration may be required for exceptional circumstances where elements of a storage account require public accessibility. In these circumstances, it is highly recommended that all data stored in the public facing storage account be reviewed for sensitive or potentially compromising data, and that sensitive or compromising data is never stored in these storage accounts.",
+ "RemediationProcedure": "**Remediate from Azure Portal** 1. Go to `Storage Accounts`. 1. For each storage account, under `Settings`, click `Configuration`. 1. Set `Allow Blob Anonymous Access` to `Disabled`. 1. Click `Save`. **Remediate from Powershell** For every storage account in scope, run the following: ``` $storageAccount = Get-AzStorageAccount -ResourceGroupName -Name $storageAccount.AllowBlobPublicAccess = $false Set-AzStorageAccount -InputObject $storageAccount ```",
+ "AuditProcedure": "**Audit from Azure Portal** 1. Go to `Storage Accounts`. 1. For each storage account, under `Settings`, click `Configuration`. 1. Ensure `Allow Blob Anonymous Access` is set to `Disabled`. **Audit from Azure CLI** For every storage account in scope: ``` az storage account show --name --query allowBlobPublicAccess ``` Ensure that every storage account in scope returns `false` for the allowBlobPublicAccess setting. **Audit from Azure Policy** If referencing a digital copy of this Benchmark, clicking a Policy ID will open a link to the associated Policy definition in Azure. If referencing a printed copy, you can search Policy IDs from this URL: https://portal.azure.com/#view/Microsoft_Azure_Policy/PolicyMenuBlade/~/Definitions - **Policy ID:** [4fa4b6c0-31ca-4c0d-b10d-24b96f62a751](https://portal.azure.com/#view/Microsoft_Azure_Policy/PolicyDetailBlade/definitionId/%2Fproviders%2FMicrosoft.Authorization%2FpolicyDefinitions%2F4fa4b6c0-31ca-4c0d-b10d-24b96f62a751) **- Name:** 'Storage account public access should be disallowed'",
+ "AdditionalInformation": "Azure Storage accounts that use the classic deployment model will be retired on August 31, 2024.",
+ "References": "https://learn.microsoft.com/en-us/azure/storage/blobs/anonymous-read-access-prevent?tabs=portal:https://learn.microsoft.com/en-us/azure/storage/blobs/anonymous-read-access-prevent?source=recommendations&tabs=portal:Classic Storage Accounts: https://learn.microsoft.com/en-us/azure/storage/blobs/anonymous-read-access-prevent-classic?tabs=portal",
+ "DefaultValue": "Disabled"
+ }
+ ]
+ },
+ {
+ "Id": "10.3.12",
+ "Description": "Ensure Redundancy is set to 'geo-redundant storage (GRS)' on critical Azure Storage Accounts",
+ "Checks": [
+ "storage_geo_redundant_enabled"
+ ],
+ "Attributes": [
+ {
+ "Section": "10 Storage Services",
+ "SubSection": "10.3 Storage Accounts",
+ "Profile": "Level 2",
+ "AssessmentStatus": "Automated",
+ "Description": "Geo-redundant storage (GRS) in Azure replicates data three times within the primary region using locally redundant storage (LRS) and asynchronously copies it to a secondary region hundreds of miles away. This setup ensures high availability and resilience by providing 16 nines (99.99999999999999%) durability over a year, safeguarding data against regional outages.",
+ "RationaleStatement": "Enabling GRS protects critical data from regional failures by maintaining a copy in a geographically separate location. This significantly reduces the risk of data loss, supports business continuity, and meets high availability requirements for disaster recovery.",
+ "ImpactStatement": "Enabling geo-redundant storage on Azure storage accounts increases costs due to cross-region data replication.",
+ "RemediationProcedure": "**Remediate from Azure Portal** 1. Go to `Storage accounts`. 1. Click on a storage account. 1. Under `Data management`, click `Redundancy`. 1. From the `Redundancy` drop-down menu, select `Geo-redundant storage (GRS)`. 1. Click `Save`. 1. Repeat steps 1-5 for each storage account requiring remediation. **Remediate from Azure CLI** For each storage account requiring remediation, run the following command to enable geo-redundant storage: ``` az storage account update --resource-group --name --sku Standard_GRS ``` **Remediate from PowerShell** For each storage account requiring remediation, run the following command to enable geo-redundant storage: ``` Set-AzStorageAccount -ResourceGroupName -Name -SkuName Standard_GRS ```",
+ "AuditProcedure": "**Audit from Azure Portal** 1. Go to `Storage accounts`. 1. Click on a storage account. 1. Under `Data management`, click `Redundancy`. 1. Ensure that `Redundancy` is set to `Geo-redundant storage (GRS)`. 1. Repeat steps 1-4 for each storage account. **Audit from Azure CLI** Run the following command to list storage accounts: ``` az storage account list ``` For each storage account, run the following command: ``` az storage account show --resource-group --name ``` Under `sku`, ensure that `name` is set to `Standard_GRS`. **Audit from PowerShell** Run the following command to list storage accounts: ``` Get-AzStorageAccount ``` Run the following command to get the storage account in a resource group with a given name: ``` $storageAccount = Get-AzStorageAccount -ResourceGroupName -Name ``` Run the following command to get the redundancy setting for the storage account: ``` $storageAccount.SKU.Name ``` Ensure that the command returns `Standard_GRS`. Repeat for each storage account. **Audit from Azure Policy** If referencing a digital copy of this Benchmark, clicking a Policy ID will open a link to the associated Policy definition in Azure. If referencing a printed copy, you can search Policy IDs from this URL: https://portal.azure.com/#view/Microsoft_Azure_Policy/PolicyMenuBlade/~/Definitions - **Policy ID:** [bf045164-79ba-4215-8f95-f8048dc1780b](https://portal.azure.com/#view/Microsoft_Azure_Policy/PolicyDetailBlade/definitionId/%2Fproviders%2FMicrosoft.Authorization%2FpolicyDefinitions%2Fbf045164-79ba-4215-8f95-f8048dc1780b) **- Name:** 'Geo-redundant storage should be enabled for Storage Accounts'",
+ "AdditionalInformation": "When choosing the best redundancy option, weigh the trade-offs between lower costs and higher availability. Key factors to consider include: - The method of data replication within the primary region. - The replication of data from a primary to a geographically distant secondary region for protection against regional disasters (geo-replication). - The necessity for read access to replicated data in the secondary region during an outage in the primary region (geo-replication with read access).",
+ "References": "https://learn.microsoft.com/en-us/azure/storage/common/storage-redundancy:https://learn.microsoft.com/en-us/azure/storage/common/redundancy-migration:https://learn.microsoft.com/en-us/cli/azure/storage/account?view=azure-cli-latest#az-storage-account-update:https://learn.microsoft.com/en-us/powershell/module/az.storage/set-azstorageaccount?view=azps-12.4.0:https://learn.microsoft.com/en-us/azure/storage/common/storage-disaster-recovery-guidance",
+ "DefaultValue": "When creating a storage account in the Azure Portal, the default redundancy setting is geo-redundant storage (GRS). Using the Azure CLI, the default is read-access geo-redundant storage (RA-GRS). In PowerShell, a redundancy level must be explicitly specified during account creation."
+ }
+ ]
+ },
+ {
+ "Id": "6.12",
+ "Description": "Ensure that 'User consent for applications' is set to 'Do not allow user consent'",
+ "Checks": [
+ "entra_policy_restricts_user_consent_for_apps"
+ ],
+ "Attributes": [
+ {
+ "Section": "6 Identity Services",
+ "Profile": "Level 1",
+ "AssessmentStatus": "Manual",
+ "Description": "Require administrators to provide consent for applications before use.",
+ "RationaleStatement": "If Microsoft Entra ID is running as an identity provider for third-party applications, permissions and consent should be limited to administrators or pre-approved. Malicious applications may attempt to exfiltrate data or abuse privileged user accounts.",
+ "ImpactStatement": "Enforcing this setting may create additional requests that administrators need to review.",
+ "RemediationProcedure": "**Remediate from Azure Portal** 1. From Azure Home select the Portal Menu 1. Select `Microsoft Entra ID` 1. Under `Manage`, select `Enterprise applications` 1. Under `Security`, select `Consent and permissions` 1. Under `Manage`, select `User consent settings` 1. Set `User consent for applications` to `Do not allow user consent` 1. Click `Save`",
+ "AuditProcedure": "**Audit from Azure Portal** 1. From Azure Home select the Portal Menu 1. Select `Microsoft Entra ID` 1. Under `Manage`, select `Enterprise applications` 1. Under `Security`, select `Consent and permissions` 1. Under `Manage`, select `User consent settings` 1. Ensure `User consent for applications` is set to `Do not allow user consent` **Audit from PowerShell** ``` Connect-MgGraph (Get-MgPolicyAuthorizationPolicy).DefaultUserRolePermissions | Select-Object -ExpandProperty PermissionGrantPoliciesAssigned ``` If the command returns no values in response, the configuration complies with the recommendation.",
+ "AdditionalInformation": "",
+ "References": "https://learn.microsoft.com/en-us/entra/identity/enterprise-apps/configure-user-consent?pivots=ms-powershell#configure-user-consent-to-applications:https://learn.microsoft.com/en-us/security/benchmark/azure/mcsb-privileged-access#pa-1-separate-and-limit-highly-privilegedadministrative-users:https://learn.microsoft.com/en-us/security/benchmark/azure/mcsb-governance-strategy#gs-2-define-and-implement-enterprise-segmentationseparation-of-duties-strategy:https://learn.microsoft.com/en-us/security/benchmark/azure/mcsb-governance-strategy#gs-6-define-and-implement-identity-and-privileged-access-strategy",
+ "DefaultValue": "By default, `Users consent for applications` is set to `Allow user consent for apps`."
+ }
+ ]
+ },
+ {
+ "Id": "6.13",
+ "Description": "Ensure that 'User consent for applications' is set to 'Allow user consent for apps from verified publishers, for selected permissions'",
+ "Checks": [
+ "entra_policy_user_consent_for_verified_apps"
+ ],
+ "Attributes": [
+ {
+ "Section": "6 Identity Services",
+ "Profile": "Level 2",
+ "AssessmentStatus": "Manual",
+ "Description": "Allow users to provide consent for selected permissions when a request is coming from a verified publisher.",
+ "RationaleStatement": "If Microsoft Entra ID is running as an identity provider for third-party applications, permissions and consent should be limited to administrators or pre-approved. Malicious applications may attempt to exfiltrate data or abuse privileged user accounts.",
+ "ImpactStatement": "Enforcing this setting may create additional requests that administrators need to review.",
+ "RemediationProcedure": "**Remediate from Azure Portal** 1. From Azure Home select the Portal Menu 1. Select `Microsoft Entra ID` 1. Under `Manage`, select `Enterprise applications` 1. Under `Security, select `Consent and permissions` 1. Under `Manage`, select `User consent settings` 1. Under `User consent for applications`, select `Allow user consent for apps from verified publishers, for selected permissions` 1. Click `Save`",
+ "AuditProcedure": "**Audit from Azure Portal** 1. From Azure Home select the Portal Menu 1. Select `Microsoft Entra ID` 1. Under `Manage`, select `Enterprise applications` 1. Under `Security, select `Consent and permissions` 1. Under `Manage`, select `User consent settings` 1. Under `User consent for applications`, ensure `Allow user consent for apps from verified publishers, for selected permissions` is selected **Audit from PowerShell** ``` Connect-MgGraph (Get-MgPolicyAuthorizationPolicy).DefaultUserRolePermissions | Select-Object -ExpandProperty PermissionGrantPoliciesAssigned ``` The command should return either `ManagePermissionGrantsForSelf.microsoft-user-default-low` or a custom app consent policy id if one is in use.",
+ "AdditionalInformation": "",
+ "References": "https://learn.microsoft.com/en-us/entra/identity/enterprise-apps/configure-user-consent?pivots=ms-graph#configure-user-consent-to-applications:https://learn.microsoft.com/en-us/security/benchmark/azure/mcsb-privileged-access#pa-1-separate-and-limit-highly-privilegedadministrative-users:https://learn.microsoft.com/en-us/security/benchmark/azure/mcsb-governance-strategy#gs-2-define-and-implement-enterprise-segmentationseparation-of-duties-strategy:https://learn.microsoft.com/en-us/security/benchmark/azure/mcsb-governance-strategy#gs-6-define-and-implement-identity-and-privileged-access-strategy:https://learn.microsoft.com/en-us/powershell/module/microsoft.graph.identity.signins/get-mgpolicyauthorizationpolicy?view=graph-powershell-1.0",
+ "DefaultValue": "By default, `User consent for applications` is set to `Allow user consent for apps`."
+ }
+ ]
+ },
+ {
+ "Id": "6.14",
+ "Description": "Ensure that 'Users can register applications' is set to 'No'",
+ "Checks": [
+ "entra_policy_ensure_default_user_cannot_create_apps"
+ ],
+ "Attributes": [
+ {
+ "Section": "6 Identity Services",
+ "Profile": "Level 1",
+ "AssessmentStatus": "Automated",
+ "Description": "Require administrators or appropriately delegated users to register third-party applications.",
+ "RationaleStatement": "It is recommended to only allow an administrator to register custom-developed applications. This ensures that the application undergoes a formal security review and approval process prior to exposing Microsoft Entra ID data. Certain users like developers or other high-request users may also be delegated permissions to prevent them from waiting on an administrative user. Your organization should review your policies and decide your needs.",
+ "ImpactStatement": "Enforcing this setting will create additional requests for approval that will need to be addressed by an administrator. If permissions are delegated, a user may approve a malevolent third party application, potentially giving it access to your data.",
+ "RemediationProcedure": "**Remediate from Azure Portal** 1. From Azure Home select the Portal Menu 1. Select `Microsoft Entra ID` 1. Under `Manage`, select `Users` 1. Under `Manage`, select `User settings` 1. Set `Users can register applications` to `No` 1. Click `Save` **Remediate from PowerShell** ``` $param = @{ AllowedToCreateApps = $false } Update-MgPolicyAuthorizationPolicy -DefaultUserRolePermissions $param ```",
+ "AuditProcedure": "**Audit from Azure Portal** 1. From Azure Home select the Portal Menu 1. Select `Microsoft Entra ID` 1. Under `Manage`, select `Users` 1. Under `Manage`, select `User settings` 1. Ensure that `Users can register applications` is set to `No` **Audit from PowerShell** ``` (Get-MgPolicyAuthorizationPolicy).DefaultUserRolePermissions | Format-List AllowedToCreateApps ``` Command should return the value of `False`",
+ "AdditionalInformation": "",
+ "References": "https://learn.microsoft.com/en-us/entra/identity/role-based-access-control/delegate-app-roles#restrict-who-can-create-applications:https://learn.microsoft.com/en-us/entra/identity-platform/how-applications-are-added#who-has-permission-to-add-applications-to-my-azure-ad-instance:https://learn.microsoft.com/en-us/security/benchmark/azure/mcsb-governance-strategy#gs-6-define-and-implement-identity-and-privileged-access-strategy:https://learn.microsoft.com/en-us/security/benchmark/azure/mcsb-privileged-access#pa-1-separate-and-limit-highly-privilegedadministrative-users:https://learn.microsoft.com/en-us/powershell/module/microsoft.graph.identity.signins/get-mgpolicyauthorizationpolicy?view=graph-powershell-1.0",
+ "DefaultValue": "By default, `Users can register applications` is set to Yes."
+ }
+ ]
+ },
+ {
+ "Id": "6.15",
+ "Description": "Ensure that 'Guest users access restrictions' is set to 'Guest user access is restricted to properties and memberships of their own directory objects'",
+ "Checks": [
+ "entra_policy_guest_users_access_restrictions"
+ ],
+ "Attributes": [
+ {
+ "Section": "6 Identity Services",
+ "Profile": "Level 1",
+ "AssessmentStatus": "Automated",
+ "Description": "Limit guest user permissions.",
+ "RationaleStatement": "Limiting guest access ensures that guest accounts do not have permission for certain directory tasks, such as enumerating users, groups or other directory resources, and cannot be assigned to administrative roles in your directory. Guest access has three levels of restriction. 1. Guest users have the same access as members (most inclusive), 2. Guest users have limited access to properties and memberships of directory objects (default value), 3. Guest user access is restricted to properties and memberships of their own directory objects (most restrictive). The recommended option is the 3rd, most restrictive: Guest user access is restricted to their own directory object.",
+ "ImpactStatement": "This may create additional requests for permissions to access resources that administrators will need to approve. According to https://learn.microsoft.com/en-us/azure/active-directory/enterprise-users/users-restrict-guest-permissions#services-currently-not-supported Service without current support might have compatibility issues with the new guest restriction setting. - Forms - Project - Yammer - Planner in SharePoint",
+ "RemediationProcedure": "**Remediate from Azure Portal** 1. From Azure Home select the Portal Menu 1. Select `Microsoft Entra ID` 1. Under `Manage`, select `External Identities` 1. Select `External collaboration settings` 1. Under `Guest user access`, set `Guest user access restrictions` to `Guest user access is restricted to properties and memberships of their own directory objects` 1. Click `Save` **Remediate from PowerShell** 1. Enter the following to update the policy ID: ``` Update-MgPolicyAuthorizationPolicy -GuestUserRoleId 2af84b1e-32c8-42b7-82bc-daa82404023b ``` 1. Check the GuestUserRoleId again: ``` (Get-MgPolicyAuthorizationPolicy).GuestUserRoleId ``` 1. Ensure that the GuestUserRoleId is equal to the earlier entered value of `2af84b1e-32c8-42b7-82bc-daa82404023b`.",
+ "AuditProcedure": "**Audit from Azure Portal** 1. From Azure Home select the Portal Menu 1. Select `Microsoft Entra ID` 1. Under `Manage`, select `External Identities` 1. Select `External collaboration settings` 1. Under `Guest user access`, ensure that `Guest user access restrictions ` is set to `Guest user access is restricted to properties and memberships of their own directory objects` **Audit from PowerShell** 1. Enter the following: ``` Connect-MgGraph (Get-MgPolicyAuthorizationPolicy).GuestUserRoleId ``` Which will give a result like: ``` Id : authorizationPolicy OdataType : Description : Used to manage authorization related settings across the company. DisplayName : Authorization Policy EnabledPreviewFeatures : {} GuestUserRoleId : 10dae51f-b6af-4016-8d66-8c2a99b929b3 PermissionGrantPolicyIdsAssignedToDefaultUserRole : {user-default-legacy} ``` If the GuestUserRoleID property does not equal `2af84b1e-32c8-42b7-82bc-daa82404023b` then it is not set to most restrictive.",
+ "AdditionalInformation": "",
+ "References": "https://learn.microsoft.com/en-us/entra/fundamentals/users-default-permissions#member-and-guest-users:https://learn.microsoft.com/en-us/security/benchmark/azure/mcsb-privileged-access#pa-3-manage-lifecycle-of-identities-and-entitlements:https://learn.microsoft.com/en-us/security/benchmark/azure/mcsb-governance-strategy#gs-2-define-and-implement-enterprise-segmentationseparation-of-duties-strategy:https://learn.microsoft.com/en-us/security/benchmark/azure/mcsb-governance-strategy#gs-6-define-and-implement-identity-and-privileged-access-strategy:https://learn.microsoft.com/en-us/entra/identity/users/users-restrict-guest-permissions",
+ "DefaultValue": "By default, `Guest user access restrictions` is set to `Guest users have limited access to properties and memberships of directory objects`."
+ }
+ ]
+ },
+ {
+ "Id": "6.16",
+ "Description": "Ensure that 'Guest invite restrictions' is set to 'Only users assigned to specific admin roles can invite guest users'",
+ "Checks": [
+ "entra_policy_guest_invite_only_for_admin_roles"
+ ],
+ "Attributes": [
+ {
+ "Section": "6 Identity Services",
+ "Profile": "Level 2",
+ "AssessmentStatus": "Automated",
+ "Description": "Restrict invitations to users with specific administrative roles only.",
+ "RationaleStatement": "Restricting invitations to users with specific administrator roles ensures that only authorized accounts have access to cloud resources. This helps to maintain Need to Know permissions and prevents inadvertent access to data. By default the setting `Guest invite restrictions` is set to `Anyone in the organization can invite guest users including guests and non-admins`. This would allow anyone within the organization to invite guests and non-admins to the tenant, posing a security risk.",
+ "ImpactStatement": "With the option of `Only users assigned to specific admin roles can invite guest users` selected, users with specific admin roles will be in charge of sending invitations to the external users, requiring additional overhead by them to manage user accounts. This will mean coordinating with other departments as they are onboarding new users.",
+ "RemediationProcedure": "**Remediate from Azure Portal** 1. From Azure Home select the Portal Menu 1. Select `Microsoft Entra ID` 1. Under `Manage`, select `External Identities` 1. Select `External collaboration settings` 1. Under `Guest invite settings`, set `Guest invite restrictions`, to `Only users assigned to specific admin roles can invite guest users` 1. Click `Save` **Remediate from Powershell** Enter the following: ``` Connect-MgGraph Update-MgPolicyAuthorizationPolicy -AllowInvitesFrom adminsAndGuestInviters ```",
+ "AuditProcedure": "**Audit from Azure Portal** 1. From Azure Home select the Portal Menu 1. Select `Microsoft Entra ID` 1. Under `Manage`, select `External Identities` 1. Select `External collaboration settings` 1. Under `Guest invite settings`, for `Guest invite restrictions`, ensure that `Only users assigned to specific admin roles can invite guest users` is selected Note: This setting has 4 levels of restriction, which include: - Anyone in the organization can invite guest users including guests and non-admins (most inclusive), - Member users and users assigned to specific admin roles can invite guest users including guests with member permissions, - Only users assigned to specific admin roles can invite guest users, - No one in the organization can invite guest users including admins (most restrictive). **Audit from Powershell** Enter the following: ``` Connect-MgGraph (Get-MgPolicyAuthorizationPolicy).AllowInvitesFrom ``` If the resulting value is `adminsAndGuestInviters` the configuration complies.",
+ "AdditionalInformation": "",
+ "References": "https://learn.microsoft.com/en-us/entra/external-id/external-collaboration-settings-configure:https://learn.microsoft.com/en-us/security/benchmark/azure/mcsb-governance-strategy#gs-6-define-and-implement-identity-and-privileged-access-strategy:https://learn.microsoft.com/en-us/security/benchmark/azure/mcsb-governance-strategy#gs-2-define-and-implement-enterprise-segmentationseparation-of-duties-strategy:https://learn.microsoft.com/en-us/security/benchmark/azure/mcsb-privileged-access#pa-3-manage-lifecycle-of-identities-and-entitlements:https://learn.microsoft.com/en-us/powershell/module/microsoft.graph.identity.signins/update-mgpolicyauthorizationpolicy?view=graph-powershell-1.0",
+ "DefaultValue": "By default, `Guest invite restrictions` is set to `Anyone in the organization can invite guest users including guests and non-admins`"
+ }
+ ]
+ },
+ {
+ "Id": "10.3.1.3",
+ "Description": "Ensure 'Allow storage account key access' for Azure Storage Accounts is 'Disabled'",
+ "Checks": [
+ "storage_account_key_access_disabled"
+ ],
+ "Attributes": [
+ {
+ "Section": "10 Storage Services",
+ "SubSection": "10.3 Storage Accounts",
+ "Profile": "Level 1",
+ "AssessmentStatus": "Automated",
+ "Description": "Every secure request to an Azure Storage account must be authorized. By default, requests can be authorized with either Microsoft Entra credentials or by using the account access key for Shared Key authorization.",
+ "RationaleStatement": "Microsoft Entra ID provides superior security and ease of use compared to Shared Key and is recommended by Microsoft. To require clients to use Microsoft Entra ID for authorizing requests, you can disallow requests to the storage account that are authorized with Shared Key.",
+ "ImpactStatement": "When you disallow Shared Key authorization for a storage account, any requests to the account that are authorized with Shared Key, including shared access signatures (SAS), will be denied. Client applications that currently access the storage account using the Shared Key will no longer function.",
+ "RemediationProcedure": "**Remediate from Azure Portal** 1. Go to `Storage accounts`. 1. Click on a storage account. 1. Under `Settings`, click `Configuration`. 1. Under `Allow storage account key access`, click the radio button next to `Disabled`. 1. Click `Save`. 1. Repeat steps 1-5 for each storage account requiring remediation. **Remediate from Azure CLI** For each storage account requiring remediation, run the following command to disallow shared key authorization: ``` az storage account update --resource-group --name --allow-shared-key-access false ``` **Remediate from PowerShell** For each storage account requiring remediation, run the following command to disallow shared key authorization: ``` Set-AzStorageAccount -ResourceGroupName -Name -AllowSharedKeyAccess $false ```",
+ "AuditProcedure": "**Audit from Azure Portal** 1. Go to `Storage accounts`. 1. Click on a storage account. 1. Under `Settings`, click `Configuration`. 1. Under `Allow storage account key access`, ensure that the radio button next to `Disabled` is selected. 1. Repeat steps 1-4 for each storage account. **Audit from Azure CLI** Run the following command to list storage accounts: ``` az storage account list ``` For each storage account, run the following command: ``` az storage account show --resource-group --name ``` Ensure that `allowSharedKeyAccess` is set to `false`. **Audit from PowerShell** Run the following command to list storage accounts: ``` Get-AzStorageAccount ``` Run the following command to get the storage account in a resource group with a given name: ``` $storageAccount = Get-AzStorageAccount -ResourceGroupName -Name ``` Run the following command to get the shared key access setting for the storage account: ``` $storageAccount.allowSharedKeyAccess ``` Ensure that the command returns `False`. Repeat for each storage account. **Audit from Azure Policy** If referencing a digital copy of this Benchmark, clicking a Policy ID will open a link to the associated Policy definition in Azure. If referencing a printed copy, you can search Policy IDs from this URL: https://portal.azure.com/#view/Microsoft_Azure_Policy/PolicyMenuBlade/~/Definitions - **Policy ID:** [8c6a50c6-9ffd-4ae7-986f-5fa6111f9a54](https://portal.azure.com/#view/Microsoft_Azure_Policy/PolicyDetailBlade/definitionId/%2Fproviders%2FMicrosoft.Authorization%2FpolicyDefinitions%2F8c6a50c6-9ffd-4ae7-986f-5fa6111f9a54) **- Name:** 'Storage accounts should prevent shared key access'",
+ "AdditionalInformation": "",
+ "References": "https://learn.microsoft.com/en-us/azure/storage/common/shared-key-authorization-prevent:https://learn.microsoft.com/en-us/cli/azure/storage/account:https://learn.microsoft.com/en-us/powershell/module/az.storage/get-azstorageaccount:https://learn.microsoft.com/en-us/powershell/module/az.storage/set-azstorageaccount",
+ "DefaultValue": "The AllowSharedKeyAccess property of a storage account is not set by default and does not return a value until you explicitly set it. The storage account permits requests that are authorized with the Shared Key when the property value is **null** or when it is **true**."
+ }
+ ]
+ },
+ {
+ "Id": "10.3.3.1",
+ "Description": "Ensure that 'Default to Microsoft Entra authorization in the Azure portal' is set to 'Enabled'",
+ "Checks": [
+ "storage_default_to_entra_authorization_enabled"
+ ],
+ "Attributes": [
+ {
+ "Section": "10 Storage Services",
+ "SubSection": "10.3 Storage Accounts",
+ "Profile": "Level 1",
+ "AssessmentStatus": "Automated",
+ "Description": "When this property is enabled, the Azure portal authorizes requests to blobs, files, queues, and tables with Microsoft Entra ID by default.",
+ "RationaleStatement": "Microsoft Entra ID provides superior security and ease of use over Shared Key.",
+ "ImpactStatement": "",
+ "RemediationProcedure": "**Remediate from Azure Portal** 1. Go to `Storage accounts`. 1. Click the name of a storage account. 1. Under `Settings`, click `Configuration`. 1. Under `Default to Microsoft Entra authorization in the Azure portal`, click the radio button next to `Enabled`. 1. Click `Save`. 1. Repeat steps 1-5 for each storage account requiring remediation. **Remediate from Azure CLI** For each storage account requiring remediation, run the following command to enable `defaultToOAuthAuthentication`: ``` az storage account update --resource-group --name --set defaultToOAuthAuthentication=true ```",
+ "AuditProcedure": "**Audit from Azure Portal** 1. Go to `Storage accounts`. 1. Click the name of a storage account. 1. Under `Settings`, click `Configuration`. 1. Ensure that `Default to Microsoft Entra authorization in the Azure portal` is set to `Enabled`. 1. Repeat steps 1-4 for each storage account. **Audit from Azure CLI** Run the following command to get the `name` and `defaultToOAuthAuthentication` setting for each storage account: ``` az storage account list --query [*].[name,defaultToOAuthAuthentication] ``` Ensure that `true` is returned for each storage account.",
+ "AdditionalInformation": "",
+ "References": "https://learn.microsoft.com/en-us/azure/storage/blobs/authorize-data-operations-portal#default-to-microsoft-entra-authorization-in-the-azure-portal:https://learn.microsoft.com/en-us/cli/azure/storage/account?view=azure-cli-latest",
+ "DefaultValue": "By default, `defaultToOAuthAuthentication` is disabled."
+ }
+ ]
+ },
+ {
+ "Id": "6.19",
+ "Description": "Ensure that 'Users can create security groups in Azure portals, API or PowerShell' is set to 'No'",
+ "Checks": [
+ "entra_policy_default_users_cannot_create_security_groups"
+ ],
+ "Attributes": [
+ {
+ "Section": "6 Identity Services",
+ "Profile": "Level 2",
+ "AssessmentStatus": "Manual",
+ "Description": "Restrict security group creation to administrators only.",
+ "RationaleStatement": "When creating security groups is enabled, all users in the directory are allowed to create new security groups and add members to those groups. Unless a business requires this day-to-day delegation, security group creation should be restricted to administrators only.",
+ "ImpactStatement": "Enabling this setting could create a number of requests that would need to be managed by an administrator.",
+ "RemediationProcedure": "**Remediate from Azure Portal** 1. From Azure Home select the Portal Menu 1. Select `Microsoft Entra ID` 1. Under `Manage`, select `Groups` 1. Under `Settings`, select `General` 1. Under `Security Groups`, set `Users can create security groups in Azure portals, API or PowerShell` to `No` 1. Click `Save`",
+ "AuditProcedure": "**Audit from Azure Portal** 1. From Azure Home select the Portal Menu 1. Select `Microsoft Entra ID` 1. Under `Manage`, select `Groups` 1. Under `Settings`, select `General` 1. Under `Security Groups`, ensure that `Users can create security groups in Azure portals, API or PowerShell` is set to `No`",
+ "AdditionalInformation": "",
+ "References": "https://learn.microsoft.com/en-us/entra/identity/users/groups-self-service-management#making-a-group-available-for-end-user-self-service:https://learn.microsoft.com/en-us/security/benchmark/azure/mcsb-governance-strategy#gs-6-define-and-implement-identity-and-privileged-access-strategy:https://learn.microsoft.com/en-us/security/benchmark/azure/mcsb-governance-strategy#gs-2-define-and-implement-enterprise-segmentationseparation-of-duties-strategy:https://learn.microsoft.com/en-us/security/benchmark/azure/mcsb-privileged-access#pa-1-separate-and-limit-highly-privilegedadministrative-users:https://learn.microsoft.com/en-us/security/benchmark/azure/mcsb-privileged-access#pa-3-manage-lifecycle-of-identities-and-entitlements",
+ "DefaultValue": "By default, `Users can create security groups in Azure portals, API or PowerShell` is set to `Yes`"
+ }
+ ]
+ },
+ {
+ "Id": "2.1.1.1.1",
+ "Description": "Ensure Critical Data is Encrypted with Microsoft Managed Keys (MMK)",
+ "Checks": [],
+ "Attributes": [
+ {
+ "Section": "2 Common Reference Recommendations",
+ "SubSection": "2.1 Secrets and Keys",
+ "Profile": "Level 1",
+ "AssessmentStatus": "Manual",
+ "Description": "Microsoft Managed Keys (MMK) (also known as Platform-managed keys (PMK)) provides a very low overhead method of encrypting data at rest and implementing encryption key management. Keys maintained in an MMK implementation are automatically managed by Azure and require no customer interaction.",
+ "RationaleStatement": "The encryption of data at rest is a foundational component of data security. Data at rest without encryption is easily compromised through loss or theft. Encrypting data at rest introduces confidentiality to the data by obfuscating the data contents with a cipher algorithm and provides an authentication requirement through the use of cryptographic keys. MMK makes the encryption of data at rest very easy to implement and maintain.",
+ "ImpactStatement": "",
+ "RemediationProcedure": "",
+ "AuditProcedure": "",
+ "AdditionalInformation": "",
+ "References": "https://docs.microsoft.com/en-us/azure/security/fundamentals/data-encryption-best-practices#protect-data-at-rest:https://learn.microsoft.com/en-us/security/benchmark/azure/mcsb-data-protection#dp-5-use-customer-managed-key-option-in-data-at-rest-encryption-when-required:https://learn.microsoft.com/en-us/azure/security/fundamentals/key-management",
+ "DefaultValue": "By default, Encryption type is set to Microsoft Managed Keys."
+ }
+ ]
+ },
+ {
+ "Id": "6.21",
+ "Description": "Ensure that 'Users can create Microsoft 365 groups in Azure portals, API or PowerShell' is set to 'No'",
+ "Checks": [
+ "entra_users_cannot_create_microsoft_365_groups"
+ ],
+ "Attributes": [
+ {
+ "Section": "6 Identity Services",
+ "Profile": "Level 2",
+ "AssessmentStatus": "Manual",
+ "Description": "Restrict Microsoft 365 group creation to administrators only.",
+ "RationaleStatement": "Restricting Microsoft 365 group creation to administrators only ensures that creation of Microsoft 365 groups is controlled by the administrator. Appropriate groups should be created and managed by the administrator and group creation rights should not be delegated to any other user.",
+ "ImpactStatement": "Enabling this setting could create a number of requests that would need to be managed by an administrator.",
+ "RemediationProcedure": "**Remediate from Azure Portal** 1. From Azure Home select the Portal Menu 1. Select `Microsoft Entra ID` 1. Under `Manage`, select `Groups` 1. Under `Settings`, select `General` 1. Under `Microsoft 365 Groups`, set `Users can create Microsoft 365 groups in Azure portals, API or PowerShell` to `No` 1. Click `Save`",
+ "AuditProcedure": "**Audit from Azure Portal** 1. From Azure Home select the Portal Menu 1. Select `Microsoft Entra ID` 1. Under `Manage`, select `Groups` 1. Under `Settings`, select `General` 1. Under `Microsoft 365 Groups`, ensure that `Users can create Microsoft 365 groups in Azure portals, API or PowerShell` is set to `No`",
+ "AdditionalInformation": "",
+ "References": "https://learn.microsoft.com/en-us/microsoft-365/solutions/manage-creation-of-groups?view=o365-worldwide&redirectSourcePath=%252fen-us%252farticle%252fControl-who-can-create-Office-365-Groups-4c46c8cb-17d0-44b5-9776-005fced8e618:https://learn.microsoft.com/en-us/security/benchmark/azure/mcsb-governance-strategy#gs-6-define-and-implement-identity-and-privileged-access-strategy:https://learn.microsoft.com/en-us/security/benchmark/azure/mcsb-governance-strategy#gs-2-define-and-implement-enterprise-segmentationseparation-of-duties-strategy:https://learn.microsoft.com/en-us/security/benchmark/azure/mcsb-privileged-access#pa-1-separate-and-limit-highly-privilegedadministrative-users:https://learn.microsoft.com/en-us/security/benchmark/azure/mcsb-privileged-access#pa-3-manage-lifecycle-of-identities-and-entitlements",
+ "DefaultValue": "By default, `Users can create Microsoft 365 groups in Azure portals, API or PowerShell` is set to `Yes`."
+ }
+ ]
+ },
+ {
+ "Id": "3.1.2",
+ "Description": "Ensure that network security groups are configured for Databricks subnets",
+ "Checks": [],
+ "Attributes": [
+ {
+ "Section": "3 Analytics Services",
+ "SubSection": "3.1 Azure Databricks",
+ "Profile": "Level 1",
+ "AssessmentStatus": "Manual",
+ "Description": "Network Security Groups (NSGs) should be implemented to control inbound and outbound traffic to Azure Databricks subnets, ensuring only authorized communication. NSGs should be configured with deny rules to block unwanted traffic and restrict communication to essential sources only.",
+ "RationaleStatement": "",
+ "ImpactStatement": "* NSGs require periodic maintenance to ensure rule accuracy. * Misconfigured NSGs could inadvertently block required traffic.",
+ "RemediationProcedure": "**Remediate from Azure Portal** 1. Assign NSG to Databricks subnets under Networking > NSG Settings.",
+ "AuditProcedure": "**Audit from Azure Portal** 1. Navigate to Virtual Networks > Subnets, and review NSG assignments. **Audit from Azure CLI** ``` az network nsg list --query [].{Name:name, Rules:securityRules} ``` **Audit from PowerShell** ``` Get-AzNetworkSecurityGroup -ResourceGroupName ```",
+ "AdditionalInformation": "",
+ "References": "https://learn.microsoft.com/en-us/security/benchmark/azure/baselines/azure-databricks-security-baseline:https://learn.microsoft.com/en-us/azure/databricks/security/network/classic/vnet-inject#network-security-group-rules",
+ "DefaultValue": "By default, Databricks subnets do not have NSGs assigned."
+ }
+ ]
+ },
+ {
+ "Id": "6.23",
+ "Description": "Ensure that no custom subscription administrator roles exist",
+ "Checks": [
+ "iam_subscription_roles_owner_custom_not_created"
+ ],
+ "Attributes": [
+ {
+ "Section": "6 Identity Services",
+ "Profile": "Level 1",
+ "AssessmentStatus": "Automated",
+ "Description": "The principle of least privilege should be followed and only necessary privileges should be assigned instead of allowing full administrative access.",
+ "RationaleStatement": "Custom roles in Azure with administrative access can obfuscate the permissions granted and introduce complexity and blind spots to the management of privileged identities. For less mature security programs without regular identity audits, the creation of Custom roles should be avoided entirely. For more mature security programs with regular identity audits, Custom Roles should be audited for use and assignment, used minimally, and the principle of least privilege should be observed when granting permissions",
+ "ImpactStatement": "Subscriptions will need to be handled by Administrators with permissions.",
+ "RemediationProcedure": "**Remediate from Azure Portal** 1. From Azure Home select the Portal Menu. 1. Select `Subscriptions`. 1. Select a subscription. 1. Select `Access control (IAM)`. 1. Select `Roles`. 1. Click `Type` and select `Custom role` from the drop-down menu. 1. Check the box next to each role which grants subscription administrator privileges. 1. Select `Delete`. 1. Select `Yes`. **Remediate from Azure CLI** List custom roles: ``` az role definition list --custom-role-only True ``` Check for entries with `assignableScope` of the `subscription`, and an action of `*`. To remove a violating role: ``` az role definition delete --name ``` Note that any role assignments must be removed before a custom role can be deleted. Ensure impact is assessed before deleting a custom role granting subscription administrator privileges.",
+ "AuditProcedure": "**Audit from Azure Portal** 1. From Azure Home select the Portal Menu. 1. Select `Subscriptions`. 1. Select a subscription. 1. Select `Access control (IAM)`. 1. Select `Roles`. 1. Click `Type` and select `Custom role` from the drop-down menu. 1. Select `View` next to a role. 1. Select `JSON`. 1. Check for `assignableScopes` set to the subscription, and `actions` set to `*`. 1. Repeat steps 7-9 for each custom role. **Audit from Azure CLI** List custom roles: ``` az role definition list --custom-role-only True ``` Check for entries with `assignableScope` of the `subscription`, and an action of `*` **Audit from PowerShell** ``` Connect-AzAccount Get-AzRoleDefinition |Where-Object {($_.IsCustom -eq $true) -and ($_.Actions.contains('*'))} ``` Check the output for `AssignableScopes` value set to the subscription. **Audit from Azure Policy** If referencing a digital copy of this Benchmark, clicking a Policy ID will open a link to the associated Policy definition in Azure. If referencing a printed copy, you can search Policy IDs from this URL: https://portal.azure.com/#view/Microsoft_Azure_Policy/PolicyMenuBlade/~/Definitions - **Policy ID:** [a451c1ef-c6ca-483d-87ed-f49761e3ffb5](https://portal.azure.com/#view/Microsoft_Azure_Policy/PolicyDetailBlade/definitionId/%2Fproviders%2FMicrosoft.Authorization%2FpolicyDefinitions%2Fa451c1ef-c6ca-483d-87ed-f49761e3ffb5) **- Name:** 'Audit usage of custom RBAC roles'",
+ "AdditionalInformation": "",
+ "References": "https://docs.microsoft.com/en-us/azure/billing/billing-add-change-azure-subscription-administrator:https://learn.microsoft.com/en-us/security/benchmark/azure/mcsb-privileged-access#pa-1-separate-and-limit-highly-privilegedadministrative-users:https://learn.microsoft.com/en-us/security/benchmark/azure/mcsb-privileged-access#pa-3-manage-lifecycle-of-identities-and-entitlements:https://learn.microsoft.com/en-us/security/benchmark/azure/mcsb-governance-strategy#gs-2-define-and-implement-enterprise-segmentationseparation-of-duties-strategy:https://learn.microsoft.com/en-us/security/benchmark/azure/mcsb-governance-strategy#gs-6-define-and-implement-identity-and-privileged-access-strategy:https://learn.microsoft.com/en-us/security/benchmark/azure/mcsb-privileged-access#pa-7-follow-just-enough-administration-least-privilege-principle",
+ "DefaultValue": "By default, no custom owner roles are created."
+ }
+ ]
+ },
+ {
+ "Id": "6.24",
+ "Description": "Ensure that a custom role is assigned permissions for administering resource locks",
+ "Checks": [
+ "iam_custom_role_has_permissions_to_administer_resource_locks"
+ ],
+ "Attributes": [
+ {
+ "Section": "6 Identity Services",
+ "Profile": "Level 2",
+ "AssessmentStatus": "Manual",
+ "Description": "Resource locking is a powerful protection mechanism that can prevent inadvertent modification or deletion of resources within Azure subscriptions and resource groups, and it is a recommended NIST configuration.",
+ "RationaleStatement": "Given that the resource lock functionality is outside of standard Role-Based Access Control (RBAC), it would be prudent to create a resource lock administrator role to prevent inadvertent unlocking of resources.",
+ "ImpactStatement": "By adding this role, specific permissions may be granted for managing only resource locks rather than needing to provide the broad Owner or User Access Administrator role, reducing the risk of the user being able to cause unintentional damage.",
+ "RemediationProcedure": "**Remediate from Azure Portal** 1. In the Azure portal, navigate to a subscription or resource group. 1. Click `Access control (IAM)`. 1. Click `+ Add`. 1. Click `Add custom role`. 1. In the `Custom role name` field enter `Resource Lock Administrator`. 1. In the `Description` field enter `Can Administer Resource Locks`. 1. For `Baseline permissions` select `Start from scratch`. 1. Click `Next`. 1. Click `Add permissions`. 1. In the `Search for a permission` box, type `Microsoft.Authorization/locks`. 1. Click the result. 1. Check the box next to `Permission`. 1. Click `Add`. 1. Click `Review + create`. 1. Click `Create`. 1. Click `OK`. 1. Click `+ Add`. 1. Click `Add role assignment`. 1. In the `Search by role name, description, permission, or ID` box, type `Resource Lock Administrator`. 1. Select the role. 1. Click `Next`. 1. Click `+ Select members`. 1. Select appropriate members. 1. Click `Select`. 1. Click `Review + assign`. 1. Click `Review + assign` again. 1. Repeat steps 1-26 for each subscription or resource group requiring remediation. **Remediate from PowerShell:** Below is a PowerShell definition for a resource lock administrator role created at an Azure Management group level ``` Import-Module Az.Accounts Connect-AzAccount $role = Get-AzRoleDefinition User Access Administrator $role.Id = $null $role.Name = Resource Lock Administrator $role.Description = Can Administer Resource Locks $role.Actions.Clear() $role.Actions.Add(Microsoft.Authorization/locks/*) $role.AssignableScopes.Clear() * Scope at the Management group level Management group $role.AssignableScopes.Add(/providers/Microsoft.Management/managementGroups/MG-Name) New-AzRoleDefinition -Role $role Get-AzureRmRoleDefinition Resource Lock Administrator ```",
+ "AuditProcedure": "**Audit from Azure Portal** 1. In the Azure portal, navigate to a subscription or resource group. 1. Click `Access control (IAM)`. 1. Click `Roles`. 1. Click `Type : All`. 1. Click to view the drop-down menu. 1. Select `Custom role`. 1. Click `View` in the `Details` column of a custom role. 1. Review the role permissions. 1. Click `Assignments` and review the assignments. 1. Click the `X` to exit the custom role details page. 1. Repeat steps 7-10. Ensure that at least one custom role exists that assigns the `Microsoft.Authorization/locks` permission to appropriate members. 1. Repeat steps 1-11 for each subscription or resource group.",
+ "AdditionalInformation": "",
+ "References": "https://docs.microsoft.com/en-us/azure/role-based-access-control/custom-roles:https://docs.microsoft.com/en-us/azure/role-based-access-control/check-access:https://learn.microsoft.com/en-us/security/benchmark/azure/mcsb-privileged-access#pa-1-separate-and-limit-highly-privilegedadministrative-users:https://learn.microsoft.com/en-us/security/benchmark/azure/mcsb-privileged-access#pa-7-follow-just-enough-administration-least-privilege-principle:https://learn.microsoft.com/en-us/security/benchmark/azure/mcsb-privileged-access#pa-3-manage-lifecycle-of-identities-and-entitlements:https://learn.microsoft.com/en-us/security/benchmark/azure/mcsb-governance-strategy#gs-2-define-and-implement-enterprise-segmentationseparation-of-duties-strategy:https://learn.microsoft.com/en-us/security/benchmark/azure/mcsb-governance-strategy#gs-6-define-and-implement-identity-and-privileged-access-strategy",
+ "DefaultValue": "A role for administering resource locks does not exist by default."
+ }
+ ]
+ },
+ {
+ "Id": "3.1.3",
+ "Description": "Ensure that traffic is encrypted between cluster worker nodes",
+ "Checks": [],
+ "Attributes": [
+ {
+ "Section": "3 Analytics Services",
+ "SubSection": "3.1 Azure Databricks",
+ "Profile": "Level 2",
+ "AssessmentStatus": "Manual",
+ "Description": "By default, data exchanged between worker nodes in an Azure Databricks cluster is not encrypted. To ensure that data is encrypted at all times, whether at rest or in transit, you can create an initialization script that configures your clusters to encrypt traffic between worker nodes using AES 256-bit encryption over a TLS 1.3 connection.",
+ "RationaleStatement": "* Protects sensitive data during transit between cluster nodes, mitigating risks of data interception or unauthorized access. * Aligns with organizational security policies and compliance requirements that mandate encryption of data in transit. * Enhances overall security posture by ensuring that all inter-node communications within the cluster are encrypted.",
+ "ImpactStatement": "* Enabling encryption may introduce a performance penalty due to the computational overhead associated with encrypting and decrypting traffic. This can result in longer query execution times, especially for data-intensive operations. * Implementing encryption requires creating and managing init scripts, which adds complexity to cluster configuration and maintenance. * The shared encryption secret is derived from the hash of the keystore stored in DBFS. If the keystore is updated or rotated, all running clusters must be restarted to prevent authentication failures between Spark workers and drivers.",
+ "RemediationProcedure": "Create a JKS keystore: 1. Generate a Java KeyStore (JKS) file that will be used for SSL/TLS encryption. 2. Upload the keystore file to a secure directory in DBFS (e.g. /dbfs//jetty_ssl_driver_keystore.jks). Develop an init script: 3. Create an init script that performs the following tasks: - Retrieves the JKS keystore file and password. - Derives a shared encryption secret from the keystore. - Configures Spark driver and executor settings to enable encryption. 4. Example init script: ``` #!/bin/bash set -euo pipefail keystore_dbfs_file=/dbfs//jetty_ssl_driver_keystore.jks max_attempts=30 while [ ! -f ${keystore_dbfs_file} ]; do if [ $max_attempts == 0 ]; then echo ERROR: Unable to find the file : $keystore_dbfs_file. Failing the script. exit 1 fi sleep 2s ((max_attempts--)) done sasl_secret=$(sha256sum $keystore_dbfs_file | cut -d' ' -f1) if [ -z ${sasl_secret} ]; then echo ERROR: Unable to derive the secret. Failing the script. exit 1 fi local_keystore_file=$DB_HOME/keys/jetty_ssl_driver_keystore.jks local_keystore_password=gb1gQqZ9ZIHS if [[ $DB_IS_DRIVER = TRUE ]]; then driver_conf=${DB_HOME}/driver/conf/spark-branch.conf echo Configuring driver conf at $driver_conf if [ ! -e $driver_conf ]; then echo spark.authenticate true >> $driver_conf echo spark.authenticate.secret $sasl_secret >> $driver_conf echo spark.authenticate.enableSaslEncryption true >> $driver_conf echo spark.network.crypto.enabled true >> $driver_conf echo spark.network.crypto.keyLength 256 >> $driver_conf echo spark.network.crypto.keyFactoryAlgorithm PBKDF2WithHmacSHA1 >> $driver_conf echo spark.io.encryption.enabled true >> $driver_conf echo spark.ssl.enabled true >> $driver_conf echo spark.ssl.keyPassword $local_keystore_password >> $driver_conf echo spark.ssl.keyStore $local_keystore_file >> $driver_conf echo spark.ssl.keyStorePassword $local_keystore_password >> $driver_conf echo spark.ssl.protocol TLSv1.3 >> $driver_conf fi fi executor_conf=${DB_HOME}/conf/spark.executor.extraJavaOptions echo Configuring executor conf at $executor_conf if [ ! -e $executor_conf ]; then echo -Dspark.authenticate=true >> $executor_conf echo -Dspark.authenticate.secret=$sasl_secret >> $executor_conf echo -Dspark.authenticate.enableSaslEncryption=true >> $executor_conf echo -Dspark.network.crypto.enabled=true >> $executor_conf echo -Dspark.network.crypto.keyLength=256 >> $executor_conf echo -Dspark.network.crypto.keyFactoryAlgorithm=PBKDF2WithHmacSHA1 >> $executor_conf echo -Dspark.io.encryption.enabled=true >> $executor_conf echo -Dspark.ssl.enabled=true >> $executor_conf echo -Dspark.ssl.keyPassword=$local_keystore_password >> $executor_conf echo -Dspark.ssl.keyStore=$local_keystore_file >> $executor_conf echo -Dspark.ssl.keyStorePassword=$local_keystore_password >> $executor_conf echo -Dspark.ssl.protocol=TLSv1.3 >> $executor_conf fi ``` 5. Save.",
+ "AuditProcedure": "**Audit from Azure Portal** Review cluster init scripts: 1. Navigate to your Azure Databricks workspace, go to the Clusters section, select a cluster, and check the Advanced Options for any init scripts that configure encryption settings. Verify spark configuration: 2. Ensure that the following Spark configurations are set: ``` spark.authenticate true spark.authenticate.enableSaslEncryption true spark.network.crypto.enabled true spark.network.crypto.keyLength 256 spark.network.crypto.keyFactoryAlgorithm PBKDF2WithHmacSHA1 spark.io.encryption.enabled true ``` These settings can be found in the cluster's Spark configuration properties. Check keystone management: 3. Verify that the Java KeyStore (JKS) file is securely stored in DBFS and that its integrity is maintained. 4. Ensure that the keystore password is securely managed and not hardcoded in scripts.",
+ "AdditionalInformation": "",
+ "References": "https://learn.microsoft.com/en-us/azure/databricks/security/keys/encrypt-otw",
+ "DefaultValue": "By default, traffic is not encrypted between cluster worker nodes."
+ }
+ ]
+ },
+ {
+ "Id": "6.26",
+ "Description": "Ensure fewer than 5 users have global administrator assignment",
+ "Checks": [
+ "entra_global_admin_in_less_than_five_users"
+ ],
+ "Attributes": [
+ {
+ "Section": "6 Identity Services",
+ "Profile": "Level 1",
+ "AssessmentStatus": "Manual",
+ "Description": "This recommendation aims to maintain a balance between security and operational efficiency by ensuring that a minimum of 2 and a maximum of 4 users are assigned the Global Administrator role in Microsoft Entra ID. Having at least two Global Administrators ensures redundancy, while limiting the number to four reduces the risk of excessive privileged access.",
+ "RationaleStatement": "The Global Administrator role has extensive privileges across all services in Microsoft Entra ID. The Global Administrator role should never be used in regular daily activities; administrators should have a regular user account for daily activities, and a separate account for administrative responsibilities. Limiting the number of Global Administrators helps mitigate the risk of unauthorized access, reduces the potential impact of human error, and aligns with the principle of least privilege to reduce the attack surface of an Azure tenant. Conversely, having at least two Global Administrators ensures that administrative functions can be performed without interruption in case of unavailability of a single admin.",
+ "ImpactStatement": "Implementing this recommendation may require changes in administrative workflows or the redistribution of roles and responsibilities. Adequate training and awareness should be provided to all Global Administrators.",
+ "RemediationProcedure": "**Remediate from Azure Portal** 1. From Azure Home select the Portal Menu 1. Select `Microsoft Entra ID` 1. Under `Manage`, select `Roles and administrators` 1. Under `Administrative Roles`, select `Global Administrator` If more than 4 users are assigned: 1. Remove Global Administrator role for users which do not or no longer require the role. 1. Assign Global Administrator role via PIM which can be activated when required. 1. Assign more granular roles to users to conduct their duties. If only one user is assigned: 1. Provide the Global Administrator role to a trusted user or create a break glass admin account.",
+ "AuditProcedure": "**Audit from Azure Portal** 1. From Azure Home select the Portal Menu 1. Select `Microsoft Entra ID` 1. Under `Manage`, select `Roles and administrators` 1. Under `Administrative Roles`, select `Global Administrator` 1. Ensure less than 5 users are actively assigned the role. 1. Ensure that at least 2 users are actively assigned the role.",
+ "AdditionalInformation": "",
+ "References": "https://learn.microsoft.com/en-us/entra/identity/role-based-access-control/best-practices#5-limit-the-number-of-global-administrators-to-less-than-5:https://learn.microsoft.com/en-us/microsoft-365/admin/add-users/about-admin-roles?view=o365-worldwide#security-guidelines-for-assigning-roles:https://learn.microsoft.com/en-us/entra/identity/role-based-access-control/security-emergency-access:https://learn.microsoft.com/en-us/security/benchmark/azure/mcsb-privileged-access#pa-1-separate-and-limit-highly-privilegedadministrative-users",
+ "DefaultValue": ""
+ }
+ ]
+ },
+ {
+ "Id": "6.1.1",
+ "Description": "Ensure that 'security defaults' is enabled in Microsoft Entra ID",
+ "Checks": [
+ "entra_security_defaults_enabled"
+ ],
+ "Attributes": [
+ {
+ "Section": "6 Identity Services",
+ "SubSection": "6.1 Security Defaults (Per-User MFA)",
+ "Profile": "Level 1",
+ "AssessmentStatus": "Manual",
+ "Description": "[**IMPORTANT - Please read the section overview:** If your organization pays for Microsoft Entra ID licensing (included in Microsoft 365 E3, E5, F5, or Business Premium, and EM&S E3 or E5 licenses) and **CAN** use Conditional Access, ignore the recommendations in this section and proceed to the Conditional Access section.] Security defaults in Microsoft Entra ID make it easier to be secure and help protect your organization. Security defaults contain preconfigured security settings for common attacks. Security defaults is available to everyone. The goal is to ensure that all organizations have a basic level of security enabled at no extra cost. You may turn on security defaults in the Azure portal.",
+ "RationaleStatement": "Security defaults provide secure default settings that we manage on behalf of organizations to keep customers safe until they are ready to manage their own identity security settings. For example, doing the following: - Requiring all users and admins to register for MFA. - Challenging users with MFA - when necessary, based on factors such as location, device, role, and task. - Disabling authentication from legacy authentication clients, which can’t do MFA.",
+ "ImpactStatement": "This recommendation should be implemented initially and then may be overridden by other service/product specific CIS Benchmarks. Administrators should also be aware that certain configurations in Microsoft Entra ID may impact other Microsoft services such as Microsoft 365.",
+ "RemediationProcedure": "**Remediate from Azure Portal** To enable security defaults in your directory: 1. From Azure Home select the Portal Menu. 1. Browse to `Microsoft Entra ID` > `Properties`. 1. Select `Manage security defaults`. 1. Under `Security defaults`, select `Enabled (recommended)`. 1. Select `Save`.",
+ "AuditProcedure": "**Audit from Azure Portal** To ensure security defaults is enabled in your directory: 1. From Azure Home select the Portal Menu. 2. Browse to `Microsoft Entra ID` > `Properties`. 3. Select `Manage security defaults`. 4. Under `Security defaults`, verify that `Enabled (recommended)` is selected.",
+ "AdditionalInformation": "This recommendation differs from the [Microsoft 365 Benchmark](https://workbench.cisecurity.org/benchmarks/5741). This is because the potential impact associated with disabling Security Defaults is dependent upon the security settings implemented in the environment. It is recommended that organizations disabling Security Defaults implement appropriate security settings to replace the settings configured by Security Defaults.",
+ "References": "https://docs.microsoft.com/en-us/azure/active-directory/fundamentals/concept-fundamentals-security-defaults:https://techcommunity.microsoft.com/t5/azure-active-directory-identity/introducing-security-defaults/ba-p/1061414:https://learn.microsoft.com/en-us/security/benchmark/azure/mcsb-identity-management#im-2-protect-identity-and-authentication-systems",
+ "DefaultValue": "If your tenant was created on or after October 22, 2019, security defaults may already be enabled in your tenant."
+ }
+ ]
+ },
+ {
+ "Id": "6.1.2",
+ "Description": "Ensure that 'multifactor authentication' is 'enabled' for all users",
+ "Checks": [
+ "entra_privileged_user_has_mfa",
+ "entra_non_privileged_user_has_mfa"
+ ],
+ "Attributes": [
+ {
+ "Section": "6 Identity Services",
+ "SubSection": "6.1 Security Defaults (Per-User MFA)",
+ "Profile": "Level 1",
+ "AssessmentStatus": "Manual",
+ "Description": "[**IMPORTANT - Please read the section overview:** If your organization pays for Microsoft Entra ID licensing (included in Microsoft 365 E3, E5, F5, or Business Premium, and EM&S E3 or E5 licenses) and **CAN** use Conditional Access, ignore the recommendations in this section and proceed to the Conditional Access section.] Enable multifactor authentication for all users. **Note:** Since 2024, Azure has been rolling out mandatory multifactor authentication. For more information: - https://azure.microsoft.com/en-us/blog/announcing-mandatory-multi-factor-authentication-for-azure-sign-in - https://learn.microsoft.com/en-us/entra/identity/authentication/concept-mandatory-multifactor-authentication",
+ "RationaleStatement": "Multifactor authentication requires an individual to present a minimum of two separate forms of authentication before access is granted. Multifactor authentication provides additional assurance that the individual attempting to gain access is who they claim to be. With multifactor authentication, an attacker would need to compromise at least two different authentication mechanisms, increasing the difficulty of compromise and thus reducing the risk.",
+ "ImpactStatement": "Users would require two forms of authentication before any access is granted. Additional administrative time will be required for managing dual forms of authentication when enabling multifactor authentication.",
+ "RemediationProcedure": "**Remediate from Azure Portal** 1. Go to `Microsoft Entra ID`. 1. Under `Manage`, click `Users`. 1. Click `Per-user MFA` from the top menu. 1. Click the box next to a user with `Status` `disabled`. 1. Click `Enable MFA`. 1. Click `Enable`. 1. Repeat steps 1-6 for each user requiring remediation. **Other options within Azure Portal** - [https://docs.microsoft.com/en-us/azure/active-directory/authentication/tutorial-enable-azure-mfa](https://docs.microsoft.com/en-us/azure/active-directory/authentication/tutorial-enable-azure-mfa) - [https://docs.microsoft.com/en-us/azure/active-directory/authentication/howto-mfa-mfasettings](https://docs.microsoft.com/en-us/azure/active-directory/authentication/howto-mfa-mfasettings) - [https://docs.microsoft.com/en-us/azure/active-directory/conditional-access/howto-conditional-access-policy-admin-mfa](https://docs.microsoft.com/en-us/azure/active-directory/conditional-access/howto-conditional-access-policy-admin-mfa) - [https://docs.microsoft.com/en-us/azure/active-directory/authentication/howto-mfa-getstarted#enable-multi-factor-authentication-with-conditional-access](https://docs.microsoft.com/en-us/azure/active-directory/authentication/howto-mfa-getstarted#enable-multi-factor-authentication-with-conditional-access)",
+ "AuditProcedure": "**Audit from Azure Portal** 1. Go to `Microsoft Entra ID`. 1. Under `Manage`, click `Users`. 1. Click `Per-user MFA` from the top menu. 1. Ensure that `Status` is `enabled` for all users. **Audit from REST API** Run the following Graph PowerShell command: ``` get-mguser -All | where {$_.StrongAuthenticationMethods.Count -eq 0} | Select-Object -Property UserPrincipalName ``` If the output contains any `UserPrincipalName`, then this recommendation is non-compliant.",
+ "AdditionalInformation": "",
+ "References": "https://docs.microsoft.com/en-us/azure/multi-factor-authentication/multi-factor-authentication:https://learn.microsoft.com/en-us/entra/identity/authentication/concept-mandatory-multifactor-authentication:https://azure.microsoft.com/en-us/blog/announcing-mandatory-multi-factor-authentication-for-azure-sign-in/:https://learn.microsoft.com/en-us/security/benchmark/azure/mcsb-identity-management#im-4-authenticate-server-and-services",
+ "DefaultValue": "Multifactor authentication is not enabled for all users by default. Starting in 2024, multifactor authentication is enabled for administrative accounts by default."
+ }
+ ]
+ },
+ {
+ "Id": "3.1.4",
+ "Description": "Ensure that users and groups are synced from Microsoft Entra ID to Azure Databricks",
+ "Checks": [],
+ "Attributes": [
+ {
+ "Section": "3 Analytics Services",
+ "Profile": "Level 1",
+ "AssessmentStatus": "Manual",
+ "Description": "To ensure centralized identity and access management, users and groups from Microsoft Entra ID should be synchronized with Azure Databricks. This is achieved through SCIM provisioning, which automates the creation, update, and deactivation of users and groups in Databricks based on Entra ID assignments. Enabling this integration ensures that access controls in Databricks remain consistent with corporate identity governance policies, reducing the risk of orphaned accounts, stale permissions, and unauthorized access.",
+ "RationaleStatement": "Syncing users and groups from Microsoft Entra ID centralizes access control, enforces the least privilege principle by automatically revoking unnecessary access, reduces administrative overhead by eliminating manual user management, and ensures auditability and compliance with industry regulations.",
+ "ImpactStatement": "SCIM provisioning requires role mapping to avoid misconfigured user privileges.",
+ "RemediationProcedure": "**Remediate from Azure Portal** Enable provisioning in Azure Portal: 1. Go to `Microsoft Entra ID`. 1. Under `Manage`, click `Enterprise applications`. 1. Click the name of the Azure Databricks SCIM application. 1. Under `Provisioning`, select `Automatic` and enter the SCIM endpoint and API token from Databricks. Enable provisioning in Databricks: 5. Navigate to `Admin Console` > `Identity and Access Management`. 6. Enable SCIM provisioning and generate an API token. Configure role assignments: 7. Ensure groups from Entra ID are mapped to appropriate Databricks roles. 8. Restrict administrative privileges to designated security groups. Regularly monitor sync logs: 9. Periodically review sync logs in Microsoft Entra ID and Databricks Admin Console. 10. Configure Azure Monitor alerts for provisioning failures. Disable manual user creation in Databricks: 11. Ensure that all user management is controlled via SCIM sync from Entra ID. 12. Disable personal access token usage for authentication. **Remediate from Azure CLI** Enable SCIM User and Group Provisioning in Azure Databricks: ``` az ad app update --id --set provisioning.provisioningMode=Automatic ```",
+ "AuditProcedure": "**Audit from Azure Portal** Verify SCIM provisioning is enabled: 1. Go to `Microsoft Entra ID`. 1. Under `Manage`, click `Enterprise applications`. 1. Click the name of the Azure Databricks SCIM application. 1. Under `Provisioning`, confirm that SCIM provisioning is enabled and running. Check user sync status in Azure Portal: 5. Under `Provisioning Logs`, verify the last successful sync and any failed entries. Check user sync status in Databricks: 6. Go to `Admin Console` > `Identity and Access Management`. 7. Confirm that Users and Groups match those assigned in Microsoft Entra ID. Ensure role-based access control (RBAC) mapping is correct: 8. Verify that users are assigned appropriate Databricks roles (e.g. Admin, User, Contributor). 9. Confirm that groups are mapped to workspace access roles.",
+ "AdditionalInformation": "",
+ "References": "https://learn.microsoft.com/en-us/azure/databricks/administration-guide/users-groups/scim/aad",
+ "DefaultValue": "By default, Azure Databricks does not sync users and groups from Microsoft Entra ID."
+ }
+ ]
+ },
+ {
+ "Id": "6.2.1",
+ "Description": "Ensure that 'trusted locations' are defined",
+ "Checks": [
+ "entra_trusted_named_locations_exists"
+ ],
+ "Attributes": [
+ {
+ "Section": "6 Identity Services",
+ "SubSection": "6.2 Conditional Access",
+ "Profile": "Level 2",
+ "AssessmentStatus": "Manual",
+ "Description": "Microsoft Entra ID Conditional Access allows an organization to configure `Named locations` and configure whether those locations are trusted or untrusted. These settings provide organizations the means to specify Geographical locations for use in conditional access policies, or define actual IP addresses and IP ranges and whether or not those IP addresses and/or ranges are trusted by the organization.",
+ "RationaleStatement": "Defining trusted source IP addresses or ranges helps organizations create and enforce Conditional Access policies around those trusted or untrusted IP addresses and ranges. Users authenticating from trusted IP addresses and/or ranges may have less access restrictions or access requirements when compared to users that try to authenticate to Microsoft Entra ID from untrusted locations or untrusted source IP addresses/ranges.",
+ "ImpactStatement": "When configuring `Named locations`, the organization can create locations using Geographical location data or by defining source IP addresses or ranges. Configuring `Named locations` using a Country location does not provide the organization the ability to mark those locations as trusted, and any Conditional Access policy relying on those `Countries location` setting will not be able to use the `All trusted locations` setting within the Conditional Access policy. They instead will have to rely on the `Select locations` setting. This may add additional resource requirements when configuring and will require thorough organizational testing. In general, Conditional Access policies may completely prevent users from authenticating to Microsoft Entra ID, and thorough testing is recommended. To avoid complete lockout, a 'Break Glass' account with full Global Administrator rights is recommended in the event all other administrators are locked out of authenticating to Microsoft Entra ID. This 'Break Glass' account should be excluded from Conditional Access Policies and should be configured with the longest pass phrase feasible in addition to a FIDO2 security key or certificate kept in a very secure physical location. This account should only be used in the event of an emergency and complete administrator lockout. **NOTE:** Starting July 2024, Microsoft will begin requiring MFA for All Users - including Break Glass Accounts. By the end of October 2024, this requirement will be enforced. Physical FIDO2 security keys, or a certificate kept on secure removable storage can fulfill this MFA requirement. If opting for a physical device, that device should be kept in a very secure, documented physical location.",
+ "RemediationProcedure": "**Remediate from Azure Portal** 1. In the Azure Portal, navigate to `Microsoft Entra ID` 1. Under `Manage`, click `Security` 1. Under `Protect`, click `Conditional Access` 1. Under `Manage`, click `Named locations` 1. Within the `Named locations` blade, click on `IP ranges location` 1. Enter a name for this location setting in the `Name` text box 1. Click on the `+` sign 1. Add an IP Address Range in CIDR notation inside the text box that appears 1. Click on the `Add` button 1. Repeat steps 7 through 9 for each IP Range that needs to be added 1. If the information entered are trusted ranges, select the `Mark as trusted location` check box 1. Once finished, click on `Create` **Remediate from PowerShell** Create a new trusted IP-based Named location policy ``` [System.Collections.Generic.List`1[Microsoft.Open.MSGraph.Model.IpRange]]$ipRanges = @() $ipRanges.Add() $ipRanges.Add() $ipRanges.Add() New-MgIdentityConditionalAccessNamedLocation -dataType #microsoft.graph.ipNamedLocation -DisplayName -IsTrusted $true -IpRanges $ipRanges ``` Set an existing IP-based Named location policy to trusted ``` Update-MgIdentityConditionalAccessNamedLocation -PolicyId -dataType #microsoft.graph.ipNamedLocation -IsTrusted $true ```",
+ "AuditProcedure": "**Audit from Azure Portal** 1. In the Azure Portal, navigate to `Microsoft Entra ID` 1. Under `Manage`, click `Security` 1. Under `Protect`, click `Conditional Access` 1. Under `Manage`, click `Named locations` Ensure there are `IP ranges location` settings configured and marked as `Trusted` **Audit from PowerShell** ``` Get-MgIdentityConditionalAccessNamedLocation ``` In the output from the above command, for each Named location group, make sure at least one entry contains the `IsTrusted` parameter with a value of `True`. Otherwise, if there is no output as a result of the above command or all of the entries contain the `IsTrusted` parameter with an empty value, a `NULL` value, or a value of `False`, the results are out of compliance with this check.",
+ "AdditionalInformation": "",
+ "References": "https://learn.microsoft.com/en-us/entra/identity/conditional-access/concept-assignment-network:https://learn.microsoft.com/en-us/security/benchmark/azure/mcsb-identity-management#im-7-restrict-resource-access-based-on--conditions:https://learn.microsoft.com/en-us/entra/identity/role-based-access-control/security-emergency-access",
+ "DefaultValue": "By default, no locations are configured under the `Named locations` blade within the Microsoft Entra ID Conditional Access blade."
+ }
+ ]
+ },
+ {
+ "Id": "3.1.5",
+ "Description": "Ensure that Unity Catalog is configured for Azure Databricks",
+ "Checks": [],
+ "Attributes": [
+ {
+ "Section": "3 Analytics Services",
+ "SubSection": "3.1 Azure Databricks",
+ "Profile": "Level 1",
+ "AssessmentStatus": "Manual",
+ "Description": "Unity Catalog is a centralized governance model for managing and securing data in Azure Databricks. It provides fine-grained access control to databases, tables, and views using Microsoft Entra ID identities. Unity Catalog also enhances data lineage, audit logging, and compliance monitoring, making it a critical component for security and governance.",
+ "RationaleStatement": "* Enforces centralized access control policies and reduces data security risks. * Enables identity-based authentication via Microsoft Entra ID. * Improves compliance with industry regulations (e.g. GDPR, HIPAA, SOC 2) by providing audit logs and access visibility. * Prevents unauthorized data access through table-, row-, and column-level security (RLS & CLS).",
+ "ImpactStatement": "* Improperly configured permissions may lead to data exfiltration or unauthorized access. * Unity Catalog requires structured governance policies to be effective and prevent overly permissive access.",
+ "RemediationProcedure": "Use the remediation procedure written in this article: https://learn.microsoft.com/en-us/azure/databricks/data-governance/unity-catalog/get-started.",
+ "AuditProcedure": "Method 1: Verify unity catalog deployment: 1. As an Azure Databricks account admin, log into the account console. 1. Click Workspaces. 1. Find your workspace and check the Metastore column. If a metastore name is present, your workspace is attached to a Unity Catalog metastore and therefore enabled for Unity Catalog. Method 2: Run a SQL query to confirm Unity Catalog enablement Run the following SQL query in the SQL query editor or a notebook that is attached to a Unity Catalog-enabled compute resource. No admin role is required. ``` SELECT CURRENT_METASTORE(); ``` If the query returns a metastore ID like the following, then your workspace is attached to a Unity Catalog metastore and therefore enabled for Unity Catalog.",
+ "AdditionalInformation": "",
+ "References": "https://learn.microsoft.com/en-us/azure/databricks/data-governance/unity-catalog/:https://learn.microsoft.com/en-us/azure/databricks/admin/users-groups/:https://learn.microsoft.com/en-us/azure/databricks/data-governance/unity-catalog/enable-workspaces",
+ "DefaultValue": "New workspaces have Unity Catalog enabled by default. Existing workspaces may require manual enablement."
+ }
+ ]
+ },
+ {
+ "Id": "3.1.6",
+ "Description": "Ensure that usage is restricted and expiry is enforced for Databricks personal access tokens",
+ "Checks": [],
+ "Attributes": [
+ {
+ "Section": "3 Analytics Services",
+ "SubSection": "3.1 Azure Databricks",
+ "Profile": "Level 1",
+ "AssessmentStatus": "Manual",
+ "Description": "Databricks personal access tokens (PATs) provide API-based authentication for users and applications. By default, users can generate API tokens without expiration, leading to potential security risks if tokens are leaked, improperly stored, or not rotated regularly. To mitigate these risks, administrators should: * Restrict token creation to approved users and service principals. * Enforce expiration policies to prevent long-lived tokens. * Monitor token usage and revoke unused or compromised tokens.",
+ "RationaleStatement": "Restricting usage and enforcing expiry for personal access tokens reduces exposure to long-lived tokens, minimizes the risk of API abuse if compromised, and aligns with security best practices through controlled issuance and enforced expiry.",
+ "ImpactStatement": "If revoked improperly, applications relying on these tokens may fail, requiring a remediation plan for token rotation. Increased administrative effort is required to track and manage API tokens effectively.",
+ "RemediationProcedure": "**Remediate from Azure Portal** Disable personal access tokens: If your workspace does not require PATs, you can disable them entirely to prevent their use.",
+ "AuditProcedure": "Azure Databricks administrators can monitor and revoke personal access tokens within their workspace. Detailed instructions are available in the Monitor and Revoke Personal Access Tokens section of the Microsoft documentation: https://learn.microsoft.com/en-us/azure/databricks/admin/access-control/tokens. To evaluate the usage of personal access tokens in your Azure Databricks account, you can utilize the provided notebook that lists all PATs not rotated or updated in the last 90 days, allowing you to identify tokens that may require revocation. This process is detailed here: https://docs.azure.cn/en-us/databricks/security/auth/oauth-pat-usage. Implementing diagnostic logging provides a comprehensive reference of audit log services and events, enabling you to track activities related to personal access tokens. More information can be found in the diagnostic log reference section: https://docs.azure.cn/en-us/databricks/security/auth/oauth-pat-usage.",
+ "AdditionalInformation": "",
+ "References": "https://learn.microsoft.com/en-us/azure/databricks/administration-guide/access-control/tokens:https://learn.microsoft.com/en-us/azure/databricks/dev-tools/auth/",
+ "DefaultValue": "By default, personal access tokens are enabled and users can create the Personal access token and their expiry time."
+ }
+ ]
+ },
+ {
+ "Id": "3.1.7",
+ "Description": "Ensure that diagnostic log delivery is configured for Azure Databricks",
+ "Checks": [],
+ "Attributes": [
+ {
+ "Section": "3 Analytics Services",
+ "SubSection": "3.1 Azure Databricks",
+ "Profile": "Level 1",
+ "AssessmentStatus": "Manual",
+ "Description": "Azure Databricks Diagnostic Logging provides insights into system operations, user activities, and security events within a Databricks workspace. Enabling diagnostic logs helps organizations: * Detect security threats by logging access, job executions, and cluster activities. * Ensure compliance with industry regulations such as SOC 2, HIPAA, and GDPR. * Monitor operational performance and troubleshoot issues proactively.",
+ "RationaleStatement": "Diagnostic logging provides visibility into security and operational activities within Databricks workspaces while maintaining an audit trail for forensic investigations, and it supports compliance with regulatory standards that require logging and monitoring.",
+ "ImpactStatement": "Logs consume storage and may require additional monitoring tools, leading to increased operational overhead and costs. Incomplete log configurations may result in missing critical events, reducing monitoring effectiveness.",
+ "RemediationProcedure": "**Remediate from Azure Portal** Enable diagnostic logging for Azure Databricks: 1. Navigate to your Azure Databricks workspace. 1. In the left-hand menu, select `Monitoring` > `Diagnostic settings`. 1. Click `+ Add diagnostic setting`. 1. Under `Category details`, select the log categories you wish to capture, such as AuditLogs, Clusters, Notebooks, and Jobs. 1. Choose a destination for the logs: - `Log Analytics workspace`: For advanced querying and monitoring. - `Storage account`: For long-term retention. - `Event Hub`: For integration with third-party systems. 1. Provide a `Name` for the diagnostic setting. 1. Click `Save`. Implement log retention policies: 1. Navigate to your Log Analytics workspace. 1. Under `General`, select `Usage and estimated costs`. 1. Click `Data Retention`. 1. Adjust the retention period slider to the desired number of days (up to 730 days). 1. Click `OK`. Monitor logs for anomalies: 1. Navigate to `Azure Monitor`. 1. Select `Alerts` > `+ New alert rule`. 1. Under `Scope`, specify the Databricks resource. 1. Define `Condition` based on log queries that identify anomalies (e.g. unauthorized access attempts). 1. Configure `Actions` to notify stakeholders or trigger automated responses. 1. Provide an Alert rule `name` and `description`. 1. Click `Create alert rule`. **Remediate from Azure CLI** Enable diagnostic logging for Azure Databricks: ``` az monitor diagnostic-settings create --name DatabricksLogging --resource --logs '[{category: AuditLogs, enabled: true}, {category: Clusters, enabled: true}, {category: Notebooks, enabled: true}, {category: Jobs, enabled: true}]' --workspace ``` Implement log retention policies: ``` az monitor log-analytics workspace update --resource-group --name --retention-time 365 ``` Monitor logs for anomalies: ``` az monitor activity-log alert create --name DatabricksAnomalyAlert --resource-group --scopes --condition contains 'UnauthorizedAccess' ```",
+ "AuditProcedure": "**Audit from Azure Portal** Check if diagnostic logging is enabled for the Databricks workspace: 1. Go to `Azure Databricks`. 1. Select a workspace. 1. In the left-hand menu, select `Monitoring` > `Diagnostic settings`. 1. Verify if a diagnostic setting is configured. If not, diagnostic logging is not enabled. Ensure that logging is enabled for the following categories:",
+ "AdditionalInformation": "* Ensure that the Azure Databricks workspace is on the Premium plan to utilize diagnostic logging features. * Regularly review and update alert rules to adapt to evolving security threats and operational requirements.",
+ "References": "https://learn.microsoft.com/en-us/azure/databricks/admin/account-settings/audit-log-delivery:https://learn.microsoft.com/en-us/troubleshoot/azure/azure-monitor/log-analytics/billing/configure-data-retention",
+ "DefaultValue": ""
+ }
+ ]
+ },
+ {
+ "Id": "6.5",
+ "Description": "Ensure that 'Number of methods required to reset' is set to '2'",
+ "Checks": [],
+ "Attributes": [
+ {
+ "Section": "6 Identity Services",
+ "Profile": "Level 1",
+ "AssessmentStatus": "Manual",
+ "Description": "Ensures that two alternate forms of identification are provided before allowing a password reset.",
+ "RationaleStatement": "A Self-service Password Reset (SSPR) through Azure Multi-factor Authentication (MFA) ensures the user's identity is confirmed using two separate methods of identification. With multiple methods set, an attacker would have to compromise both methods before they could maliciously reset a user's password.",
+ "ImpactStatement": "There may be administrative overhead, as users who lose access to their secondary authentication methods will need an administrator with permissions to remove it. There will also need to be organization-wide security policies and training to teach administrators to verify the identity of the requesting user so that social engineering cannot render this setting useless.",
+ "RemediationProcedure": "**Remediate from Azure Portal** 1. From Azure Home select the Portal Menu 1. Select `Microsoft Entra ID` 1. Under `Manage`, select `Users` 1. Under `Manage`, select `Password reset` 1. Select `Authentication methods` 1. Set the `Number of methods required to reset` to `2` 1. Click `Save`",
+ "AuditProcedure": "**Audit from Azure Portal** 1. From Azure Home select the Portal Menu 1. Select `Microsoft Entra ID` 1. Under `Manage`, select `Users` 1. Under `Manage`, select `Password reset` 1. Select `Authentication methods` 1. Ensure that `Number of methods required to reset` is set to `2`",
+ "AdditionalInformation": "",
+ "References": "https://learn.microsoft.com/en-us/entra/identity/authentication/tutorial-enable-sspr:https://learn.microsoft.com/en-us/entra/identity/authentication/concept-registration-mfa-sspr-combined:https://learn.microsoft.com/en-us/security/benchmark/azure/mcsb-identity-management#im-6-use-strong-authentication-controls:https://learn.microsoft.com/en-us/entra/identity/authentication/passwords-faq#password-reset-registration:https://support.microsoft.com/en-us/account-billing/reset-your-work-or-school-password-using-security-info-23dde81f-08bb-4776-ba72-e6b72b9dda9e:https://learn.microsoft.com/en-us/entra/identity/authentication/concept-authentication-methods",
+ "DefaultValue": "By default, the `Number of methods required to reset` is set to 2."
+ }
+ ]
+ },
+ {
+ "Id": "6.2.6",
+ "Description": "Ensure that multifactor authentication is required for Windows Azure Service Management API",
+ "Checks": [
+ "entra_conditional_access_policy_require_mfa_for_management_api"
+ ],
+ "Attributes": [
+ {
+ "Section": "6 Identity Services",
+ "SubSection": "6.2 Conditional Access",
+ "Profile": "Level 2",
+ "AssessmentStatus": "Manual",
+ "Description": "This recommendation ensures that users accessing the Windows Azure Service Management API (i.e. Azure Powershell, Azure CLI, Azure Resource Manager API, etc.) are required to use multi-factor authentication (MFA) credentials when accessing resources through the Windows Azure Service Management API.",
+ "RationaleStatement": "Administrative access to the Windows Azure Service Management API should be secured with a higher level of scrutiny to authenticating mechanisms. Enabling multi-factor authentication is recommended to reduce the potential for abuse of Administrative actions, and to prevent intruders or compromised admin credentials from changing administrative settings. **IMPORTANT**: While this recommendation allows exceptions to specific Users or Groups, they should be very carefully tracked and reviewed for necessity on a regular interval through an Access Review process. It is important that this rule be built to include All Users to ensure that all users not specifically excepted will be required to use MFA to access the Azure Service Management API.",
+ "ImpactStatement": "Conditional Access policies require Microsoft Entra ID P1 or P2 licenses. Similarly, they may require additional overhead to maintain if users lose access to their MFA. Any users or groups which are granted an exception to this policy should be carefully tracked, be granted only minimal necessary privileges, and conditional access exceptions should be regularly reviewed or investigated.",
+ "RemediationProcedure": "**Remediate from Azure Portal** 1. From the Azure Admin Portal dashboard, open `Microsoft Entra ID`. 1. Click `Security` in the Entra ID blade. 1. Click `Conditional Access` in the Security blade. 1. Click `Policies` in the Conditional Access blade. 1. Click `+ New policy`. 1. Enter a name for the policy. 1. Click the blue text under `Users`. 1. Under `Include`, select `All users`. 1. Under `Exclude`, check `Users and groups`. 1. Select users or groups to be exempted from this policy (e.g. break-glass emergency accounts, and non-interactive service accounts) then click the `Select` button. 1. Click the blue text under `Target resources`. 1. Under `Include`, click the `Select apps` radio button. 1. Click the blue text under `Select`. 1. Check the box next to `Windows Azure Service Management APIs` then click the `Select` button. 1. Click the blue text under `Grant`. 1. Under `Grant access` check the box for `Require multi-factor authentication` then click the `Select` button. 1. Before creating, set `Enable policy` to `Report-only`. 1. Click `Create`. After testing the policy in report-only mode, update the `Enable policy` setting from `Report-only` to `On`.",
+ "AuditProcedure": "**Audit from Azure Portal** 1. From the Azure Admin Portal dashboard, open `Microsoft Entra ID`. 1. In the menu on the left of the Entra ID blade, click `Security`. 1. In the menu on the left of the Security blade, click `Conditional Access`. 1. In the menu on the left of the Conditional Access blade, click `Policies`. 1. Click on the name of the policy you wish to audit. 1. Click the blue text under `Users`. 1. Under the `Include` section of Users, ensure that `All Users` is selected. 1. Under the `Exclude` section of Users, review the `Users and Groups` that are excluded from the policy (NOTE: this should be limited to break-glass emergency access accounts, non-interactive service accounts, and other carefully considered exceptions). 1. On the left side, click the blue text under `Target resources`. 1. Under the `Include` section of Target Resources, ensure that the `Select apps` radio button is selected. 1. Under `Select`, ensure that `Windows Azure Service Management API` is listed.",
+ "AdditionalInformation": "These policies should be tested by using the What If tool in the References. Setting these can and will create issues with administrators changing settings until they use an MFA device linked to their accounts. An emergency access account is recommended for this eventuality if all administrators are locked out. Please see the documentation in the references for further information. Similarly further testing can also be done via the insights and reporting resource in References which monitors Azure sign ins.",
+ "References": "https://learn.microsoft.com/en-us/security/benchmark/azure/mcsb-identity-management#im-7-restrict-resource-access-based-on--conditions:https://docs.microsoft.com/en-us/azure/active-directory/conditional-access/concept-conditional-access-users-groups:https://learn.microsoft.com/en-us/entra/identity/conditional-access/howto-conditional-access-policy-azure-management:https://learn.microsoft.com/en-us/entra/identity/conditional-access/concept-conditional-access-cloud-apps#windows-azure-service-management-api",
+ "DefaultValue": "MFA is not enabled by default for administrative actions."
+ }
+ ]
+ },
+ {
+ "Id": "6.2.7",
+ "Description": "Ensure that multifactor authentication is required to access Microsoft Admin Portals",
+ "Checks": [
+ "defender_ensure_defender_for_server_is_on"
+ ],
+ "Attributes": [
+ {
+ "Section": "6 Identity Services",
+ "SubSection": "6.2 Conditional Access",
+ "Profile": "Level 2",
+ "AssessmentStatus": "Manual",
+ "Description": "This recommendation ensures that users accessing Microsoft Admin Portals (i.e. Microsoft 365 Admin, Microsoft 365 Defender, Exchange Admin Center, Azure Portal, etc.) are required to use multi-factor authentication (MFA) credentials when logging into an Admin Portal.",
+ "RationaleStatement": "Administrative Portals for Microsoft Azure should be secured with a higher level of scrutiny to authenticating mechanisms. Enabling multi-factor authentication is recommended to reduce the potential for abuse of Administrative actions, and to prevent intruders or compromised admin credentials from changing administrative settings. **IMPORTANT**: While this recommendation allows exceptions to specific Users or Groups, they should be very carefully tracked and reviewed for necessity on a regular interval through an Access Review process. It is important that this rule be built to include All Users to ensure that all users not specifically excepted will be required to use MFA to access Admin Portals.",
+ "ImpactStatement": "Conditional Access policies require Microsoft Entra ID P1 or P2 licenses. Similarly, they may require additional overhead to maintain if users lose access to their MFA. Any users or groups which are granted an exception to this policy should be carefully tracked, be granted only minimal necessary privileges, and conditional access exceptions should be reviewed or investigated.",
+ "RemediationProcedure": "**Remediate from Azure Portal** 1. From the Azure Admin Portal dashboard, open `Microsoft Entra ID`. 1. Click `Security` in the Entra ID blade. 1. Click `Conditional Access` in the Security blade. 1. Click `Policies` in the Conditional Access blade. 1. Click `+ New policy`. 1. Enter a name for the policy. 1. Click the blue text under `Users`. 1. Under `Include`, select `All users`. 1. Under `Exclude`, check `Users and groups`. 1. Select users or groups to be exempted from this policy (e.g. break-glass emergency accounts, and non-interactive service accounts) then click the `Select` button. 1. Click the blue text under `Target resources`. 1. Under `Include`, click the `Select apps` radio button. 1. Click the blue text under `Select`. 1. Check the box next to `Microsoft Admin Portals` then click the `Select` button. 1. Click the blue text under `Grant`. 1. Under `Grant access` check the box for `Require multifactor authentication` then click the `Select` button. 1. Before creating, set `Enable policy` to `Report-only`. 1. Click `Create`. After testing the policy in report-only mode, update the `Enable policy` setting from `Report-only` to `On`.",
+ "AuditProcedure": "**Audit from Azure Portal** 1. From the Azure Admin Portal dashboard, open `Microsoft Entra ID`. 1. In the menu on the left of the Entra ID blade, click `Security`. 1. In the menu on the left of the Security blade, click `Conditional Access`. 1. In the menu on the left of the Conditional Access blade, click `Policies`. 1. Click on the name of the policy you wish to audit. 1. Click the blue text under `Users`. 1. Under the `Include` section of Users, review `Users and Groups` to ensure that `All Users` is selected. 1. Under the `Exclude` section of Users, review the `Users and Groups` that are excluded from the policy (NOTE: this should be limited to break-glass emergency access accounts, non-interactive service accounts, and other carefully considered exceptions). 1. On the left side, click the blue text under `Target Resources`. 1. Under the `Include` section of Target resources, ensure the `Select apps` radio button is selected. 1. Under `Select`, ensure `Microsoft Admin Portals` is listed.",
+ "AdditionalInformation": "These policies should be tested by using the What If tool in the References. Setting these can and will create issues with administrators changing settings until they use an MFA device linked to their accounts. An emergency access account is recommended for this eventuality if all administrators are locked out. Please see the documentation in the references for further information. Similarly further testing can also be done via the insights and reporting resource in References which monitors Azure sign ins.",
+ "References": "https://learn.microsoft.com/en-us/security/benchmark/azure/mcsb-identity-management#im-7-restrict-resource-access-based-on--conditions:https://docs.microsoft.com/en-us/azure/active-directory/conditional-access/concept-conditional-access-users-groups:https://learn.microsoft.com/en-us/entra/identity/conditional-access/how-to-policy-mfa-admin-portals",
+ "DefaultValue": "MFA is not enabled by default for administrative actions."
+ }
+ ]
+ },
+ {
+ "Id": "6.6",
+ "Description": "Ensure that account 'Lockout threshold' is less than or equal to '10'",
+ "Checks": [],
+ "Attributes": [
+ {
+ "Section": "6 Identity Services",
+ "Profile": "Level 1",
+ "AssessmentStatus": "Manual",
+ "Description": "The account lockout threshold determines how many failed login attempts are permitted prior to placing the account in a locked-out state and initiating a variable lockout duration.",
+ "RationaleStatement": "Account lockout is a method of protecting against brute-force and password spray attacks. Once the lockout threshold has been exceeded, the account enters a locked-out state which prevents all login attempts for a variable duration. The lockout in combination with a reasonable duration reduces the total number of failed login attempts that a malicious actor can execute in a given period of time.",
+ "ImpactStatement": "If account lockout threshold is set too low (less than 3), users may experience frequent lockout events and the resulting security alerts may contribute to alert fatigue. If account lockout threshold is set too high (more than 10), malicious actors can programmatically execute more password attempts in a given period of time.",
+ "RemediationProcedure": "**Remediate from Azure Portal** 1. From Azure Home select the Portal Menu. 1. Select `Microsoft Entra ID`. 1. Under `Manage`, select `Security`. 1. Under `Manage`, select `Authentication methods`. 1. Under `Manage`, select `Password protection`. 1. Set the `Lockout threshold` to `10` or fewer. 1. Click `Save`.",
+ "AuditProcedure": "**Audit from Azure Portal** 1. From Azure Home select the Portal Menu. 1. Select `Microsoft Entra ID`. 1. Under `Manage`, select `Security`. 1. Under `Manage`, select `Authentication methods`. 1. Under `Manage`, select `Password protection`. 1. Ensure that `Lockout threshold` is set to `10` or fewer.",
+ "AdditionalInformation": "**NOTE:** The variable number for failed login attempts allowed before lockout is prescribed by many security and compliance frameworks. The **appropriate** setting for this variable should be determined by the most restrictive security or compliance framework that your organization follows.",
+ "References": "https://learn.microsoft.com/en-us/entra/identity/authentication/howto-password-smart-lockout#manage-microsoft-entra-smart-lockout-values",
+ "DefaultValue": "By default, Lockout threshold is set to `10`."
+ }
+ ]
+ },
+ {
+ "Id": "6.7",
+ "Description": "Ensure that account 'Lockout duration in seconds' is greater than or equal to '60'",
+ "Checks": [],
+ "Attributes": [
+ {
+ "Section": "6 Identity Services",
+ "Profile": "Level 1",
+ "AssessmentStatus": "Manual",
+ "Description": "The account lockout duration value determines how long an account retains the status of lockout, and therefore how long before a user can continue to attempt to login after passing the lockout threshold.",
+ "RationaleStatement": "Account lockout is a method of protecting against brute-force and password spray attacks. Once the lockout threshold has been exceeded, the account enters a locked-out state which prevents all login attempts for a variable duration. The lockout in combination with a reasonable duration reduces the total number of failed login attempts that a malicious actor can execute in a given period of time.",
+ "ImpactStatement": "If account lockout duration is set too low (less than 60 seconds), malicious actors can perform more password spray and brute-force attempts over a given period of time. If the account lockout duration is set too high (more than 300 seconds) users may experience inconvenient delays during lockout.",
+ "RemediationProcedure": "**Remediate from Azure Portal** 1. From Azure Home select the Portal Menu. 1. Select `Microsoft Entra ID`. 1. Under `Manage`, select `Security`. 1. Under `Manage`, select `Authentication methods`. 1. Under `Manage`, select `Password protection`. 1. Set the `Lockout duration in seconds` to `60` or higher. 1. Click `Save`.",
+ "AuditProcedure": "**Audit from Azure Portal** 1. From Azure Home select the Portal Menu. 1. Select `Microsoft Entra ID`. 1. Under `Manage`, select `Security`. 1. Under `Manage`, select `Authentication methods`. 1. Under `Manage`, select `Password protection`. 1. Ensure that `Lockout duration in seconds` is set to `60` or higher.",
+ "AdditionalInformation": "",
+ "References": "https://learn.microsoft.com/en-us/entra/identity/authentication/howto-password-smart-lockout#manage-microsoft-entra-smart-lockout-values",
+ "DefaultValue": "By default, Lockout duration in seconds is set to `60`."
+ }
+ ]
+ },
+ {
+ "Id": "6.8",
+ "Description": "Ensure that a 'Custom banned password list' is set to 'Enforce'",
+ "Checks": [],
+ "Attributes": [
+ {
+ "Section": "6 Identity Services",
+ "Profile": "Level 1",
+ "AssessmentStatus": "Manual",
+ "Description": "Microsoft Azure applies a default global banned password list to all user and admin accounts that are created and managed directly in Microsoft Entra ID. The Microsoft Entra password policy does not apply to user accounts that are synchronized from an on-premises Active Directory environment, unless Microsoft Entra ID Connect is used and `EnforceCloudPasswordPolicyForPasswordSyncedUsers` is enabled. Review the `Default Value` section for more detail on the password policy. For increased password security, a custom banned password list is recommended",
+ "RationaleStatement": "Implementing a custom banned password list gives your organization further control over the password policy. Disallowing easy-to-guess passwords increases the security of your Azure resources.",
+ "ImpactStatement": "Increasing password complexity may increase user account administration overhead. Utilizing the default global banned password list and a custom list requires a Microsoft Entra ID P1 or P2 license. On-premises Active Directory Domain Services users who aren't synchronized to Microsoft Entra ID still benefit from Microsoft Entra ID Password Protection based on the existing licensing of synchronized users.",
+ "RemediationProcedure": "**Remediate from Azure Portal** 1. From Azure Home select the Portal Menu. 1. Select `Microsoft Entra ID`. 1. Under `Manage`, select `Security`. 1. Under `Manage`, select `Authentication methods`. 1. Under `Manage`, select `Password protection`. 1. Set the `Enforce custom list` option to `Yes`. 1. Click in the `Custom banned password list` text box. 1. Add a list of words, one per line, to prevent users from using in passwords. 1. Click `Save`.",
+ "AuditProcedure": "**Audit from Azure Portal** 1. From Azure Home select the Portal Menu. 1. Select `Microsoft Entra ID`. 1. Under `Manage`, select `Security`. 1. Under `Manage`, select `Authentication methods`. 1. Under `Manage`, select `Password protection`. 1. Ensure `Enforce custom list` is set to `Yes`. 1. Review the list of words banned from use in passwords.",
+ "AdditionalInformation": "",
+ "References": "https://learn.microsoft.com/en-us/entra/identity/authentication/concept-password-ban-bad-combined-policy:https://learn.microsoft.com/en-us/entra/identity/authentication/concept-password-ban-bad:https://docs.microsoft.com/en-us/powershell/module/Azuread/:https://www.microsoft.com/en-us/research/publication/password-guidance/:https://learn.microsoft.com/en-us/entra/identity/authentication/tutorial-configure-custom-password-protection:https://learn.microsoft.com/en-us/security/benchmark/azure/mcsb-identity-management#im-6-use-strong-authentication-controls",
+ "DefaultValue": "By default the custom banned password list is not 'Enabled'. Organization-specific terms can be added to the custom banned password list, such as the following examples: - Brand names - Product names - Locations, such as company headquarters - Company-specific terms - Abbreviations that have specific company meaning - Months and weekdays with your company's local languages The default global banned password list is already applied to your resources which applies the following basic requirements: **Characters allowed:** - Uppercase characters (A - Z) - Lowercase characters (a - z) - Numbers (0 - 9) - Symbols: - @ # $ % ^ & * - _ ! + = [ ] { } | \\ : ' , . ? / ` ~ ( ) ; < > - blank space **Characters not allowed:** - Unicode characters **Password length:** Passwords require: - A minimum of eight characters - A maximum of 256 characters **Password complexity:** Passwords require three out of four of the following categories: - Uppercase characters - Lowercase characters - Numbers - Symbols Note: Password complexity check isn't required for Education tenants. **Password not recently used:** - When a user changes or resets their password, the new password can't be the same as the current or recently used passwords. - Password isn't banned by Entra ID Password Protection. - The password can't be on the global list of banned passwords for Azure AD Password Protection, or on the customizable list of banned passwords specific to your organization. **Evaluation** New passwords are evaluated for strength and complexity by validating against the combined list of terms from the global and custom banned password lists. Even if a user's password contains a banned password, the password may be accepted if the overall password is otherwise strong enough."
+ }
+ ]
+ },
+ {
+ "Id": "6.9",
+ "Description": "Ensure that 'Number of days before users are asked to re-confirm their authentication information' is not set to '0'",
+ "Checks": [],
+ "Attributes": [
+ {
+ "Section": "6 Identity Services",
+ "Profile": "Level 1",
+ "AssessmentStatus": "Manual",
+ "Description": "Ensure that the number of days before users are asked to re-confirm their authentication information is not set to 0.",
+ "RationaleStatement": "This setting is necessary if 'Require users to register when signing in' is enabled. If authentication re-confirmation is disabled, registered users will never be prompted to re-confirm their existing authentication information. If the authentication information for a user changes, such as a phone number or email, then the password reset information for that user reverts to the previously registered authentication information.",
+ "ImpactStatement": "Users will be prompted to re-confirm their authentication information after the number of days specified.",
+ "RemediationProcedure": "**Remediate from Azure Portal** 1. From Azure Home select the Portal Menu. 1. Select `Microsoft Entra ID`. 1. Under `Manage`, select `Users`. 1. Select `Password reset`. 1. Under `Manage`, select `Registration`. 1. Set the `Number of days before users are asked to re-confirm their authentication information` to your organization-defined frequency. 1. Click `Save`.",
+ "AuditProcedure": "**Audit from Azure Portal** 1. From Azure Home select the Portal Menu. 1. Select `Microsoft Entra ID`. 1. Under `Manage`, select `Users`. 1. Select `Password reset`. 1. Under `Manage`, select `Registration`. 1. Ensure that `Number of days before users are asked to re-confirm their authentication information` is not set to `0`.",
+ "AdditionalInformation": "",
+ "References": "https://learn.microsoft.com/en-us/entra/identity/authentication/concept-sspr-howitworks#registration:https://support.microsoft.com/en-us/account-billing/reset-your-work-or-school-password-using-security-info-23dde81f-08bb-4776-ba72-e6b72b9dda9e:https://learn.microsoft.com/en-us/security/benchmark/azure/mcsb-governance-strategy#gs-6-define-and-implement-identity-and-privileged-access-strategy:https://learn.microsoft.com/en-us/entra/identity/authentication/concept-authentication-methods",
+ "DefaultValue": "By default, the `Number of days before users are asked to re-confirm their authentication information` is set to 180 days."
+ }
+ ]
+ },
+ {
+ "Id": "6.1",
+ "Description": "Ensure that 'Notify users on password resets?' is set to 'Yes'",
+ "Checks": [],
+ "Attributes": [
+ {
+ "Section": "6 Identity Services",
+ "Profile": "Level 1",
+ "AssessmentStatus": "Manual",
+ "Description": "Ensure that users are notified on their primary and alternate emails on password resets.",
+ "RationaleStatement": "User notification on password reset is a proactive way of confirming password reset activity. It helps the user to recognize unauthorized password reset activities.",
+ "ImpactStatement": "Users will receive emails alerting them to password changes to both their primary and alternate emails.",
+ "RemediationProcedure": "**Remediate from Azure Portal** 1. From Azure Home select the Portal Menu 1. Select `Microsoft Entra ID` 1. Under `Manage`, select `Users` 1. Under `Manage`, select `Password reset` 1. Under `Manage`, select `Notifications` 1. Set `Notify users on password resets?` to `Yes` 1. Click `Save`",
+ "AuditProcedure": "**Audit from Azure Portal** 1. From Azure Home select the Portal Menu 1. Select `Microsoft Entra ID` 1. Under `Manage`, select `Users` 1. Under `Manage`, select `Password reset` 1. Under `Manage`, select `Notifications` 1. Ensure that `Notify users on password resets?` is set to `Yes`",
+ "AdditionalInformation": "",
+ "References": "https://learn.microsoft.com/en-us/entra/identity/authentication/tutorial-enable-sspr#set-up-notifications-and-customizations:https://learn.microsoft.com/en-us/entra/identity/authentication/concept-sspr-howitworks#notifications:https://support.microsoft.com/en-us/account-billing/reset-your-work-or-school-password-using-security-info-23dde81f-08bb-4776-ba72-e6b72b9dda9e:https://learn.microsoft.com/en-us/security/benchmark/azure/mcsb-governance-strategy#gs-6-define-and-implement-identity-and-privileged-access-strategy",
+ "DefaultValue": "By default, `Notify users on password resets?` is set to Yes."
+ }
+ ]
+ },
+ {
+ "Id": "6.11",
+ "Description": "Ensure that 'Notify all admins when other admins reset their password?' is set to 'Yes'",
+ "Checks": [],
+ "Attributes": [
+ {
+ "Section": "6 Identity Services",
+ "Profile": "Level 1",
+ "AssessmentStatus": "Manual",
+ "Description": "Ensure that all Global Administrators are notified if any other administrator resets their password.",
+ "RationaleStatement": "Administrator accounts are sensitive. Any password reset activity notification, when sent to all Administrators, ensures that all Global Administrators can passively confirm if such a reset is a common pattern within their group. For example, if all Administrators change their password every 30 days, any password reset activity before that may require administrator(s) to evaluate any unusual activity and confirm its origin.",
+ "ImpactStatement": "All Global Administrators will receive a notification from Azure every time a password is reset. This is useful for auditing procedures to confirm that there are no out of the ordinary password resets for Administrators. There is additional overhead, however, in the time required for Global Administrators to audit the notifications. This setting is only useful if all Global Administrators pay attention to the notifications and audit each one.",
+ "RemediationProcedure": "**Remediate from Azure Portal** 1. From Azure Home select the Portal Menu 1. Select `Microsoft Entra ID` 1. Under `Manage`, select `Users` 1. Under `Manage`, select `Password reset` 1. Under `Manage`, select `Notifications` 1. Set `Notify all admins when other admins reset their password?` to `Yes` 1. Click `Save`",
+ "AuditProcedure": "**Audit from Azure Portal** 1. From Azure Home select the Portal Menu 1. Select `Microsoft Entra ID` 1. Under `Manage`, select `Users` 1. Under `Manage`, select `Password reset` 1. Under `Manage`, select `Notifications` 1. Ensure that `Notify all admins when other admins reset their password?` is set to `Yes`",
+ "AdditionalInformation": "",
+ "References": "https://learn.microsoft.com/en-us/entra/identity/authentication/concept-sspr-howitworks#notifications:https://support.microsoft.com/en-us/account-billing/reset-your-work-or-school-password-using-security-info-23dde81f-08bb-4776-ba72-e6b72b9dda9e:https://learn.microsoft.com/en-us/security/benchmark/azure/mcsb-governance-strategy#gs-6-define-and-implement-identity-and-privileged-access-strategy:https://learn.microsoft.com/en-us/security/benchmark/azure/mcsb-privileged-access#pa-1-separate-and-limit-highly-privilegedadministrative-users:https://learn.microsoft.com/en-us/entra/identity/authentication/tutorial-enable-sspr#set-up-notifications-and-customizations",
+ "DefaultValue": "By default, `Notify all admins when other admins reset their password?` is set to No."
+ }
+ ]
+ },
+ {
+ "Id": "6.17",
+ "Description": "Ensure that 'Restrict access to Microsoft Entra admin center' is set to 'Yes'",
+ "Checks": [],
+ "Attributes": [
+ {
+ "Section": "6 Identity Services",
+ "Profile": "Level 1",
+ "AssessmentStatus": "Manual",
+ "Description": "Restrict access to the Microsoft Entra ID administration center to administrators only. **NOTE**: This only affects access to the Entra ID administrator's web portal. This setting does not prohibit privileged users from using other methods such as Rest API or Powershell to obtain sensitive information from Microsoft Entra ID.",
+ "RationaleStatement": "The Microsoft Entra ID administrative center has sensitive data and permission settings. All non-administrators should be prohibited from accessing any Microsoft Entra ID data in the administration center to avoid exposure.",
+ "ImpactStatement": "All administrative tasks will need to be done by Administrators, causing additional overhead in management of users and resources.",
+ "RemediationProcedure": "**Remediate from Azure Portal** 1. From Azure Home select the Portal Menu 1. Select `Microsoft Entra ID` 1. Under `Manage`, select `Users` 1. Under `Manage`, select `User settings` 1. Under `Administration centre`, set `Restrict access to Microsoft Entra admin center` to `Yes` 1. Click `Save`",
+ "AuditProcedure": "**Audit from Azure Portal** 1. From Azure Home select the Portal Menu 1. Select `Microsoft Entra ID` 1. Under `Manage`, select `Users` 1. Under `Manage`, select `User settings` 1. Under `Administration centre`, ensure that `Restrict access to Microsoft Entra admin center` is set to `Yes`",
+ "AdditionalInformation": "",
+ "References": "https://docs.microsoft.com/en-us/azure/active-directory/active-directory-assign-admin-roles-azure-portal:https://learn.microsoft.com/en-us/security/benchmark/azure/mcsb-governance-strategy#gs-2-define-and-implement-enterprise-segmentationseparation-of-duties-strategy:https://learn.microsoft.com/en-us/security/benchmark/azure/mcsb-governance-strategy#gs-6-define-and-implement-identity-and-privileged-access-strategy:https://learn.microsoft.com/en-us/security/benchmark/azure/mcsb-privileged-access#pa-1-separate-and-limit-highly-privilegedadministrative-users",
+ "DefaultValue": "By default, `Restrict access to Microsoft Entra admin center` is set to `No`"
+ }
+ ]
+ },
+ {
+ "Id": "7.1.1.1",
+ "Description": "Ensure that a 'Diagnostic Setting' exists for Subscription Activity Logs",
+ "Checks": [
+ "monitor_diagnostic_settings_exists"
+ ],
+ "Attributes": [
+ {
+ "Section": "7 Management and Governance Services",
+ "SubSection": "7.1 Logging and Monitoring",
+ "Profile": "Level 1",
+ "AssessmentStatus": "Manual",
+ "Description": "Enable Diagnostic settings for exporting activity logs. Diagnostic settings are available for each individual resource within a subscription. Settings should be configured for all appropriate resources for your environment.",
+ "RationaleStatement": "A diagnostic setting controls how a diagnostic log is exported. By default, logs are retained only for 90 days. Diagnostic settings should be defined so that logs can be exported and stored for a longer duration to analyze security activities within an Azure subscription.",
+ "ImpactStatement": "",
+ "RemediationProcedure": "**Remediate from Azure Portal** To enable Diagnostic Settings on a Subscription: 1. Go to `Monitor` 2. Click on `Activity log` 3. Click on `Export Activity Logs` 4. Click `+ Add diagnostic setting` 5. Enter a `Diagnostic setting name` 6. Select `Categories` for the diagnostic setting 7. Select the appropriate `Destination details` (this may be Log Analytics, Storage Account, Event Hub, or Partner solution) 8. Click `Save` To enable Diagnostic Settings on a specific resource: 1. Go to `Monitoring` 1. Click `Diagnostic settings` 1. Select `Add diagnostic setting` 1. Enter a `Diagnostic setting name` 1. Select the appropriate log, metric, and destination (this may be Log Analytics, Storage Account, Event Hub, or Partner solution) 1. Click `Save` Repeat these step for all resources as needed. **Remediate from Azure CLI** To configure Diagnostic Settings on a Subscription: ``` az monitor diagnostic-settings subscription create --subscription --name --location <[--event-hub --event-hub-auth-rule ] [--storage-account ] [--workspace ] --logs (e.g. [{category:Security,enabled:true},{category:Administrative,enabled:true},{category:Alert,enabled:true},{category:Policy,enabled:true}]) ``` To configure Diagnostic Settings on a specific resource: ``` az monitor diagnostic-settings create --subscription --resource --name <[--event-hub --event-hub-rule ] [--storage-account ] [--workspace ] --logs --metrics ``` **Remediate from PowerShell** To configure Diagnostic Settings on a subscription: ``` $logCategories = @(); $logCategories += New-AzDiagnosticSettingSubscriptionLogSettingsObject -Category Administrative -Enabled $true $logCategories += New-AzDiagnosticSettingSubscriptionLogSettingsObject -Category Security -Enabled $true $logCategories += New-AzDiagnosticSettingSubscriptionLogSettingsObject -Category ServiceHealth -Enabled $true $logCategories += New-AzDiagnosticSettingSubscriptionLogSettingsObject -Category Alert -Enabled $true $logCategories += New-AzDiagnosticSettingSubscriptionLogSettingsObject -Category Recommendation -Enabled $true $logCategories += New-AzDiagnosticSettingSubscriptionLogSettingsObject -Category Policy -Enabled $true $logCategories += New-AzDiagnosticSettingSubscriptionLogSettingsObject -Category Autoscale -Enabled $true $logCategories += New-AzDiagnosticSettingSubscriptionLogSettingsObject -Category ResourceHealth -Enabled $true New-AzSubscriptionDiagnosticSetting -SubscriptionId -Name <[-EventHubAuthorizationRule -EventHubName ] [-StorageAccountId ] [-WorkSpaceId ] [-MarketplacePartner ID ]> -Log $logCategories ``` To configure Diagnostic Settings on a specific resource: ``` $logCategories = @() $logCategories += New-AzDiagnosticSettingLogSettingsObject -Category -Enabled $true Repeat command and variable assignment for each Log category specific to the resource where this Diagnostic Setting will get configured. $metricCategories = @() $metricCategories += New-AzDiagnosticSettingMetricSettingsObject -Enabled $true [-Category ] [-RetentionPolicyDay ] [-RetentionPolicyEnabled $true] Repeat command and variable assignment for each Metric category or use the 'AllMetrics' category. New-AzDiagnosticSetting -ResourceId -Name -Log $logCategories -Metric $metricCategories [-EventHubAuthorizationRuleId -EventHubName ] [-StorageAccountId ] [-WorkspaceId ] [-MarketplacePartnerId ]>",
+ "AuditProcedure": "**Audit from Azure Portal** To identify Diagnostic Settings on a subscription: 1. Go to `Monitor` 2. Click `Activity Log` 3. Click `Export Activity Logs` 4. Select a `Subscription` 5. Ensure a `Diagnostic setting` exists for the selected Subscription To identify Diagnostic Settings on specific resources: 1. Go to `Monitoring` 2. Click `Diagnostic settings` 3. Ensure a `Diagnostic setting` exists for all appropriate resources. **Audit from Azure CLI** To identify Diagnostic Settings on a subscription: ``` az monitor diagnostic-settings subscription list --subscription ``` To identify Diagnostic Settings on a resource ``` az monitor diagnostic-settings list --resource ``` **Audit from PowerShell** To identify Diagnostic Settings on a Subscription: ``` Get-AzDiagnosticSetting -SubscriptionId ``` To identify Diagnostic Settings on a specific resource: ``` Get-AzDiagnosticSetting -ResourceId ```",
+ "AdditionalInformation": "",
+ "References": "https://docs.microsoft.com/en-us/azure/monitoring-and-diagnostics/monitoring-overview-activity-logs#export-the-activity-log-with-a-log-profile:https://learn.microsoft.com/en-us/cli/azure/monitor/diagnostic-settings?view=azure-cli-latest:https://learn.microsoft.com/en-us/security/benchmark/azure/mcsb-logging-threat-detection#lt-3-enable-logging-for-security-investigation",
+ "DefaultValue": "By default, diagnostic setting is not set."
+ }
+ ]
+ },
+ {
+ "Id": "7.1.1.2",
+ "Description": "Ensure Diagnostic Setting captures appropriate categories",
+ "Checks": [
+ "monitor_diagnostic_setting_with_appropriate_categories"
+ ],
+ "Attributes": [
+ {
+ "Section": "7 Management and Governance Services",
+ "SubSection": "7.1 Logging and Monitoring",
+ "Profile": "Level 1",
+ "AssessmentStatus": "Automated",
+ "Description": "**Prerequisite**: A Diagnostic Setting must exist. If a Diagnostic Setting does not exist, the navigation and options within this recommendation will not be available. Please review the recommendation at the beginning of this subsection titled: Ensure that a 'Diagnostic Setting' exists. The diagnostic setting should be configured to log the appropriate activities from the control/management plane.",
+ "RationaleStatement": "A diagnostic setting controls how the diagnostic log is exported. Capturing the diagnostic setting categories for appropriate control/management plane activities allows proper alerting.",
+ "ImpactStatement": "",
+ "RemediationProcedure": "**Remediate from Azure Portal** 1. Go to `Monitor`. 1. Click `Activity log`. 1. Click on `Export Activity Logs`. 1. Select the `Subscription` from the drop down menu. 1. Click `Edit setting` next to a diagnostic setting. 1. Check the following categories: `Administrative, Alert, Policy, and Security`. 1. Choose the destination details according to your organization's needs. 1. Click `Save`. **Remediate from Azure CLI** ``` az monitor diagnostic-settings subscription create --subscription --name --location <[--event-hub --event-hub-auth-rule ] [--storage-account ] [--workspace ] --logs [{category:Security,enabled:true},{category:Administrative,enabled:true},{category:Alert,enabled:true},{category:Policy,enabled:true}] ``` **Remediate from PowerShell** ``` $logCategories = @(); $logCategories += New-AzDiagnosticSettingSubscriptionLogSettingsObject -Category Administrative -Enabled $true $logCategories += New-AzDiagnosticSettingSubscriptionLogSettingsObject -Category Security -Enabled $true $logCategories += New-AzDiagnosticSettingSubscriptionLogSettingsObject -Category Alert -Enabled $true $logCategories += New-AzDiagnosticSettingSubscriptionLogSettingsObject -Category Policy -Enabled $true New-AzSubscriptionDiagnosticSetting -SubscriptionId -Name <[-EventHubAuthorizationRule -EventHubName ] [-StorageAccountId ] [-WorkSpaceId ] [-MarketplacePartner ID ]> -Log $logCategories ```",
+ "AuditProcedure": "**Audit from Azure Portal** 1. Go to `Monitor`. 1. Click `Activity log`. 1. Click on `Export Activity Logs`. 1. Select the appropriate `Subscription`. 1. Click `Edit setting` next to a diagnostic setting. 1. Ensure that the following categories are checked: `Administrative, Alert, Policy, and Security`. **Audit from Azure CLI** Ensure the categories `'Administrative', 'Alert', 'Policy', and 'Security'` set to: 'enabled: true' ``` az monitor diagnostic-settings subscription list --subscription ``` **Audit from PowerShell** Ensure the categories Administrative, Alert, Policy, and Security are set to Enabled:True ``` Get-AzSubscriptionDiagnosticSetting -Subscription ``` **Audit from Azure Policy** If referencing a digital copy of this Benchmark, clicking a Policy ID will open a link to the associated Policy definition in Azure. If referencing a printed copy, you can search Policy IDs from this URL: https://portal.azure.com/#view/Microsoft_Azure_Policy/PolicyMenuBlade/~/Definitions - **Policy ID:** [3b980d31-7904-4bb7-8575-5665739a8052](https://portal.azure.com/#view/Microsoft_Azure_Policy/PolicyDetailBlade/definitionId/%2Fproviders%2FMicrosoft.Authorization%2FpolicyDefinitions%2F3b980d31-7904-4bb7-8575-5665739a8052) **- Name:** 'An activity log alert should exist for specific Security operations' - **Policy ID:** [b954148f-4c11-4c38-8221-be76711e194a](https://portal.azure.com/#view/Microsoft_Azure_Policy/PolicyDetailBlade/definitionId/%2Fproviders%2FMicrosoft.Authorization%2FpolicyDefinitions%2Fb954148f-4c11-4c38-8221-be76711e194a) **- Name:** 'An activity log alert should exist for specific Administrative operations' - **Policy ID:** [c5447c04-a4d7-4ba8-a263-c9ee321a6858](https://portal.azure.com/#view/Microsoft_Azure_Policy/PolicyDetailBlade/definitionId/%2Fproviders%2FMicrosoft.Authorization%2FpolicyDefinitions%2Fc5447c04-a4d7-4ba8-a263-c9ee321a6858) **- Name:** 'An activity log alert should exist for specific Policy operations'",
+ "AdditionalInformation": "",
+ "References": "https://docs.microsoft.com/en-us/azure/azure-monitor/platform/diagnostic-settings:https://docs.microsoft.com/en-us/azure/azure-monitor/samples/resource-manager-diagnostic-settings:https://learn.microsoft.com/en-us/security/benchmark/azure/mcsb-logging-threat-detection#lt-3-enable-logging-for-security-investigation:https://learn.microsoft.com/en-us/cli/azure/monitor/diagnostic-settings?view=azure-cli-latest:https://learn.microsoft.com/en-us/powershell/module/az.monitor/new-azsubscriptiondiagnosticsetting?view=azps-9.2.0",
+ "DefaultValue": "When the diagnostic setting is created using Azure Portal, by default no categories are selected."
+ }
+ ]
+ },
+ {
+ "Id": "7.1.1.3",
+ "Description": "Ensure the storage account containing the container with activity logs is encrypted with Customer Managed Key (CMK)",
+ "Checks": [
+ "monitor_storage_account_with_activity_logs_cmk_encrypted"
+ ],
+ "Attributes": [
+ {
+ "Section": "7 Management and Governance Services",
+ "SubSection": "7.1 Logging and Monitoring",
+ "Profile": "Level 2",
+ "AssessmentStatus": "Automated",
+ "Description": "Storage accounts with the activity log exports can be configured to use Customer Managed Keys (CMK).",
+ "RationaleStatement": "Configuring the storage account with the activity log export container to use CMKs provides additional confidentiality controls on log data, as a given user must have read permission on the corresponding storage account and must be granted decrypt permission by the CMK.",
+ "ImpactStatement": "**NOTE:** You must have your key vault setup to utilize this. All Audit Logs will be encrypted with a key you provide. You will need to set up customer managed keys separately, and you will select which key to use via the instructions here. You will be responsible for the lifecycle of the keys, and will need to manually replace them at your own determined intervals to keep the data secure.",
+ "RemediationProcedure": "**Remediate from Azure Portal** 1. Go to `Monitor`. 1. Select `Activity log`. 1. Select `Export Activity Logs`. 1. Select a `Subscription`. 1. Note the name of the `Storage Account` for the diagnostic setting. 1. Navigate to `Storage accounts`. 1. Click on the storage account. 1. Under `Security + networking`, click `Encryption`. 1. Next to `Encryption type`, select `Customer-managed keys`. 1. Complete the steps to configure a customer-managed key for encryption of the storage account. **Remediate from Azure CLI** ``` az storage account update --name --resource-group --encryption-key-source=Microsoft.Keyvault --encryption-key-vault --encryption-key-name --encryption-key-version ``` **Remediate from PowerShell** ``` Set-AzStorageAccount -ResourceGroupName -Name -KeyvaultEncryption -KeyVaultUri -KeyName ```",
+ "AuditProcedure": "**Audit from Azure Portal** 1. Go to `Monitor`. 1. Select `Activity log`. 1. Select `Export Activity Logs`. 1. Select a `Subscription`. 1. Note the name of the `Storage Account` for the diagnostic setting. 1. Navigate to `Storage accounts`. 1. Click on the storage account name noted in Step 5. 1. Under `Security + networking`, click `Encryption`. 1. Ensure `Customer-managed keys` is selected and a key is set. **Audit from Azure CLI** 1. Get storage account id configured with log profile: ``` az monitor diagnostic-settings subscription list --subscription --query 'value[*].storageAccountId' ``` 2. Ensure the storage account is encrypted with CMK: ``` az storage account list --query [?name==''] ``` In command output ensure `keySource` is set to `Microsoft.Keyvault` and `keyVaultProperties` is not set to `null` **Audit from PowerShell** ``` Get-AzStorageAccount -ResourceGroupName -Name |select-object -ExpandProperty encryption|format-list ``` Ensure the value of `KeyVaultProperties` is not `null` or empty, and ensure `KeySource` is not set to `Microsoft.Storage`. **Audit from Azure Policy** If referencing a digital copy of this Benchmark, clicking a Policy ID will open a link to the associated Policy definition in Azure. If referencing a printed copy, you can search Policy IDs from this URL: https://portal.azure.com/#view/Microsoft_Azure_Policy/PolicyMenuBlade/~/Definitions - **Policy ID:** [fbb99e8e-e444-4da0-9ff1-75c92f5a85b2](https://portal.azure.com/#view/Microsoft_Azure_Policy/PolicyDetailBlade/definitionId/%2Fproviders%2FMicrosoft.Authorization%2FpolicyDefinitions%2Ffbb99e8e-e444-4da0-9ff1-75c92f5a85b2) **- Name:** 'Storage account containing the container with activity logs must be encrypted with BYOK'",
+ "AdditionalInformation": "",
+ "References": "https://learn.microsoft.com/en-us/security/benchmark/azure/mcsb-data-protection#dp-5-use-customer-managed-key-option-in-data-at-rest-encryption-when-required:https://learn.microsoft.com/en-us/azure/azure-monitor/essentials/activity-log?tabs=cli#managing-legacy-log-profiles",
+ "DefaultValue": "By default, for a storage account `keySource` is set to `Microsoft.Storage` allowing encryption with vendor Managed key and not a Customer Managed Key."
+ }
+ ]
+ },
+ {
+ "Id": "7.1.1.4",
+ "Description": "Ensure that logging for Azure Key Vault is 'Enabled'",
+ "Checks": [
+ "keyvault_logging_enabled"
+ ],
+ "Attributes": [
+ {
+ "Section": "7 Management and Governance Services",
+ "SubSection": "7.1 Logging and Monitoring",
+ "Profile": "Level 1",
+ "AssessmentStatus": "Automated",
+ "Description": "Enable AuditEvent logging for key vault instances to ensure interactions with key vaults are logged and available.",
+ "RationaleStatement": "Monitoring how and when key vaults are accessed, and by whom, enables an audit trail of interactions with confidential information, keys, and certificates managed by Azure Key Vault. Enabling logging for Key Vault saves information in a user provided destination of either an Azure storage account or Log Analytics workspace. The same destination can be used for collecting logs for multiple Key Vaults.",
+ "ImpactStatement": "",
+ "RemediationProcedure": "**Remediate from Azure Portal** 1. Go to `Key vaults`. 2. Select a Key vault. 3. Under `Monitoring`, select `Diagnostic settings`. 4. Click `Edit setting` to update an existing diagnostic setting, or `Add diagnostic setting` to create a new one. 5. If creating a new diagnostic setting, provide a name. 6. Configure an appropriate destination. 7. Under `Category groups`, check `audit` and `allLogs`. 8. Click `Save`. **Remediate from Azure CLI** To update an existing `Diagnostic Settings` ``` az monitor diagnostic-settings update --name --resource ``` To create a new `Diagnostic Settings` ``` az monitor diagnostic-settings create --name --resource --logs [{category:audit,enabled:true},{category:allLogs,enabled:true}] --metrics [{category:AllMetrics,enabled:true}] <[--event-hub --event-hub-rule | --storage-account |--workspace | --marketplace-partner-id ]> ``` **Remediate from PowerShell** Create the `Log` settings object ``` $logSettings = @() $logSettings += New-AzDiagnosticSettingLogSettingsObject -Enabled $true -Category audit $logSettings += New-AzDiagnosticSettingLogSettingsObject -Enabled $true -Category allLogs ``` Create the `Metric` settings object ``` $metricSettings = @() $metricSettings += New-AzDiagnosticSettingMetricSettingsObject -Enabled $true -Category AllMetrics ``` Create the `Diagnostic Settings` for each `Key Vault` ``` New-AzDiagnosticSetting -Name -ResourceId -Log $logSettings -Metric $metricSettings [-StorageAccountId | -EventHubName -EventHubAuthorizationRuleId | -WorkSpaceId | -MarketPlacePartnerId ] ```",
+ "AuditProcedure": "**Audit from Azure Portal** 1. Go to `Key vaults`. 1. For each Key vault, under `Monitoring`, go to `Diagnostic settings`. 1. Click `Edit setting` next to a diagnostic setting. 1. Ensure that a destination is configured. 1. Under `Category groups`, ensure that `audit` and `allLogs` are checked. **Audit from Azure CLI** List all key vaults ``` az keyvault list ``` For each keyvault `id` ``` az monitor diagnostic-settings list --resource ``` Ensure that `storageAccountId` reflects your desired destination and that `categoryGroup` and `enabled` are set as follows in the sample outputs below. ``` logs: [ { categoryGroup: audit, enabled: true, }, { categoryGroup: allLogs, enabled: true, } ``` **Audit from PowerShell** List the key vault(s) in the subscription ``` Get-AzKeyVault ``` For each key vault, run the following: ``` Get-AzDiagnosticSetting -ResourceId ``` Ensure that `StorageAccountId`, `ServiceBusRuleId`, `MarketplacePartnerId`, or `WorkspaceId` is set as appropriate. Also, ensure that `enabled` is set to `true`, and that `categoryGroup` reflects both `audit` and `allLogs` category groups. **Audit from Azure Policy** If referencing a digital copy of this Benchmark, clicking a Policy ID will open a link to the associated Policy definition in Azure. If referencing a printed copy, you can search Policy IDs from this URL: https://portal.azure.com/#view/Microsoft_Azure_Policy/PolicyMenuBlade/~/Definitions - **Policy ID:** [cf820ca0-f99e-4f3e-84fb-66e913812d21](https://portal.azure.com/#view/Microsoft_Azure_Policy/PolicyDetailBlade/definitionId/%2Fproviders%2FMicrosoft.Authorization%2FpolicyDefinitions%2Fcf820ca0-f99e-4f3e-84fb-66e913812d21) **- Name:** 'Resource logs in Key Vault should be enabled'",
+ "AdditionalInformation": "**DEPRECATION WARNING** Retention rules for Key Vault logging is being migrated to Azure Storage Lifecycle Management. Retention rules should be set based on the needs of your organization and security or compliance frameworks. Please visit [https://learn.microsoft.com/en-us/azure/azure-monitor/essentials/migrate-to-azure-storage-lifecycle-policy?tabs=portal](https://learn.microsoft.com/en-us/azure/azure-monitor/essentials/migrate-to-azure-storage-lifecycle-policy?tabs=portal) for detail on migrating your retention rules. Microsoft has provided the following deprecation timeline: March 31, 2023 – The Diagnostic Settings Storage Retention feature will no longer be available to configure new retention rules for log data. This includes using the portal, CLI PowerShell, and ARM and Bicep templates. If you have configured retention settings, you'll still be able to see and change them in the portal. March 31, 2024 – You will no longer be able to use the API (CLI, Powershell, or templates), or Azure portal to configure retention setting unless you're changing them to 0. Existing retention rules will still be respected. September 30, 2025 – All retention functionality for the Diagnostic Settings Storage Retention feature will be disabled across all environments.",
+ "References": "https://docs.microsoft.com/en-us/azure/key-vault/general/howto-logging:https://learn.microsoft.com/en-us/security/benchmark/azure/mcsb-data-protection#dp-8-ensure-security-of-key-and-certificate-repository:https://learn.microsoft.com/en-us/security/benchmark/azure/mcsb-logging-threat-detection#lt-3-enable-logging-for-security-investigation",
+ "DefaultValue": "By default, Diagnostic AuditEvent logging is not enabled for Key Vault instances."
+ }
+ ]
+ },
+ {
+ "Id": "7.1.1.5",
+ "Description": "Ensure that Network Security Group Flow logs are captured and sent to Log Analytics",
+ "Checks": [
+ "network_flow_log_captured_sent"
+ ],
+ "Attributes": [
+ {
+ "Section": "7 Management and Governance Services",
+ "SubSection": "7.1 Logging and Monitoring",
+ "Profile": "Level 2",
+ "AssessmentStatus": "Manual",
+ "Description": "Ensure that network flow logs are captured and fed into a central log analytics workspace. **Retirement Notice** On September 30, 2027, network security group (NSG) flow logs will be retired. Starting June 30, 2025, it will no longer be possible to create new NSG flow logs. Azure recommends migrating to virtual network flow logs. Review https://azure.microsoft.com/en-gb/updates?id=Azure-NSG-flow-logs-Retirement for more information. For virtual network flow logs, consider applying the recommendation `Ensure that virtual network flow logs are captured and sent to Log Analytics` in this section.",
+ "RationaleStatement": "Network Flow Logs provide valuable insight into the flow of traffic around your network and feed into both Azure Monitor and Azure Sentinel (if in use), permitting the generation of visual flow diagrams to aid with analyzing for lateral movement, etc.",
+ "ImpactStatement": "The impact of configuring NSG Flow logs is primarily one of cost and configuration. If deployed, it will create storage accounts that hold minimal amounts of data on a 5-day lifecycle before feeding to Log Analytics Workspace. This will increase the amount of data stored and used by Azure Monitor.",
+ "RemediationProcedure": "**Remediate from Azure Portal** 1. Navigate to `Network Watcher`. 1. Under `Logs`, select `Flow logs`. 1. Select `+ Create`. 1. Select the desired Subscription. 1. For `Flow log type`, select `Network security group`. 1. Select `+ Select target resource`. 1. Select `Network security group`. 1. Select a network security group. 1. Click `Confirm selection`. 1. Select or create a new Storage Account. 1. If using a v2 storage account, input the retention in days to retain the log. 1. Click `Next`. 1. Under `Analytics`, for `Flow log version`, select `Version 2`. 1. Check the box next to `Enable traffic analytics`. 1. Select a processing interval. 1. Select a `Log Analytics Workspace`. 1. Select `Next`. 1. Optionally add Tags. 1. Select `Review + create`. 1. Select `Create`. ***Warning*** The remediation policy creates remediation deployment and names them by concatenating the subscription name and the resource group name. The MAXIMUM permitted length of a deployment name is 64 characters. Exceeding this will cause the remediation task to fail.",
+ "AuditProcedure": "**Audit from Azure Portal** 1. Navigate to `Network Watcher`. 1. Under `Logs`, select `Flow logs`. 1. Click `Add filter`. 1. From the `Filter` drop-down, select `Flow log type`. 1. From the `Value` drop-down, check `Network security group` only. 1. Click `Apply`. 1. Ensure that at least one network security group flow log is listed and is configured to send logs to a `Log Analytics Workspace`. **Audit from Azure Policy** If referencing a digital copy of this Benchmark, clicking a Policy ID will open a link to the associated Policy definition in Azure. If referencing a printed copy, you can search Policy IDs from this URL: https://portal.azure.com/#view/Microsoft_Azure_Policy/PolicyMenuBlade/~/Definitions - **Policy ID:** [27960feb-a23c-4577-8d36-ef8b5f35e0be](https://portal.azure.com/#view/Microsoft_Azure_Policy/PolicyDetailBlade/definitionId/%2Fproviders%2FMicrosoft.Authorization%2FpolicyDefinitions%2F27960feb-a23c-4577-8d36-ef8b5f35e0be) **- Name:** 'All flow log resources should be in enabled state' - **Policy ID:** [c251913d-7d24-4958-af87-478ed3b9ba41](https://portal.azure.com/#view/Microsoft_Azure_Policy/PolicyDetailBlade/definitionId/%2Fproviders%2FMicrosoft.Authorization%2FpolicyDefinitions%2Fc251913d-7d24-4958-af87-478ed3b9ba41) **- Name:** 'Flow logs should be configured for every network security group' - **Policy ID:** [4c3c6c5f-0d47-4402-99b8-aa543dd8bcee](https://portal.azure.com/#view/Microsoft_Azure_Policy/PolicyDetailBlade/definitionId/%2Fproviders%2FMicrosoft.Authorization%2FpolicyDefinitions%2F4c3c6c5f-0d47-4402-99b8-aa543dd8bcee) **- Name:** 'Flow logs should be configured for every virtual network'",
+ "AdditionalInformation": "",
+ "References": "https://docs.microsoft.com/en-us/azure/network-watcher/network-watcher-nsg-flow-logging-portal:https://learn.microsoft.com/en-us/security/benchmark/azure/mcsb-logging-threat-detection#lt-4-enable-network-logging-for-security-investigation",
+ "DefaultValue": "By default Network Security Group logs are not sent to Log Analytics."
+ }
+ ]
+ },
+ {
+ "Id": "7.1.1.6",
+ "Description": "Ensure that logging for Azure AppService 'HTTP logs' is enabled",
+ "Checks": [
+ "app_http_logs_enabled"
+ ],
+ "Attributes": [
+ {
+ "Section": "7 Management and Governance Services",
+ "SubSection": "7.1 Logging and Monitoring",
+ "Profile": "Level 2",
+ "AssessmentStatus": "Automated",
+ "Description": "Enable AppServiceHTTPLogs diagnostic log category for Azure App Service instances to ensure all http requests are captured and centrally logged.",
+ "RationaleStatement": "Capturing web requests can be important supporting information for security analysts performing monitoring and incident response activities. Once logging, these logs can be ingested into SIEM or other central aggregation point for the organization.",
+ "ImpactStatement": "Log consumption and processing will incur additional cost.",
+ "RemediationProcedure": "**Remediate from Azure Portal** 1. Go to `App Services`. For each `App Service`: 2. Under `Monitoring`, go to `Diagnostic settings`. 3. To update an existing diagnostic setting, click `Edit setting` against the setting. To create a new diagnostic setting, click `Add diagnostic setting` and provide a name for the new setting. 4. Check the checkbox next to `HTTP logs`. 5. Configure a destination based on your specific logging consumption capability (for example Stream to an event hub and then consuming with SIEM integration for Event Hub logging). 6. Click `Save`.",
+ "AuditProcedure": "**Audit from Azure Portal** 1. Go to `App Services`. For each `App Service`: 2. Under `Monitoring`, go to `Diagnostic settings`. 3. Ensure a diagnostic setting exists that logs `HTTP logs` to a destination aligned to your environment's approach to log consumption (event hub, storage account, etc. dependent on what is consuming the logs such as SIEM or other log aggregation utility). **Audit from Azure Policy** If referencing a digital copy of this Benchmark, clicking a Policy ID will open a link to the associated Policy definition in Azure. If referencing a printed copy, you can search Policy IDs from this URL: https://portal.azure.com/#view/Microsoft_Azure_Policy/PolicyMenuBlade/~/Definitions - **Policy ID:** [91a78b24-f231-4a8a-8da9-02c35b2b6510](https://portal.azure.com/#view/Microsoft_Azure_Policy/PolicyDetailBlade/definitionId/%2Fproviders%2FMicrosoft.Authorization%2FpolicyDefinitions%2F91a78b24-f231-4a8a-8da9-02c35b2b6510) **- Name:** 'App Service apps should have resource logs enabled' - **Policy ID:** [d639b3af-a535-4bef-8dcf-15078cddf5e2](https://portal.azure.com/#view/Microsoft_Azure_Policy/PolicyDetailBlade/definitionId/%2Fproviders%2FMicrosoft.Authorization%2FpolicyDefinitions%2Fd639b3af-a535-4bef-8dcf-15078cddf5e2) **- Name:** 'App Service app slots should have resource logs enabled'",
+ "AdditionalInformation": "",
+ "References": "https://docs.microsoft.com/en-us/azure/app-service/troubleshoot-diagnostic-logs:https://learn.microsoft.com/en-us/security/benchmark/azure/mcsb-logging-threat-detection#lt-3-enable-logging-for-security-investigation",
+ "DefaultValue": "Not configured."
+ }
+ ]
+ },
+ {
+ "Id": "6.18",
+ "Description": "Ensure that 'Restrict user ability to access groups features in My Groups' is set to 'Yes'",
+ "Checks": [],
+ "Attributes": [
+ {
+ "Section": "6 Identity Services",
+ "Profile": "Level 2",
+ "AssessmentStatus": "Manual",
+ "Description": "Restrict access to group web interface in the Access Panel portal.",
+ "RationaleStatement": "Self-service group management enables users to create and manage security groups or Office 365 groups in Microsoft Entra ID. Unless a business requires this day-to-day delegation for some users, self-service group management should be disabled. Any user can access the Access Panel, where they can reset their passwords, view their information, etc. By default, users are also allowed to access the Group feature, which shows groups, members, related resources (SharePoint URL, Group email address, Yammer URL, and Teams URL). By setting this feature to 'Yes', users will no longer have access to the web interface, but still have access to the data using the API. This is useful to prevent non-technical users from enumerating groups-related information, but technical users will still be able to access this information using APIs.",
+ "ImpactStatement": "Setting to `Yes` could create administrative overhead by customers seeking certain group memberships that will have to be manually managed by administrators with appropriate permissions.",
+ "RemediationProcedure": "**Remediate from Azure Portal** 1. From Azure Home select the Portal Menu 1. Select `Microsoft Entra ID` 1. Under `Manage`, select `Groups` 1. Under `Settings`, select `General` 1. Under `Self Service Group Management`, set `Restrict user ability to access groups features in My Groups` to `Yes` 1. Click `Save`",
+ "AuditProcedure": "**Audit from Azure Portal** 1. From Azure Home select the Portal Menu 1. Select `Microsoft Entra ID` 1. Under `Manage`, select `Groups` 1. Under `Settings`, select `General` 1. Under `Self Service Group Management`, ensure that `Restrict user ability to access groups features in My Groups` is set to `Yes`",
+ "AdditionalInformation": "",
+ "References": "https://learn.microsoft.com/en-us/entra/identity/users/groups-self-service-management:https://learn.microsoft.com/en-us/security/benchmark/azure/mcsb-privileged-access#pa-1-separate-and-limit-highly-privilegedadministrative-users:https://learn.microsoft.com/en-us/security/benchmark/azure/mcsb-privileged-access#pa-3-manage-lifecycle-of-identities-and-entitlements:https://learn.microsoft.com/en-us/security/benchmark/azure/mcsb-governance-strategy#gs-2-define-and-implement-enterprise-segmentationseparation-of-duties-strategy:https://learn.microsoft.com/en-us/security/benchmark/azure/mcsb-governance-strategy#gs-6-define-and-implement-identity-and-privileged-access-strategy",
+ "DefaultValue": "By default, `Restrict user ability to access groups features in the Access Pane` is set to `No`"
+ }
+ ]
+ },
+ {
+ "Id": "6.2",
+ "Description": "Ensure that 'Owners can manage group membership requests in My Groups' is set to 'No'",
+ "Checks": [],
+ "Attributes": [
+ {
+ "Section": "6 Identity Services",
+ "Profile": "Level 2",
+ "AssessmentStatus": "Manual",
+ "Description": "Restrict security group management to administrators only.",
+ "RationaleStatement": "Restricting security group management to administrators only prohibits users from making changes to security groups. This ensures that security groups are appropriately managed and their management is not delegated to non-administrators.",
+ "ImpactStatement": "Group Membership for user accounts will need to be handled by Admins and cause administrative overhead.",
+ "RemediationProcedure": "**Remediate from Azure Portal** 1. From Azure Home select the Portal Menu 1. Select `Microsoft Entra ID` 1. Under `Manage`, select `Groups` 1. Under `Settings`, select `General` 1. Under `Self Service Group Management`, set `Owners can manage group membership requests in My Groups` to `No` 1. Click `Save`",
+ "AuditProcedure": "**Audit from Azure Portal** 1. From Azure Home select the Portal Menu 1. Select `Microsoft Entra ID` 1. Under `Manage`, select `Groups` 1. Under `Settings`, select `General` 1. Under `Self Service Group Management`, ensure that `Owners can manage group membership requests in My Groups` is set to `No`",
+ "AdditionalInformation": "",
+ "References": "https://learn.microsoft.com/en-us/entra/identity/users/groups-self-service-management#making-a-group-available-for-end-user-self-service:https://learn.microsoft.com/en-us/security/benchmark/azure/mcsb-privileged-access#pa-1-separate-and-limit-highly-privilegedadministrative-users:https://learn.microsoft.com/en-us/security/benchmark/azure/mcsb-privileged-access#pa-3-manage-lifecycle-of-identities-and-entitlements:https://learn.microsoft.com/en-us/security/benchmark/azure/mcsb-privileged-access#pa-8-determine-access-process-for-cloud-provider-support:https://learn.microsoft.com/en-us/security/benchmark/azure/mcsb-governance-strategy#gs-2-define-and-implement-enterprise-segmentationseparation-of-duties-strategy:https://learn.microsoft.com/en-us/security/benchmark/azure/mcsb-governance-strategy#gs-6-define-and-implement-identity-and-privileged-access-strategy",
+ "DefaultValue": "By default, `Owners can manage group membership requests in My Groups` is set to `No`."
+ }
+ ]
+ },
+ {
+ "Id": "6.22",
+ "Description": "Ensure that 'Require Multifactor Authentication to register or join devices with Microsoft Entra' is set to 'Yes'",
+ "Checks": [],
+ "Attributes": [
+ {
+ "Section": "6 Identity Services",
+ "Profile": "Level 1",
+ "AssessmentStatus": "Manual",
+ "Description": "**NOTE:** This recommendation is only relevant if your subscription is using Per-User MFA. If your organization is licensed to use Conditional Access, the preferred method of requiring MFA to join devices to Entra ID is to use a Conditional Access policy (see additional information below for link). Joining or registering devices to Microsoft Entra ID should require multi-factor authentication.",
+ "RationaleStatement": "Multi-factor authentication is recommended when adding devices to Microsoft Entra ID. When set to `Yes`, users who are adding devices from the internet must first use the second method of authentication before their device is successfully added to the directory. This ensures that rogue devices are not added to the domain using a compromised user account.",
+ "ImpactStatement": "A slight impact of additional overhead, as Administrators will now have to approve every access to the domain.",
+ "RemediationProcedure": "**Remediate from Azure Portal** 1. From Azure Home select the Portal Menu 1. Select `Microsoft Entra ID` 1. Under `Manage`, select `Devices` 1. Under `Manage`, select `Device settings` 1. Under `Microsoft Entra join and registration settings`, set `Require Multifactor Authentication to register or join devices with Microsoft Entra` to `Yes` 1. Click `Save`",
+ "AuditProcedure": "**Audit from Azure Portal** 1. From Azure Home select the Portal Menu 1. Select `Microsoft Entra ID` 1. Under `Manage`, select `Devices` 1. Under `Manage`, select `Device settings` 1. Under `Microsoft Entra join and registration settings`, ensure that `Require Multifactor Authentication to register or join devices with Microsoft Entra` is set to `Yes`",
+ "AdditionalInformation": "If Conditional Access is available, this recommendation should be bypassed in favor of the Conditional Access implementation of requiring Multifactor Authentication to register or join devices with Microsoft Entra. https://learn.microsoft.com/en-us/entra/identity/conditional-access/how-to-policy-mfa-device-register-join",
+ "References": "https://learn.microsoft.com/en-us/entra/identity/conditional-access/how-to-policy-mfa-device-register-join:https://learn.microsoft.com/en-us/security/benchmark/azure/mcsb-identity-management#im-6-use-strong-authentication-controls",
+ "DefaultValue": "By default, `Require Multifactor Authentication to register or join devices with Microsoft Entra` is set to `No`."
+ }
+ ]
+ },
+ {
+ "Id": "6.25",
+ "Description": "Ensure that 'Subscription leaving Microsoft Entra tenant' and 'Subscription entering Microsoft Entra tenant' is set to 'Permit no one'",
+ "Checks": [],
+ "Attributes": [
+ {
+ "Section": "6 Identity Services",
+ "Profile": "Level 2",
+ "AssessmentStatus": "Manual",
+ "Description": "Users who are set as subscription owners are able to make administrative changes to the subscriptions and move them into and out of Microsoft Entra ID.",
+ "RationaleStatement": "Permissions to move subscriptions in and out of a Microsoft Entra tenant must only be given to appropriate administrative personnel. A subscription that is moved into a Microsoft Entra tenant may be within a folder to which other users have elevated permissions. This prevents loss of data or unapproved changes of the objects within by potential bad actors.",
+ "ImpactStatement": "Subscriptions will need to have these settings turned off to be moved.",
+ "RemediationProcedure": "**Remediate from Azure Portal** 1. From the Azure Portal Home select the portal menu 1. Select `Subscriptions` 1. In the `Advanced options` drop-down menu, select `Manage Policies` 1. Set `Subscription leaving Microsoft Entra tenant` and `Subscription entering Microsoft Entra tenant` to `Permit no one` 1. Click `Save changes`",
+ "AuditProcedure": "**Audit from Azure Portal** 1. From the Azure Portal Home select the portal menu 1. Select `Subscriptions` 1. In the `Advanced options` drop-down menu, select `Manage Policies` 1. Ensure `Subscription leaving Microsoft Entra tenant` and `Subscription entering Microsoft Entra tenant` are set to `Permit no one`",
+ "AdditionalInformation": "",
+ "References": "https://docs.microsoft.com/en-us/azure/cost-management-billing/manage/manage-azure-subscription-policy:https://learn.microsoft.com/en-us/entra/fundamentals/how-subscriptions-associated-directory:https://learn.microsoft.com/en-us/security/benchmark/azure/mcsb-identity-management#im-2-protect-identity-and-authentication-systems",
+ "DefaultValue": "By default `Subscription leaving Microsoft Entra tenant` and `Subscription entering Microsoft Entra tenant` are set to `Allow everyone (default)`"
+ }
+ ]
+ },
+ {
+ "Id": "7.1.2.1",
+ "Description": "Ensure that Activity Log Alert exists for Create Policy Assignment",
+ "Checks": [
+ "monitor_alert_create_policy_assignment"
+ ],
+ "Attributes": [
+ {
+ "Section": "7 Management and Governance Services",
+ "SubSection": "7.1 Logging and Monitoring",
+ "Profile": "Level 1",
+ "AssessmentStatus": "Automated",
+ "Description": "Create an activity log alert for the Create Policy Assignment event.",
+ "RationaleStatement": "Monitoring for create policy assignment events gives insight into changes done in Azure policy - assignments and can reduce the time it takes to detect unsolicited changes.",
+ "ImpactStatement": "",
+ "RemediationProcedure": "**Remediate from Azure Portal** 1. Navigate to the `Monitor` blade. 1. Select `Alerts`. 1. Select `Create`. 1. Select `Alert rule`. 1. Choose a subscription. 1. Select `Apply`. 1. Select the `Condition` tab. 1. Click `See all signals`. 1. Select `Create policy assignment (Policy assignment)`. 1. Click `Apply`. 1. Select the `Actions` tab. 1. Click `Select action groups` to select an existing action group, or `Create action group` to create a new action group. 1. Follow the prompts to choose or create an action group. 1. Select the `Details` tab. 1. Select a `Resource group`, provide an `Alert rule name` and an optional `Alert rule description`. 1. Click `Review + create`. 1. Click `Create`. **Remediate from Azure CLI** ``` az monitor activity-log alert create --resource-group --condition category=Administrative and operationName=Microsoft.Authorization/policyAssignments/write and level= --scope /subscriptions/ --name --subscription --action-group ``` **Remediate from PowerShell** Create the `conditions` object. ``` $conditions = @() $conditions += New-AzActivityLogAlertAlertRuleAnyOfOrLeafConditionObject -Equal Administrative -Field category $conditions += New-AzActivityLogAlertAlertRuleAnyOfOrLeafConditionObject -Equal Microsoft.Authorization/policyAssignments/write -Field operationName $conditions += New-AzActivityLogAlertAlertRuleAnyOfOrLeafConditionObject -Equal Verbose -Field level ``` Get the `Action Group` information and store it in a variable, then create a new `Action` object. ``` $actionGroup = Get-AzActionGroup -ResourceGroupName -Name $actionObject = New-AzActivityLogAlertActionGroupObject -Id $actionGroup.Id ``` Create the `Scope` variable. ``` $scope = /subscriptions/ ``` Create the `Activity Log Alert Rule` for `Microsoft.Authorization/policyAssignments/write` ``` New-AzActivityLogAlert -Name -ResourceGroupName -Condition $conditions -Scope $scope -Location global -Action $actionObject -Subscription -Enabled $true ```",
+ "AuditProcedure": "**Audit from Azure Portal** 1. Navigate to the `Monitor` blade. 1. Click on `Alerts`. 1. In the Alerts window, click on `Alert rules`. 1. Ensure an alert rule exists where the Condition column contains `Operation name=Microsoft.Authorization/policyAssignments/write`. 1. Click on the Alert `Name` associated with the previous step. 1. Ensure the `Condition` panel displays the text `Whenever the Activity Log has an event with Category='Administrative', Operation name='Create policy assignment'` and does not filter on `Level`, `Status` or `Caller`. 1. Ensure the `Actions` panel displays an Action group is assigned to notify the appropriate personnel in your organization. **Audit from Azure CLI** ``` az monitor activity-log alert list --subscription --query [].{Name:name,Enabled:enabled,Condition:condition.allOf,Actions:actions} ``` Look for `Microsoft.Authorization/policyAssignments/write` in the output. If it's missing, generate a finding. **Audit from PowerShell** ``` Get-AzActivityLogAlert -SubscriptionId |where-object {$_.ConditionAllOf.Equal -match Microsoft.Authorization/policyAssignments/write}|select-object Location,Name,Enabled,ResourceGroupName,ConditionAllOf ``` If the output is empty, an `alert rule` for `Create Policy Assignments` is not configured. **Audit from Azure Policy** If referencing a digital copy of this Benchmark, clicking a Policy ID will open a link to the associated Policy definition in Azure. If referencing a printed copy, you can search Policy IDs from this URL: https://portal.azure.com/#view/Microsoft_Azure_Policy/PolicyMenuBlade/~/Definitions - **Policy ID:** [c5447c04-a4d7-4ba8-a263-c9ee321a6858](https://portal.azure.com/#view/Microsoft_Azure_Policy/PolicyDetailBlade/definitionId/%2Fproviders%2FMicrosoft.Authorization%2FpolicyDefinitions%2Fc5447c04-a4d7-4ba8-a263-c9ee321a6858) **- Name:** 'An activity log alert should exist for specific Policy operations'",
+ "AdditionalInformation": "",
+ "References": "https://azure.microsoft.com/en-us/updates/classic-alerting-monitoring-retirement:https://docs.microsoft.com/en-in/azure/azure-monitor/platform/alerts-activity-log:https://docs.microsoft.com/en-in/rest/api/monitor/activitylogalerts/createorupdate:https://docs.microsoft.com/en-in/rest/api/monitor/activitylogalerts/listbysubscriptionid:https://learn.microsoft.com/en-us/security/benchmark/azure/mcsb-logging-threat-detection#lt-3-enable-logging-for-security-investigation:https://docs.microsoft.com/en-in/rest/api/policy/policy-assignments:https://docs.microsoft.com/en-us/azure/azure-monitor/alerts/alerts-log",
+ "DefaultValue": "By default, no monitoring alerts are created."
+ }
+ ]
+ },
+ {
+ "Id": "7.1.2.2",
+ "Description": "Ensure that Activity Log Alert exists for Delete Policy Assignment",
+ "Checks": [
+ "monitor_alert_delete_policy_assignment"
+ ],
+ "Attributes": [
+ {
+ "Section": "7 Management and Governance Services",
+ "SubSection": "7.1 Logging and Monitoring",
+ "Profile": "Level 1",
+ "AssessmentStatus": "Automated",
+ "Description": "Create an activity log alert for the Delete Policy Assignment event.",
+ "RationaleStatement": "Monitoring for delete policy assignment events gives insight into changes done in azure policy - assignments and can reduce the time it takes to detect unsolicited changes.",
+ "ImpactStatement": "",
+ "RemediationProcedure": "**Remediate from Azure Portal** 1. Navigate to the `Monitor` blade. 1. Select `Alerts`. 1. Select `Create`. 1. Select `Alert rule`. 1. Choose a subscription. 1. Select `Apply`. 1. Select the `Condition` tab. 1. Click `See all signals`. 1. Select `Delete policy assignment (Policy assignment)`. 1. Click `Apply`. 1. Select the `Actions` tab. 1. Click `Select action groups` to select an existing action group, or `Create action group` to create a new action group. 1. Follow the prompts to choose or create an action group. 1. Select the `Details` tab. 1. Select a `Resource group`, provide an `Alert rule name` and an optional `Alert rule description`. 1. Click `Review + create`. 1. Click `Create`. **Remediate from Azure CLI** ``` az monitor activity-log alert create --resource-group --condition category=Administrative and operationName=Microsoft.Authorization/policyAssignments/delete and level= --scope /subscriptions/