ktg-plugin-marketplace/plugins/ms-ai-architect/skills/ms-ai-engineering/references/api-management/security-hardening-ai-gateway.md
Kjell Tore Guttormsen 6a7632146e feat(ms-ai-architect): add plugin to open marketplace (v1.5.0 baseline)
Initial addition of ms-ai-architect plugin to the open-source marketplace.
Private content excluded: orchestrator/ (Linear tooling), docs/utredning/
(client investigation), generated test reports and PDF export script.
skill-gen tooling moved from orchestrator/ to scripts/skill-gen/.

Security scan: WARNING (risk 20/100) — no secrets, no injection found.
False positive fixed: added gitleaks:allow to Python variable reference
in output-validation-grounding-verification.md line 109.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-07 17:17:17 +02:00

20 KiB

Security Hardening for AI Gateways in APIM

Last updated: 2026-02 Status: GA Category: API Management & AI Gateway


Introduksjon

Sikkerhet for AI-gateways krever en flerlagstilnaerming som dekker bade tradisjonelle API-sikkerhetstrusler og AI-spesifikke angrepsoverflater. Azure API Management som AI gateway tilbyr over 20 sikkerhetspolicies, fra IP-filtrering og sertifikatvalidering til AI-spesifikk innholdsmoderasjon og prompt injection-forebygging. En godt herdet AI gateway beskytter mot uautorisert tilgang, datalekkasje, prompt injection og misbruk av kostbare AI-ressurser.

For norsk offentlig sektor er sikkerhetsherding av AI-gateways obligatorisk gitt Datatilsynets retningslinjer for AI, NSMs grunnprinsipper for IKT-sikkerhet, Forvaltningslovens krav om forsvarlig saksbehandling, og EU AI Act som stiller krav til hoyrisiko-AI-systemer. En offentlig virksomhet som eksponerer AI-tjenester ma kunne dokumentere at tilstrekkelige sikkerhetstiltak er implementert pa alle nivaer.

Denne referansen dekker seks sikkerhetsomrader: nettverkstilgangskontroll, prompt injection-forebygging, PII-deteksjon og -maskering, mTLS-autentisering, revisjonssporing og compliance-kontroller. Hver seksjon inkluderer APIM policy XML-eksempler, Bicep-maler og anbefalinger for norsk offentlig sektor.


IP-hvitelisting og -filtrering

IP-filter policy

Begrens AI-API-tilgang til kjente IP-adresser eller nettverksomrader:

<policies>
    <inbound>
        <base />
        <!-- Allow only known IP ranges -->
        <ip-filter action="allow">
            <!-- Internal corporate network -->
            <address-range from="10.0.0.0" to="10.255.255.255" />
            <!-- VPN gateway -->
            <address>203.0.113.50</address>
            <!-- Azure Front Door backend IPs -->
            <address-range from="147.243.0.0" to="147.243.255.255" />
            <!-- Specific partner IPs -->
            <address>198.51.100.10</address>
        </ip-filter>
    </inbound>
</policies>

Dynamisk IP-filtrering med Named Values

<policies>
    <inbound>
        <base />
        <!-- Use named values for maintainable IP lists -->
        <ip-filter action="allow">
            <address-range
                from="{{AllowedIpRangeStart}}"
                to="{{AllowedIpRangeEnd}}" />
        </ip-filter>
    </inbound>
</policies>

Nettverksisolering med VNet

For maksimal sikkerhet, deploy APIM i et virtuelt nettverk:

Modus Internett-tilgang VNet-tilgang Anbefalt for
External Ja (gateway) Ja Innbyggertjenester med Front Door foran
Internal Nei Ja Rent interne AI-tjenester
VNet Integration Utgaende til VNet Nei Standard v2-tier
resource apiManagement 'Microsoft.ApiManagement/service@2023-09-01-preview' = {
  name: apimName
  location: location
  sku: {
    name: 'Premium'
    capacity: 1
  }
  properties: {
    virtualNetworkType: 'Internal'  // Kun tilgjengelig via VNet
    virtualNetworkConfiguration: {
      subnetResourceId: apimSubnet.id
    }
  }
}

Prompt Injection-forebygging

Forstar trusselen

