Python › Python for Pentesters
Sending data: parameters, headers, and bodies
A GET request that just fetches a page is the start. Real testing means sending things — query parameters, custom headers, POST bodies, JSON. The beautiful part of requests is that each piece of an HTTP request maps directly to a Python argument, and those arguments are dictionaries you already know how to build.
You'll learn to
- Send query parameters, headers, and bodies
- Use any HTTP method, including unusual ones
- See why this control is the whole game for web testing
Each request part is an argument
import requests
# Query parameters (?key=value) — pass a dict, requests builds the URL:
params = {"q": "test", "page": 2}
r = requests.get("https://example.com/search", params=params)
# → https://example.com/search?q=test&page=2
# Custom headers — a dict:
headers = {"User-Agent": "recon/1.0", "X-Forwarded-For": "127.0.0.1"}
r = requests.get("https://example.com", headers=headers)
# POST a form body:
r = requests.post("https://example.com/login", data={"user": "admin", "pass": "x"})
# POST a JSON body (sets Content-Type: application/json automatically):
r = requests.post("https://example.com/api", json={"name": "test"})
The mapping is clean and worth memorising: params= (a dict) becomes the query string; headers= (a dict) sets request headers; data= sends a form body; json= sends a JSON body and sets the right content type. The HTTP method is just the function name — get, post, put, delete.
Any method, including the unusual ones
requests.put("https://example.com/api/item/1", json={"name": "new"})
requests.delete("https://example.com/api/item/1")
requests.request("OPTIONS", "https://example.com") # any method, even custom
requests.request(method, url) lets you send any verb, including ones a browser never would.
Verb tampering: a concrete example
Many apps protect the obvious method but forget the others. The UI shows a read-only GET, but the endpoint quietly accepts DELETE:
# The app only links GET, but what happens with DELETE?
r = requests.delete("https://example.com/api/users/42", headers=auth_headers)
print(r.status_code) # 200 here would be a serious finding
Checkpoint
You want to add ?role=admin&debug=true to a GET request. What argument do you pass to requests.get, and in what form?
Pass a params dictionary with role set to admin and debug set to true. requests builds the query string for you, requests turns it into the query string role=admin and debug=true on the URL.
Try it yourself
Send a POST to https://httpbin.org/post with a JSON body of {"user": "test", "role": "admin"} using the json= argument. Print r.json() — httpbin echoes back what it received, so you can confirm your body and headers went out correctly.
Summary
Every part of an HTTP request maps to a requests argument: params= for the query string, headers= for headers, data= for form bodies, json= for JSON bodies (all dictionaries). The method is the function name, or requests.request(method, url) for any verb. This direct, total control — any header, any body, any method, in a loop — is exactly what makes Python a web-testing tool rather than just a page fetcher.
Key takeaways
params=,headers=,data=,json=are all dictionaries you build.data=is form-encoded;json=is a JSON body — don’t mix them up.- Any HTTP method is available; verb tampering finds missing access controls.
- Total request control across a loop is the core of automated testing.
Quick quiz
Next, reading what comes back: parsing JSON responses, which is most of API testing.