'Cookie manipulation in Mitmproxy requests and responses

I have problem adding new cookies that are only visible to the client in reverse proxy mode of mitmproxy. There seems to be little documentation or examples that cover cookie manipulation in it.

I want to:

  1. add cookie to the response that is sent to the client
  2. intercept the same cookie when the client sends it back, to identify the user, and remove it before it gets sent to the destination server

How can this be accomplished?



Solution 1:[1]

NOTE: I believe for 1. you meant add a cookie to the "request".

You can write a script that implements the request and response functions and modifies those objects. Then you can run mitmproxy with the -s flag and pass it the path to the script file and that should get you going. See below the main structure of the script (and further below the helper functions).

# USAGE: mitmproxy -s path/to/http_manipulate_cookies.py
from mitmproxy import http


PATH_TO_COOKIES = "./cookies.json"  # insert your path to the cookie file here
FILTER_COOKIES = {"userdata", "_ga"}  # update this to the specific cookie names you want to remove
# NOTE: we use a set for lookup efficiency

# -- Main interception functionality --
def request(flow: http.HTTPFlow) -> None:
    """Add a specific set of cookies to every request."""
    # obtain any cookies from the request
    _req_cookies_str = flow.request.headers.get("cookie", "")
    req_cookies = parse_cookies(_req_cookies_str)

    # add our cookies to the original cookies from the request
    all_cookies = req_cookies + load_json_cookies()
    # NOTE: by adding it to the end we should overwrite any existing cookies
    # of the same name but if you want to be more careful you can iterate over 
    # the req_cookies and remove the ones you want to overwrite first.

    # modify the request with the combined cookies
    flow.request.headers["cookie"] = stringify_cookies(all_cookies)


def response(flow: http.HTTPFlow) -> None:
    """Remove a specific cookie from every response."""
    set_cookies_str = flow.response.headers.get("set-cookie", "")
    # NOTE: use safe attribute access (.get), in some cases there might not be a set-cookie header
    
    if set_cookies_str:
        resp_cookies = parse_cookies(set_cookies_str)

        # remove the cookie we want to remove
        resp_cookies = [c for c in resp_cookies if c["name"] not in FILTER_COOKIES]

        # modify the request with the combined cookies
        flow.response.headers["set-cookie"] = stringify_cookies(resp_cookies)

An example of the cookie.json doc would be the following:

[
    {
        "name": "mycookie",
        "value": "somevalue"
    },
    {
        "name": "anothercookie",
        "value": "foobarfoobazfoofoobuzz"
    }
]

Here are the helper functions I used:

from typing import List, Dict
import json


# -- Helper functions --
def load_json_cookies():
    """
    Load a particular json file containing a list of cookies.
    """
    with open(PATH_TO_COOKIES, "r") as f:
        return json.load(f)
# NOTE: or just hardcode the cookies as [{"name": "", "value": ""}]


def stringify_cookies(cookies: List[Dict]) -> str:
    """
    Creates a cookie string from a list of cookie dicts.
    """
    return ";".join([f"{c['name']}={c['value']}" for c in cookies])


def parse_cookies(cookie_string: str) -> List[Dict[str, str]]:
    """
    Parses a cookie string into a list of cookie dicts.
    """
    cookies = []
    for c in cookie_string.split(";"):
        c = c.strip()
        if c:            
            k, v = c.split("=", 1)
            cookies.append({"name": k, "value": v})
    return cookies

I submitted this example to mitmproxy as a PR here: https://github.com/mitmproxy/mitmproxy/pull/5278

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 WillMonge