<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Matteo Vitali</title>
    <description>The latest articles on Forem by Matteo Vitali (@trottomv).</description>
    <link>https://forem.com/trottomv</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F2734053%2F003bdc72-a559-4656-a88d-6acd76a42996.jpg</url>
      <title>Forem: Matteo Vitali</title>
      <link>https://forem.com/trottomv</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/trottomv"/>
    <language>en</language>
    <item>
      <title>AI agent observability with OpenTelemetry and Grafana LGTM</title>
      <dc:creator>Matteo Vitali</dc:creator>
      <pubDate>Sun, 13 Jul 2025 06:52:39 +0000</pubDate>
      <link>https://forem.com/trottomv/ai-agent-observability-with-opentelemetry-and-grafana-lgtm-4m57</link>
      <guid>https://forem.com/trottomv/ai-agent-observability-with-opentelemetry-and-grafana-lgtm-4m57</guid>
      <description>&lt;p&gt;AI is powerful. But why does observing it matter?&lt;/p&gt;

&lt;p&gt;AI agents are becoming more and more common in production. However, they often behave like black boxes: we send a prompt, we get a response… but what happens in between?&lt;/p&gt;

&lt;p&gt;In this post, I’ll show why instrumenting AI agents matters more than ever, using open source tools like OpenTelemetry and the Grafana LGTM stack — that is, Loki, Grafana, Tempo, and Mimir (and yes, also “Looks Good To Me”! 😉).&lt;/p&gt;

&lt;h1&gt;
  
  
  The challenge
&lt;/h1&gt;

&lt;p&gt;&lt;strong&gt;LLMs and AI agents are 'black boxes' systems:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Complex, &lt;strong&gt;non-deterministic&lt;/strong&gt; behavior&lt;/li&gt;
&lt;li&gt;Internal &lt;strong&gt;'reasoning' is invisible at runtime&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hard to explain&lt;/strong&gt;, trust, or debug &lt;strong&gt;outputs&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;🛡️ “You can’t govern or secure what you can’t observe.” (semicit.)&lt;/p&gt;

&lt;h1&gt;
  
  
  An open standards-based approach
&lt;/h1&gt;

&lt;p&gt;&lt;strong&gt;The OWASP Agent Observability Standard (AOS)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://aos.owasp.org" rel="noopener noreferrer"&gt;OWASP AOS&lt;/a&gt; wants to bring standardized observability to AI systems:&lt;br&gt;
Mission: Transform AI agents from black boxes into trustworthy systems through standardized observability.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Instrumentation&lt;/strong&gt; – via OpenTelemetry&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Traceability&lt;/strong&gt; – end-to-end execution flow&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Inspectability&lt;/strong&gt; – insights on inner workings&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  OpenTelemetry
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://opentelemetry.io/" rel="noopener noreferrer"&gt;OpenTelemetry&lt;/a&gt; is a CNCF project that provides a standardized framework for telemetry:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Logs&lt;/strong&gt; (timestamped events)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Metrics&lt;/strong&gt; (numerical KPIs)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Traces&lt;/strong&gt; (end-to-end execution paths)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It supports manual and auto instrumentation, and works with many backends — including the Grafana LGTM stack.&lt;/p&gt;
&lt;h3&gt;
  
  
  💻 Manual instrumentation (code based)
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from openai import OpenAI
from opentelemetry.instrumentation.openai_v2 import OpenAIInstrumentor

OpenAIInstrumentor().instrument()

client = OpenAI()
response = client.chat.completions.create(
    model="gpt-4o-mini",
    messages=[
      {
        "role": "user", 
        "content": "Write a short poem on OpenTelemetry."
      }
    ],
)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  ⚡ Auto instrumentation (zero code)
&lt;/h3&gt;

&lt;p&gt;Alternatively, you can automatically instrument a Python app with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;FROM python:3.13-alpine

ENV OTEL_EXPORTER_OTLP_ENDPOINT=otel-collector:4317

RUN pip install --no-cache-dir \
  fastapi \
  opentelemetry-distro \
  opentelemetry-instrumentation

COPY main.py .