Prompt injection er den mest kritiske AI-spesifikke trusselen (OWASP LLM Top 10 #1). Angripere injiserer instruksjoner i brukerinndata for a:

  • Overstyre systemprompt
  • Eksfiltrere sensitiv informasjon
  • Fa modellen til a utfore uautoriserte handlinger
  • Omga sikkerhetsmekanismer

APIM Content Safety Policy

<policies>
    <inbound>
        <base />
        <!-- Azure AI Content Safety for prompt moderation -->
        <llm-content-safety backend-id="content-safety-backend">
            <text-blocklist-ids>
                <id>prompt-injection-patterns</id>
                <id>offensive-content-no</id>
            </text-blocklist-ids>
            <categories>
                <category name="Hate" threshold="2" />
                <category name="Violence" threshold="2" />
                <category name="SelfHarm" threshold="2" />
                <category name="Sexual" threshold="2" />
            </categories>
        </llm-content-safety>
    </inbound>
</policies>

Policy-basert prompt injection-deteksjon

<policies>
    <inbound>
        <base />
        <!-- Check for common prompt injection patterns -->
        <set-variable name="userMessage" value="@{
            var body = context.Request.Body.As<JObject>(preserveContent: true);
            var messages = (JArray)body?["messages"];
            if (messages == null) return "";

            return string.Join(" ", messages
                .Where(m => m["role"]?.ToString() == "user")
                .Select(m => m["content"]?.ToString() ?? ""));
        }" />

        <choose>
            <when condition="@{
                var msg = ((string)context.Variables["userMessage"]).ToLower();
                var injectionPatterns = new[] {
                    "ignore previous instructions",
                    "ignore all instructions",
                    "disregard your system prompt",
                    "you are now",
                    "new instructions:",
                    "override:",
                    "forget everything",
                    "system prompt:",
                    "jailbreak",
                    "do anything now",
                    "developer mode"
                };
                return injectionPatterns.Any(p => msg.Contains(p));
            }">
                <return-response>
                    <set-status code="400" reason="Bad Request" />
                    <set-header name="Content-Type" exists-action="override">
                        <value>application/json</value>
                    </set-header>
                    <set-body>@{
                        return new JObject {
                            ["error"] = new JObject {
                                ["code"] = "content_policy_violation",
                                ["message"] = "Foresporselen ble blokkert av sikkerhetspolicy.",
                                ["request_id"] = context.RequestId.ToString()
                            }
                        }.ToString();
                    }</set-body>
                </return-response>
            </when>
        </choose>

        <!-- Log potential injection attempts -->
        <choose>
            <when condition="@{
                var msg = ((string)context.Variables["userMessage"]).ToLower();
                var suspiciousPatterns = new[] {
                    "system:", "assistant:", "[inst]", "<<sys>>",
                    "\\n\\n", "```", "ignore", "pretend"
                };
                return suspiciousPatterns.Any(p => msg.Contains(p));
            }">
                <trace source="security" severity="warning">
                    <message>@($"Suspicious prompt pattern from {context.Request.IpAddress}, sub: {context.Subscription?.Name}")</message>
                </trace>
            </when>
        </choose>
    </inbound>
</policies>

Microsoft Prompt Shields

For avansert beskyttelse, bruk Microsoft Prompt Shields (via Microsoft Entra Global Secure Access):

Funksjon Beskrivelse
Jailbreak-deteksjon Identifiserer forsok pa a omga sikkerhetsinstruksjoner
Indirect injection Oppdager injeksjon via dokumenter eller URLs
Data exfiltration Blokkerer forsok pa a trekke ut data
Nettverksniva-enforcement Fungerer uavhengig av applikasjonskode

PII-deteksjon og -maskering

PII-filtrering i inbound requests

<policies>
    <inbound>
        <base />
        <!-- Detect and mask PII in prompts -->
        <set-variable name="sanitizedBody" value="@{
            var body = context.Request.Body.As<string>(preserveContent: true);

            // Norwegian national ID (fodselsnummer) - 11 digits
            body = System.Text.RegularExpressions.Regex.Replace(
                body, @"\b(\d{2})(0[1-9]|1[0-2])(\d{2})\d{5}\b", "$1$2$3*****");

            // Email addresses
            body = System.Text.RegularExpressions.Regex.Replace(
                body, @"\b[\w.+-]+@[\w.-]+\.\w{2,}\b", "[EMAIL]");

            // Norwegian phone numbers
            body = System.Text.RegularExpressions.Regex.Replace(
                body, @"\b(?:\+47|0047)?\s*(?:\d\s*){8}\b", "[TELEFON]");

            // Credit card numbers (basic pattern)
            body = System.Text.RegularExpressions.Regex.Replace(
                body, @"\b\d{4}[\s-]?\d{4}[\s-]?\d{4}[\s-]?\d{4}\b", "[KORTNUMMER]");

            // Bank account numbers (Norwegian format)
            body = System.Text.RegularExpressions.Regex.Replace(
                body, @"\b\d{4}\.\d{2}\.\d{5}\b", "[KONTONUMMER]");

            return body;
        }" />

        <!-- Replace request body with sanitized version -->
        <set-body>@((string)context.Variables["sanitizedBody"])</set-body>

        <!-- Log if PII was detected -->
        <choose>
            <when condition="@{
                var original = context.Request.Body.As<string>(preserveContent: true);
                var sanitized = (string)context.Variables["sanitizedBody"];
                return original != sanitized;
            }">
                <trace source="pii-detection" severity="warning">
                    <message>@($"PII detected and masked in request from {context.Subscription?.Name}")</message>
                </trace>
            </when>
        </choose>
    </inbound>
