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.

Before you begin

  1. Complete the demo setup to install Gloo Mesh, Istio, and Bookinfo in your cluster.

  2. Create the Gloo Mesh resources for this policy in the management and workload clusters.

    The following files are examples only for testing purposes. Your actual setup might vary. You can use the files as a reference for creating your own tests.

    1. Download the following Gloo Mesh resources:
    2. Apply the files to your management cluster.
      kubectl apply -f kubernetes-cluster_gloo-mesh_cluster-1.yaml --context ${MGMT_CONTEXT}
      kubectl apply -f kubernetes-cluster_gloo-mesh_cluster-2.yaml --context ${MGMT_CONTEXT}
      kubectl apply -f workspace_gloo-mesh_anything.yaml --context ${MGMT_CONTEXT}
      
    1. Download the following Gloo Mesh resources:
    2. Apply the files to your workload cluster.
      kubectl apply -f csrf-policy_bookinfo_csrf-policy.yaml --context ${REMOTE_CONTEXT1}
      kubectl apply -f route-table_bookinfo_www-example-com.yaml --context ${REMOTE_CONTEXT1}
      kubectl apply -f virtual-gateway_bookinfo_north-south-gw.yaml --context ${REMOTE_CONTEXT1}
      kubectl apply -f workspace-settings_bookinfo_anything.yaml --context ${REMOTE_CONTEXT1}
      

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, such as the one that you downloaded in Before you begin. 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:
  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 Configure which routes to apply the policy to, by using labels. The label matches the app and the route from the route table. If omitted, 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.
    kubectl edit deploy istio-ingressgateway -n istio-system
    
  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 --context ${REMOTE_CONTEXT1} -f csrf-policy_bookinfo_csrf-policy.yaml
    
  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 -i -X POST -H "Host: eu.bookinfo.com" \
      -d "{\"id\":100,\"ratings\":{\"Reviewer1\":5,\"Reviewer2\":4}}" \
      ${INGRESS_GW_IP}/ratings/1
    

    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 -i -X POST -H "Host: eu.bookinfo.com" \
      -H "Origin: http://istio.io" \
      -d "{\"id\":100,\"ratings\":{\"Reviewer1\":5,\"Reviewer2\":4}}" \
      ${INGRESS_GW_IP}/ratings/1
    

    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 -i -X POST -H "Host: eu.bookinfo.com" \
      -H "Origin: http://allowThisOne.solo.io" \
      -d "{\"id\":100,\"ratings\":{\"Reviewer1\":5,\"Reviewer2\":4}}" \
      ${INGRESS_GW_IP}/ratings/1
    

    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.

    kubectl port-forward -n istio-system <istio-ingressgateway-pod> 15000
    
  6. Open your browser to the local host to review CSRF metrics. In the output, search for csrf.

    http://localhost:15000/stats
    

    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