CMD ["opentelemetry-instrument", "uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  📦 The OpenTelemetry Collector
&lt;/h3&gt;

&lt;p&gt;A key component for managing telemetry data&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;receivers:
  otlp:
    protocols:
      grpc:
        endpoint: 0.0.0.0:4317
      http:
        endpoint: 0.0.0.0:4318
  hostmetrics:  # otel-collector-contrib
    root_path: "/hostfs"
    collection_interval: 10s

processors:
  batch:
  resourcedetection:
    detectors: ["env", "system"]

exporters:
  otlp/traces:
    endpoint: tempo:4317
  otlphttp/metrics:
    endpoint: http://mimir:9009/otlp
  otlphttp/logs:
    endpoint: http://loki:3100/otlp

service:
  pipelines:
    traces:
      receivers: [otlp]
      processors: [batch, resourcedetection]
      exporters: [otlp/traces]
    metrics:
      receivers: [otlp, hostmetrics]
      processors: [batch, resourcedetection]
      exporters: [otlphttp/metrics]
    logs:
      receivers: [otlp]
      processors: [batch, resourcedetection]
      exporters: [otlphttp/logs]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Observability with OpenTelemetry and Grafana LGTM
&lt;/h2&gt;

&lt;p&gt;Once data flows through the collector, the &lt;a href="https://grafana.com/" rel="noopener noreferrer"&gt;Grafana&lt;/a&gt; LGTM stack lets you explore:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Logs in Loki&lt;/li&gt;
&lt;li&gt;Metrics in Mimir&lt;/li&gt;
&lt;li&gt;Traces in Tempo&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6djsagnglbv11zadn5ne.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6djsagnglbv11zadn5ne.png" alt=" " width="748" height="782"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  PydanticAI agent instrumentation
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://ai.pydantic.dev/" rel="noopener noreferrer"&gt;PydanticAI&lt;/a&gt; is a Pythonic AI agent framework with native OpenTelemetry support&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from pydantic_ai import Agent

agent = Agent(
    'google-gla:gemini-2.0-flash',
    system_prompt='Be concise, reply with one sentence.',
    instrument=True,
)

result = agent.run_sync('Where does "hello world" come from?')
print(result.output)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  📈 Inspect telemetry data
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdtzgifnqre5a9ilrowzq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdtzgifnqre5a9ilrowzq.png" alt=" " width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbiysnx2uiiqmuy9iivxb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbiysnx2uiiqmuy9iivxb.png" alt=" " width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  🤔 Final thoughts &amp;amp; takeaways
&lt;/h1&gt;

&lt;p&gt;Even the simplest “hello world” AI agent can be observed via metrics and traces, revealing how complex and resource-hungry LLM calls are,&lt;br&gt;
benefit from structured observability for debugging and trust building.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;No business, No logic&lt;/strong&gt;
The AI agent is kept intentionally simple — a basic “hello world” — because the primary goal is to validate the power of observability in the context of agentic AI, not to showcase advanced intelligence.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Analytical and empirical evaluation&lt;/strong&gt;
With OpenTelemetry and Grafana, it's possible to analyze the agent both quantitatively (through metrics and traces) and qualitatively (by observing its flow), demonstrating the real value of observability in monitoring and understanding AI systems.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Conscious use of AI – Sometimes “a cannon is too much for a fly”&lt;/strong&gt;
Even a minimal AI agent benefits greatly from structured observability — it’s essential for understanding, debugging, and building trust. At the same time, it reveals a key insight: generating trivial output can come with a high computational cost, especially when relying on LLMs.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>aiops</category>
      <category>observability</category>
      <category>opentelemetry</category>
      <category>grafana</category>
    </item>
    <item>
      <title>Secure by design in Python: A FastAPI app with 5 DevSecOps tools and a real time SSTI vulnerability remediation</title>
      <dc:creator>Matteo Vitali</dc:creator>
      <pubDate>Sat, 10 May 2025 07:46:16 +0000</pubDate>
      <link>https://forem.com/trottomv/secure-by-design-in-python-a-fastapi-app-with-5-devsecops-tools-and-a-real-time-ssti-vulnerability-2e6n</link>
      <guid>https://forem.com/trottomv/secure-by-design-in-python-a-fastapi-app-with-5-devsecops-tools-and-a-real-time-ssti-vulnerability-2e6n</guid>
      <description>&lt;h2&gt;
  
  
  🌟 Introduction
&lt;/h2&gt;

&lt;p&gt;Security should not be an afterthought in software development. Instead, it should be a core principle baked into your design, code, and CI/CD workflows. &lt;br&gt;
To demonstrate this approach, I've created an intentionally insecure FastAPI app as a playground. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/trottomv/python-insecure-app" rel="noopener noreferrer"&gt;https://github.com/trottomv/python-insecure-app&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this post, we'll walk through:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;5 open-source security tools (SCA, SAST, DAST, container scanning, API fuzzing)&lt;/li&gt;
&lt;li&gt;How they help uncover vulnerabilities&lt;/li&gt;
&lt;li&gt;How to remediate a &lt;strong&gt;real Server-Side Template Injection (SSTI)&lt;/strong&gt; vulnerability in FastAPI&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's dive in 👇&lt;/p&gt;


&lt;h2&gt;
  
  
  1️pip-audit: Python dependency vulnerability scanning
&lt;/h2&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip-audit
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;This tool checks for known vulnerabilities in your Python dependencies using safety-db or PyPI advisories. It's simple, fast, and effective.&lt;/p&gt;

&lt;p&gt;Sample output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;jinja2 3.0.0 CVE-2022-XXXXX Template injection possible
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Remediation:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Upgrade the affected package if a patched version is available&lt;/li&gt;
&lt;li&gt;Pin versions in &lt;code&gt;requirements.txt&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  2️Bandit: static code analysis (SAST) for Python
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;bandit &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Bandit performs static analysis on Python code to find common security issues like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use of dangerous functions (&lt;code&gt;eval&lt;/code&gt;, &lt;code&gt;exec&lt;/code&gt;, etc.)&lt;/li&gt;
&lt;li&gt;Hardcoded passwords or secrets&lt;/li&gt;
&lt;li&gt;Insecure file handling&lt;/li&gt;
&lt;li&gt;External calls with &lt;code&gt;requests&lt;/code&gt; without a timeout — which may lead to denial of service if the remote server hangs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;But:&lt;/strong&gt; Bandit currently &lt;strong&gt;does not detect Server-Side Template Injection (SSTI)&lt;/strong&gt;, even when using jinja2 templates unsafely.&lt;/p&gt;

&lt;p&gt;Reference: &lt;a href="https://github.com/PyCQA/bandit/issues/517" rel="noopener noreferrer"&gt;Issue #728&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This shows a limitation of SAST — it's useful, but &lt;strong&gt;not sufficient alone&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  3️Schemathesis: API fuzzing from OpenAPI schemas
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;schemathesis run http://localhost:8000/openapi.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Schemathesis generates test cases based on your OpenAPI schema and runs fuzzing-like checks on all endpoints.&lt;/p&gt;

&lt;p&gt;Benefits:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Detects unexpected 500s and unhandled edge cases&lt;/li&gt;
&lt;li&gt;Validates assumptions in your parameter handling&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In our demo, this helped uncover inconsistent behavior in the root &lt;code&gt;/&lt;/code&gt; endpoint, especially with template injection payloads.&lt;/p&gt;




&lt;h2&gt;
  
  
  4️Trivy: container image vulnerability scanner
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;trivy image trottomv/python-insecure-app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Trivy scans for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;OS package vulnerabilities (APT, APK, etc.)&lt;/li&gt;
&lt;li&gt;Python dependency CVEs&lt;/li&gt;
&lt;li&gt;Misconfigurations&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Hardening tips:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use &lt;code&gt;python:3.12-slim&lt;/code&gt; or smaller base images&lt;/li&gt;
&lt;li&gt;Avoid running as root (&lt;code&gt;USER app&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Consider introduce distroless approach&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Bonus: Trivy also outputs SBOMs for compliance.&lt;/p&gt;




&lt;h2&gt;
  
  
  5️OWASP ZAP: dynamic app security testing (DAST)
&lt;/h2&gt;

&lt;p&gt;ZAP acts as a proxy or scanner against your running app (e.g. FastAPI on &lt;code&gt;localhost:8000&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;In our case, &lt;strong&gt;ZAP successfully discovered a Server-Side Template Injection (SSTI)&lt;/strong&gt; vulnerability:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;GET /?name={{7*6}} → "Hello 42!"
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This issue was &lt;strong&gt;not detected by Bandit&lt;/strong&gt;, highlighting the importance of dynamic analysis.&lt;/p&gt;




&lt;h2&gt;
  
  
  🛠️ Remediation: fixing the SSTI in FastAPI
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Vulnerable code:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# main.py
&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;fastapi&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;FastAPI&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;fastapi.responses&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;HTMLResponse&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;jinja2&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Template&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;FastAPI&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="nd"&gt;@app.get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;response_class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;HTMLResponse&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;try_hack_me&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;John Ripper&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;content&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;h1&amp;gt;Hello, &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;!&amp;lt;/h1&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;Template&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Why it's dangerous:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Template(content).render()&lt;/code&gt; evaluates the string using Jinja2, which allows remote code execution if input is unsanitized&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Secure fix:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# main.py
&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;fastapi&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;FastAPI&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;fastapi.responses&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;HTMLResponse&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;jinja2&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Template&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;FastAPI&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="nd"&gt;@app.get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;response_class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;HTMLResponse&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;try_hack_me&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;John Ripper&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;content&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;h1&amp;gt;Hello, {{name}}!&amp;lt;/h1&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;Template&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Test it:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# tests.py
&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;fastapi.testclient&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;TestClient&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt;

&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;TestClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_root&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/?name={{7*6}}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status_code&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;
    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;42&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  ✅ Conclusion
&lt;/h2&gt;

&lt;p&gt;Security is not a tool — it's a mindset and a workflow.&lt;/p&gt;

&lt;p&gt;Combining:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;pip-audit&lt;/code&gt; for software composition analysis&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;bandit&lt;/code&gt; for static code issues&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;schemathesis&lt;/code&gt; for fuzzing testing on APIs&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;trivy&lt;/code&gt; for vulnerability scanning and SBOMs generation on artifacts&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;zap&lt;/code&gt; for dynamic runtime scanning&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;...provides &lt;strong&gt;layered defense&lt;/strong&gt;. Fixing real issues like SSTI shows how practical this approach is.&lt;/p&gt;

&lt;p&gt;If you're curious:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🌐 Try the &lt;a href="https://github.com/trottomv/python-insecure-app" rel="noopener noreferrer"&gt;demo app&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;✏️ Suggest improvements or contribute new findings&lt;/li&gt;
&lt;li&gt;🚀 Integrate these tools in your next CI/CD pipeline&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Stay secure ✨&lt;/p&gt;




</description>
      <category>python</category>
      <category>devsecops</category>
      <category>security</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Boost your Django projects's security with proper `Cache-Control` on views</title>
      <dc:creator>Matteo Vitali</dc:creator>
      <pubDate>Sun, 19 Jan 2025 08:46:39 +0000</pubDate>
      <link>https://forem.com/trottomv/boost-your-django-projectss-security-with-proper-cache-control-on-views-546k</link>
      <guid>https://forem.com/trottomv/boost-your-django-projectss-security-with-proper-cache-control-on-views-546k</guid>
      <description>&lt;p&gt;Caching is great for performance, but sensitive data must never be cached! In Django, properly configuring Cache Control on your views ensures secure handling of sensitive content, such as login pages or user-specific data.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;🔒 Why is Cache Control important?&lt;/strong&gt;&lt;br&gt;
Without proper cache settings, sensitive data may be stored in the user's browser or intermediary proxies, exposing it to potential risks.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;💡 How to configure Cache Control in Django?&lt;/strong&gt;&lt;br&gt;
You can use the &lt;code&gt;@never_cache&lt;/code&gt; decorator on function-based views to prevent them from being cached (as mentioned in the &lt;a href="https://docs.djangoproject.com/en/5.1/topics/http/decorators/#django.views.decorators.cache.never_cache" rel="noopener noreferrer"&gt;official documentation&lt;/a&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;django.views.decorators.cache&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;never_cache&lt;/span&gt;  

&lt;span class="nd"&gt;@never_cache&lt;/span&gt;  
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;my_secure_view&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;  
    &lt;span class="c1"&gt;# Your secure logic here  
&lt;/span&gt;    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;HttpResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;This page is protected from caching!&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But what if you want to reuse this logic across multiple class-based views? &lt;strong&gt;You can create a reusable mixin&lt;/strong&gt; for this purpose!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# myproject/views.py
&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;django.contrib.auth.mixins&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;LoginRequiredMixin&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;django.utils.decorators&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;method_decorator&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;django.views.decorators.cache&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;never_cache&lt;/span&gt;

&lt;span class="nd"&gt;@method_decorator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;never_cache&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;dispatch&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PrivateAreaMixin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;LoginRequiredMixin&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Override LoginRequiredMixin by adding Cache-Control rules.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With this mixin, securing your class-based views becomes much simpler:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# myapp/views.py
&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;django.views.generic&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;TemplateView&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;myproject.views&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;PrivateAreaMixin&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;IndexView&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;PrivateAreaMixin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;TemplateView&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;The index view.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;

    &lt;span class="n"&gt;template_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;index.html&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;🛠 Testing the Implementation&lt;/strong&gt;&lt;br&gt;
Testing is crucial to ensure everything works as expected. Here's how you can test the &lt;code&gt;PrivateAreaMixin&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# myproject/tests/test_views.py
&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;django.test&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;TestCase&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;RequestFactory&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;django.contrib.auth.models&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;AnonymousUser&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;django.contrib.auth&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;get_user_model&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;django.http&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;HttpResponse&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;django.views&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;View&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;myproject.views&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;PrivateAreaMixin&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PrivateAreaMixinTest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TestCase&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Test private area Cache-Control override.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;

    &lt;span class="n"&gt;factory&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;RequestFactory&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="nd"&gt;@classmethod&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;setUpTestData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cls&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Initialize test data.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
        &lt;span class="n"&gt;cls&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_user_model&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create_user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;testuser&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
            &lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user@test.xyz&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
            &lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;5tr0ngP4ssW0rd&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_login_required_with_cache_control&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Test login required and Cache-Control dispatched.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;

        &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AView&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;PrivateAreaMixin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;View&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;HttpResponse&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

        &lt;span class="n"&gt;view&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;AView&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;as_view&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

        &lt;span class="c1"&gt;# Test redirect for anonymous users
&lt;/span&gt;        &lt;span class="n"&gt;request&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;factory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;AnonymousUser&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;view&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;assertEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status_code&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;302&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;assertEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/accounts/login/?next=/&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="c1"&gt;# Test authenticated user and cache control headers
&lt;/span&gt;        &lt;span class="n"&gt;request&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;factory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;
        &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;view&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;assertEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status_code&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;assertIn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Cache-Control&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;assertEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Cache-Control&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;max-age=0, no-cache, no-store, must-revalidate, private&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;🔥 Best Practice Tip:&lt;/strong&gt;&lt;br&gt;
Using &lt;code&gt;@never_cache&lt;/code&gt; with a mixin like &lt;code&gt;PrivateAreaMixin&lt;/code&gt; makes your code reusable and clean. Combined with rigorous testing, this ensures your sensitive views remain secure and aligned with best practices.&lt;/p&gt;

&lt;p&gt;How do you handle caching and sensitive data in your Django projects?&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Update:&lt;/em&gt;&lt;br&gt;
I’m excited to share that I’ve developed a new package, &lt;a href="https://pypi.org/project/django-never-cache/" rel="noopener noreferrer"&gt;django-never-cache&lt;/a&gt;, inspired by the concepts expressed in this post.&lt;/p&gt;

&lt;p&gt;This lightweight package simplifies applying Cache-Control rules to sensitive views by providing reusable utilities like mixins, middlewares and decorators. It is designed to make securing Django applications easier.&lt;/p&gt;

&lt;p&gt;Check it out on GitHub: &lt;a href="https://github.com/trottomv/django-never-cache" rel="noopener noreferrer"&gt;https://github.com/trottomv/django-never-cache&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Feel free to try it out, share feedback, or even contribute to its development. 🚀&lt;/p&gt;

</description>
      <category>django</category>
      <category>cache</category>
      <category>webdev</category>
      <category>python</category>
    </item>
  </channel>
</rss>