</policies>

PII-filtrering i outbound responses

<policies>
    <outbound>
        <base />
        <!-- Mask PII in AI model responses -->
        <set-body>@{
            var body = context.Response.Body.As<string>(preserveContent: true);

            // Apply same PII patterns as inbound
            body = System.Text.RegularExpressions.Regex.Replace(
                body, @"\b(\d{2})(0[1-9]|1[0-2])(\d{2})\d{5}\b", "$1$2$3*****");
            body = System.Text.RegularExpressions.Regex.Replace(
                body, @"\b[\w.+-]+@[\w.-]+\.\w{2,}\b", "[EMAIL]");
            body = System.Text.RegularExpressions.Regex.Replace(
                body, @"\b(?:\+47|0047)?\s*(?:\d\s*){8}\b", "[TELEFON]");

            return body;
        }</set-body>
    </outbound>
</policies>

PII-deteksjonskategorier

Kategori Monster Eksempel
Fodselsnummer \d{11} 01019012345
E-postadresse standard e-post regex ola@eksempel.no
Telefonnummer +47 / 8 siffer +47 912 34 567
Kortnummer 16 siffer 4111 1111 1111 1111
Kontonummer \d{4}.\d{2}.\d{5} 1234.56.78901
Organisasjonsnr \d{9} 987654321

Mutual TLS (mTLS)

Klient-sertifikatautentisering

For AI-API-er med hoyeste sikkerhetskrav, bruk mTLS:

<policies>
    <inbound>
        <base />
        <!-- Validate client certificate -->
        <choose>
            <when condition="@(context.Request.Certificate == null ||
                !context.Request.Certificate.Verify() ||
                context.Request.Certificate.NotAfter < DateTime.UtcNow)">
                <return-response>
                    <set-status code="403" reason="Forbidden" />
                    <set-body>{"error":{"code":"certificate_required","message":"A valid client certificate is required."}}</set-body>
                </return-response>
            </when>
        </choose>

        <!-- Verify certificate thumbprint against allowed list -->
        <validate-client-certificate
            validate-revocation="true"
            validate-trust="true"
            validate-not-before="true"
            validate-not-after="true">
            <identities>
                <identity
                    thumbprint="{{AllowedThumbprint1}}"
                    certificate-id="client-cert-app1" />
                <identity
                    thumbprint="{{AllowedThumbprint2}}"
                    certificate-id="client-cert-app2" />
            </identities>
        </validate-client-certificate>
    </inbound>
</policies>

Sertifikatbasert tilgangskontroll per AI-modell

<policies>
    <inbound>
        <base />
        <!-- Map client certificates to model access tiers -->
        <set-variable name="certSubject"
            value="@(context.Request.Certificate?.SubjectName?.Name ?? "")" />

        <choose>
            <!-- Premium tier: Full model access -->
            <when condition="@(((string)context.Variables["certSubject"]).Contains("OU=Premium"))">
                <!-- Allow all models -->
            </when>
            <!-- Standard tier: Limited models -->
            <when condition="@(((string)context.Variables["certSubject"]).Contains("OU=Standard"))">
                <set-variable name="requestedModel"
                    value="@(context.Request.Body.As<JObject>(preserveContent: true)?["model"]?.ToString())" />
                <choose>
                    <when condition="@(((string)context.Variables["requestedModel"]).Contains("gpt-4") &&
                        !((string)context.Variables["requestedModel"]).Contains("mini"))">
                        <return-response>
                            <set-status code="403" reason="Forbidden" />
                            <set-body>{"error":{"code":"model_not_authorized","message":"Standard tier does not have access to GPT-4o. Use gpt-4o-mini."}}</set-body>
                        </return-response>
                    </when>
                </choose>
            </when>
            <otherwise>
                <return-response>
                    <set-status code="403" reason="Forbidden" />
                    <set-body>{"error":{"code":"certificate_not_authorized","message":"Client certificate not recognized."}}</set-body>
                </return-response>
            </otherwise>
        </choose>
    </inbound>
</policies>

Sertifikathondtering med Azure Key Vault

resource keyVault 'Microsoft.KeyVault/vaults@2023-07-01' existing = {
  name: keyVaultName
}

resource apimCertificate 'Microsoft.ApiManagement/service/certificates@2023-09-01-preview' = {
  parent: apiManagement
  name: 'client-root-ca'
  properties: {
    keyVault: {
      secretIdentifier: '${keyVault.properties.vaultUri}secrets/client-root-ca'
      identityClientId: null // Use system-assigned identity
    }
  }
}

Revisjonssporing og audit trail

Krav til revisjonssporing

