CSRF

Apply a CSRF filter to the gateway to help prevent cross-site request forgery attacks.

To help prevent CSRF attacks, you can use this policy to create a CSRF filter. For each route that you apply the policy to, the filter checks to make sure that a request's origin matches its destination. If the origin and destination do not match, a 403 Forbidden error code is returned. Note that because CSRF attacks specifically target state-changing requests, the filter only acts on HTTP requests that have a state-changing method such as POST or PUT.

For more information, see the following resources.

If you import or export resources across workspaces, your policies might not apply. For more information, see Import and export policies.

Before you begin

This guide assumes that you use the same names for components like clusters, workspaces, and namespaces as in the getting started, and that your Kubernetes context is set to the cluster you store your Gloo config in (typically the management cluster). If you have different names, make sure to update the sample configuration files in this guide.

Follow the getting started instructions to:

  1. Set up Gloo Gateway in a single cluster.
  2. Deploy sample apps.
  3. Configure an HTTP listener on your gateway and set up basic routing for the sample apps.

Configure CSRF policies

You can apply a CSRF policy at the route level. For more information, see Applying policies.

Review the following sample configuration file. Continue to the Verify CSRF policies section for example steps on how to check that the CSRF filter is working.

apiVersion: security.policy.gloo.solo.io/v2
kind: CSRFPolicy
metadata:
  annotations:
    cluster.solo.io/cluster: ""
  name: csrf-policy
  namespace: bookinfo
spec:
  applyToRoutes:
  - route:
      labels:
        route: ratings
  config:
    additionalOrigins:
    - regex: allowThisOne.solo.io
    filterEnabled: true
Review the following table to understand this configuration.
Setting Description
spec.applyToRoutes Use labels to configure which routes to apply the policy to. This example label matches the app and route from the example route table that you apply separately. If omitted and you do not have another selector such as applyToDestinations, the policy applies to all routes in the workspace.
spec.config.additionalOrigins Set any additional origins to allow besides the destination origin. For allowed string matching, see the API reference.
spec.config.filterEnabled Set to true so that the CSRF policy is enforced. Replace with shadowEnabled: true to track metrics but not enforce blocking.

Enable CSRF metrics

  1. To enable CSRF metrics, edit the istio-ingressgateway deployment. If your gateway is in a different namespace, update the command accordingly.
    kubectl edit deploy $(kubectl get deployment -l "app=istio-ingressgateway" -n gloo-mesh-gateways -o jsonpath='{.items[].metadata.name}') -n gloo-mesh-gateways
    
  2. Add the following annotation to the istio-ingressgateway.
    spec:
      template:
        metadata:
          annotations:
            proxy.istio.io/config: |
              proxyStatsMatcher:
                inclusionRegexps:
                - .*csrf.*
    

Now, when you port-forward the istio-ingressgateway pod on the Envoy admin port 15000, you can review CSRF metrics. These metrics track the number of valid requests, the number of total invalid requests, and the number of invalid requests due to a missing origin.

Example use case: Shadow-enabled metrics. You might not want to block all requests with mismatching origin headers, but still track such requests. To do so, you can set up your CSRF policy with shadowEnabled: true instead of filterEnabled: true. With shadow enabled, requests are not blocked. However, the metrics still show when requests do not have the allowed origin header.

Verify CSRF policies

  1. Update and apply the example CSRF policy in the cluster with the Bookinfo workspace in your example setup.

    kubectl apply -f - << EOF
    apiVersion: security.policy.gloo.solo.io/v2
    kind: CSRFPolicy
    metadata:
      annotations:
        cluster.solo.io/cluster: ""
      name: csrf-policy
      namespace: bookinfo
    spec:
      applyToRoutes:
      - route:
          labels:
            route: ratings
      config:
        additionalOrigins:
        - regex: allowThisOne.solo.io
        filterEnabled: true
    EOF
    
  2. Send a request to the ratings app through the ingress gateway that the route table is attached to. Do not include an origin header in the request. Note that because CSRF attacks specifically target state-changing requests, the filter only acts on HTTP requests that have a state-changing method such as POST or PUT.

    curl -vik -X POST --resolve www.example.com:80:${INGRESS_GW_IP} http://www.example.com:80/ratings/1 -d "{\"id\":100,\"ratings\":{\"Reviewer1\":5,\"Reviewer2\":4}}"
    
    curl -vik -X POST --resolve www.example.com:443:${INGRESS_GW_IP} https://www.example.com:443/ratings/1 -d "{\"id\":100,\"ratings\":{\"Reviewer1\":5,\"Reviewer2\":4}}"
    

    Note that the response is a 403 Forbidden error code because no origin is set.

    HTTP/1.1 403 Forbidden
    
  3. Send the request again. This time, set the origin to a different domain than you used in the policy, such as http://istio.io.

    curl -vik -X POST --resolve www.example.com:80:${INGRESS_GW_IP} http://www.example.com:80/ratings/1 -d "{\"id\":100,\"ratings\":{\"Reviewer1\":5,\"Reviewer2\":4}}" -H "Origin: http://istio.io"
    
    curl -vik -X POST --resolve www.example.com:443:${INGRESS_GW_IP} https://www.example.com:443/ratings/1 -d "{\"id\":100,\"ratings\":{\"Reviewer1\":5,\"Reviewer2\":4}}" -H "Origin: http://istio.io"
    

    Note that the response is still a 403 Forbidden error code because the origins do not match.

    HTTP/1.1 403 Forbidden
    
  4. Send the request again. This time, set the origin to match the allowed origin in your CSRF policy.

    curl -vik -X POST --resolve www.example.com:80:${INGRESS_GW_IP} http://www.example.com:80/ratings/1 -d "{\"id\":100,\"ratings\":{\"Reviewer1\":5,\"Reviewer2\":4}}" -H "Origin: http://allowThisOne.solo.io"
    
    curl -vik -X POST --resolve www.example.com:443:${INGRESS_GW_IP} https://www.example.com:443/ratings/1 -d "{\"id\":100,\"ratings\":{\"Reviewer1\":5,\"Reviewer2\":4}}" -H "Origin: http://allowThisOne.solo.io"
    

    Note that the response is successful.

    HTTP/1.1 200 OK
    
  5. If you enabled CSRF metrics, port-forward the istio-ingressgateway pod on the Envoy admin port 15000. If your gateway is in a different namespace, update the command accordingly.

    kubectl port-forward -n gloo-mesh-gateways $(kubectl get pods -l "app=istio-ingressgateway" -n gloo-mesh-gateways -o jsonpath='{.items[].metadata.name}') 15000
    
  6. Open your browser to the localhost stats to review CSRF metrics. In the output, search for csrf.

    • Example metrics:
    http.outbound_0.0.0.0_8443.csrf.request_valid: 1
    http.outbound_0.0.0.0_8443.csrf.request_invalid: 1
    http.outbound_0.0.0.0_8443.csrf.missing_source_origin: 1
    
  7. Optional: Clean up the resources that you created.

    kubectl -n bookinfo delete csrfpolicy csrf-policy