Krav Kilde APIM-losning
Sporbarhet Forvaltningsloven Request/response logging med korrelasjons-ID
Tilgangskontroll NSM Grunnprinsipper IP-filter, sertifikat, JWT-validering
Dataminimering GDPR Art. 5 PII-maskering for lagring
Loggoppbevaring Arkivloven Log Analytics retention (90-730 dager)
Endringssporing Intern revisjon APIM audit logs i Activity Log

Omfattende audit trail-policy

<policies>
    <inbound>
        <base />
        <!-- Capture audit context -->
        <set-variable name="auditContext" value="@{
            return new JObject {
                ["timestamp"] = DateTime.UtcNow.ToString("o"),
                ["requestId"] = context.RequestId.ToString(),
                ["subscriptionName"] = context.Subscription?.Name,
                ["subscriptionId"] = context.Subscription?.Id,
                ["clientIp"] = context.Request.IpAddress,
                ["userAgent"] = context.Request.Headers.GetValueOrDefault("User-Agent", "unknown"),
                ["apiName"] = context.Api.Name,
                ["apiVersion"] = context.Api.Version,
                ["operationId"] = context.Operation.Id,
                ["certificateSubject"] = context.Request.Certificate?.SubjectName?.Name ?? "none",
                ["tenantId"] = context.Request.Headers.GetValueOrDefault("x-tenant-id", "unknown")
            }.ToString();
        }" />
    </inbound>
    <outbound>
        <base />
        <!-- Log audit trail -->
        <trace source="audit-trail" severity="information">
            <message>@{
                var audit = JObject.Parse((string)context.Variables["auditContext"]);
                audit["statusCode"] = context.Response.StatusCode;
                audit["responseTime"] = (DateTime.UtcNow -
                    DateTime.Parse(audit["timestamp"].ToString())).TotalMilliseconds;

                // Add token usage if available
                var responseBody = context.Response.Body.As<JObject>(preserveContent: true);
                if (responseBody?["usage"] != null) {
                    audit["promptTokens"] = responseBody["usage"]["prompt_tokens"];
                    audit["completionTokens"] = responseBody["usage"]["completion_tokens"];
                    audit["totalTokens"] = responseBody["usage"]["total_tokens"];
                }

                return audit.ToString();
            }</message>
        </trace>
    </outbound>
</policies>

KQL: Sikkerhetsrevisjon

// Security audit: Failed authentication attempts
ApiManagementGatewayLogs
| where TimeGenerated > ago(24h)
| where ResponseCode in (401, 403)
| summarize
    FailedAttempts = count(),
    UniqueIPs = dcount(CallerIpAddress)
    by CallerIpAddress, ApiId, bin(TimeGenerated, 1h)
| where FailedAttempts > 10
| order by FailedAttempts desc
// Security audit: Unusual token consumption
ApiManagementGatewayLlmLog
| where TimeGenerated > ago(24h)
| summarize
    AvgTokens = avg(TotalTokens),
    MaxTokens = max(TotalTokens),
    Requests = count()
    by SubscriptionId
| where MaxTokens > 10000 or Requests > 1000
| order by MaxTokens desc

Sikkerhetssjekksliste for AI Gateway

Kontroll Prioritet Status
Microsoft Entra ID-autentisering P0
IP-filtrering (intern/VPN) P0
Rate limiting (requests og tokens) P0
Content Safety policy P0
Prompt injection-deteksjon P0
TLS 1.2+ patvunget P0
PII-deteksjon i prompts P1
Audit trail-logging P1
mTLS for hoysikkerhet P1
VNet-integrasjon P1
Subscription key + JWT P1
WAF (via Front Door) P2
DDoS Protection P2
Private Link P2
Geo-filtrering P2

Referanser

For Cosmo

  • Bruk denne referansen nar kunden trenger a herde sin AI gateway for produksjon, oppfylle compliance-krav, eller etablere et forsvar-i-dybden for AI-tjenester.
  • For norsk offentlig sektor er P0-kontrollene i sjekklisten obligatoriske. Start alltid med Microsoft Entra ID, IP-filtrering, rate limiting og Content Safety -- disse gir den storste sikkerhetseffekten med lavest implementeringskostnad.
  • PII-filtrering i APIM er en ekstra sikkerhetslinje, men bor ikke vaere eneste tiltak. Anbefal ogsa PII-filtrering i applikasjonslaget og i systemprompt-instruksjoner.
  • For organisasjoner som behandler sensitiv informasjon (helseopplysninger, personopplysninger), anbefal VNet-integrasjon i Internal mode + mTLS + Azure Private Link som minimumskrav.
  • Prompt injection-deteksjon i APIM-policies er et forstforsvar, men avanserte angrep krever Azure AI Content Safety med Prompt Shields. Anbefal bade policy-basert og AI-basert deteksjon i lag.