Two critical PHP flaws were patched in October 2026, and attacks started before the patch shipped. Here is what they teach about choosing a runtime, and why modern .NET is our choice for applications that handle money and personal data.
On 21 October 2026 the PHP project released emergency fixes for two vulnerabilities in the PHP interpreter itself, now widely called php2shell:
Both are rated CVSS v4.0 9.2. In the worst case, either one lets an unauthenticated attacker run code on the server with the web worker's privileges. The fixed releases are PHP 8.5.12, 8.4.27, 8.3.36 and 8.2.35. End-of-life branches get no official release, though the PHP security team published minimal source backports for PHP 7.4.
Attacks started before the patch. Details leaked on 20 October, a day before the scheduled release. Amazon Threat Intelligence reported exploitation attempts from 17 October, and watchTowr has seen exploitation since the 21st. Several public proof-of-concept exploits exist. Rapid7 notes that none is broadly reliable yet, because each targets a specific OS, PHP version and web server combination.
If you run PHP, stop reading and patch. Updating WordPress, Laravel or any other application does not fix this, because the flaw is in the runtime underneath them. Upgrade PHP itself, or confirm that your host has.
The PHP security team shipped fixes across four supported branches under the pressure of a leak, and backported to an end-of-life branch as well. That is a good response. Every serious platform ships critical CVEs, .NET included: in October 2025, ASP.NET Core's Kestrel server was patched for an HTTP request-smuggling flaw rated CVSS 9.9 (CVE-2025-55315).
So the useful question isn't which platform has zero vulnerabilities. None does. The useful questions are:
On all three, modern .NET is a strong choice for long-lived business applications.
Both php2shell flaws are memory-safety bugs: corruption and use-after-free in native C code. These are the most dangerous kinds of bug, because they let an attacker turn a crash into code execution.
C# and the .NET runtime are managed by default. The garbage collector owns object lifetimes, array and string accesses are bounds-checked, and the type system stops you treating one kind of object as another. Ordinary application code cannot free an object and then keep using it. Raw pointers exist only inside explicitly marked unsafe blocks, which are easy to find and review.
The runtime still contains native code, and it can still have bugs. But the share of your stack where a single mistake becomes remote code execution is much smaller.
.NET follows a published annual schedule. A new major version ships every November. Even-numbered releases are Long-Term Support (LTS) with three years of patches; odd-numbered releases are Standard-Term Support (STS), now with two years.
| Version | Type | Released | Supported until |
|---|---|---|---|
| .NET 8 | LTS | Nov 2023 | 10 Nov 2026 |
| .NET 9 | STS | Nov 2024 | 10 Nov 2026 |
| .NET 10 | LTS | Nov 2025 | Nov 2028 |
Security fixes ship on Patch Tuesday, the second Tuesday of every month, through the Microsoft Security Response Center. You can plan maintenance windows a year ahead and know when an upgrade is due.
If you're on .NET 8 or 9 today: both go out of support on 10 November 2026, a few weeks from now. Plan the move to .NET 10 LTS. Moving between modern .NET versions is usually a target-framework change plus a short list of documented breaking changes.
dotnet restore checks NuGet packages against the GitHub Advisory Database and warns on known vulnerabilities, including transitive ones. In .NET 10 it audits all packages by default.
dotnet list package --vulnerable --include-transitive
ASP.NET Core with the Kestrel server consistently ranks near the top of independent web framework benchmarks. More requests per server means lower hosting costs.
Windows, Linux and macOS; x64 and ARM64; containers and Kubernetes. Official slim and "chiseled" container images keep the attack surface small.
Compile ahead of time to a single native binary with fast startup and a small memory footprint. A good fit for serverless functions and sidecars.
HTTPS redirection, HSTS, anti-forgery tokens, rate-limiting middleware, the Data Protection API and ASP.NET Core Identity ship in the box rather than as third-party add-ons.
C# catches whole categories of mistakes at compile time, including null-reference warnings. Visual Studio, VS Code and Rider make refactoring large codebases safe.
Built-in structured logging, metrics and OpenTelemetry support, plus .NET Aspire for local orchestration and dashboards.
Minimal APIs, built-in dependency injection, configuration and OpenAPI generation let a new service go from template to production quickly.
MIT-licensed and developed in the open on GitHub under the .NET Foundation, with Microsoft funding and supporting it commercially.
Everything above is about modern .NET (formerly .NET Core, now just ".NET 8/9/10"). The older .NET Framework 4.x is still supported as a Windows component and still receives security fixes, but it gets no new features, runs only on Windows, and lacks most of the performance, container and tooling improvements described here.
If you have Framework 4.x applications, there's no need to panic. It is worth putting a migration on the roadmap.
Whether you're starting from PHP or from .NET Framework, a staged migration works better than a rewrite:
php2shell will be patched and forgotten, and there will be another critical CVE somewhere next month, possibly in .NET. You can't pick a platform that never has vulnerabilities. You can pick one that makes the worst kinds of bug rare, ships fixes on a schedule you can plan around, tells you when you're exposed, and lets you roll out a fix as a routine build. For applications that hold customers' money and personal data, that's why we build on modern .NET.
Why MCP servers? LLMs are powerful—but they’re limited to what they can ‘see’. The Model Context Protocol (MCP) is an open protocol that standardizes how apps expose tools, resources, and prompts to AI clients so models can interact with real systems in a structured, discoverable way. For .NET developers, this is especially useful because you can build MCP servers in C# using the official MCP C# SDK and run servers locally over stdio or remotely over HTTP. MCP mental model (fast) Host: The application that contains the AI experience (IDE/agent tool). Client: The MCP-capable component inside the host that connects to servers. Server: Your service that exposes tools/resources/prompts. Choosing a transport: STDIO vs Streamable HTTP STDIO (local): The client launches your server as a subprocess and communicates via stdin/stdout. Messages are newline-delimited JSON-RPC, and stdout must contain only protocol messages (logs must go to stderr). Streamable HTTP (remote/scalable): Runs as an independent server reachable over HTTP. Validate the Origin header to reduce DNS rebinding risk and bind to localhost for local runs. Part 1 — The fastest way: .NET 10 MCP Server Project Template Microsoft provides a quickstart showing how to create a minimal MCP server using the .NET 10 SDK and the Microsoft.McpServer.ProjectTemplates template package. This path is great for getting a working server quickly with correct wiring and sane defaults. dotnet new install Microsoft.McpServer.ProjectTemplates Part 2 — Build a Minimal STDIO MCP Server (from scratch) STDIO is ideal when your MCP server needs access to local machine resources and you want the simplest deployment path. Below is a minimal server that uses Microsoft.Extensions.Hosting and exposes one tool (Echo). Step A — Create project & add packages dotnet new console -n MyMcpServer cd MyMcpServer dotnet add package ModelContextProtocol dotnet add package Microsoft.Extensions.Hosting Note: Microsoft’s MCP server walkthrough shows the SDK approach using Microsoft.Extensions.Hosting and MCP server registration in the builder Step B — Program.cs (STDIO server + tool discovery) using Microsoft.Extensions.Hosting; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using ModelContextProtocol.Server; using System.ComponentModel; var builder = Host.CreateApplicationBuilder(args); // IMPORTANT: STDIO servers must keep stdout clean. // Route logs to stderr so they don't corrupt JSON-RPC output. builder.Logging.AddConsole(o => o.LogToStandardErrorThreshold = LogLevel.Trace); builder.Services .AddMcpServer() .WithStdioServerTransport() .WithToolsFromAssembly(); await builder.Build().RunAsync(); [McpServerToolType] public static class EchoTools { [McpServerTool, Description("Echoes the message back to the client.")] public static string Echo([Description("Message to echo")] string message) => $"Hello from MCP (.NET 10): {message}"; } Why the stderr logging rule matters The MCP transport spec explicitly requires that in stdio, servers must not write non-protocol output to stdout; logs should go to stderr Part 3 — Build a Remote MCP Server over Streamable HTTP (ASP.NET Core) If you want a centrally hosted MCP server (team-wide tooling, enterprise integrations), use HTTP transport. MCP’s spec notes Streamable HTTP is the standard remote transport and includes security requirements like Origin validation. [modelconte...rotocol.io], [dometrain.com]Below is a minimal HTTP server exposing a demo Weather tool. Step A — Create ASP.NET Core app & add MCP server support dotnet new web -n MyHttpMcpServer cd MyHttpMcpServer dotnet add package ModelContextProtocol.AspNetCore Step B — Program.cs (HTTP MCP endpoint) using ModelContextProtocol.Server; using System.ComponentModel; var builder = WebApplication.CreateBuilder(args); builder.Services .AddMcpServer() .WithHttpTransport(options => { // Stateless mode is commonly recommended for simple remote servers // that don't need advanced server->client features. options.Stateless = true; }) .WithToolsFromAssembly(); var app = builder.Build(); // Exposes /mcp endpoint (or the configured MCP endpoint) app.MapMcp(); app.Run("http://localhost:3001"); [McpServerToolType] public static class WeatherTools { [McpServerTool, Description("Returns a sample weather status for a city.")] public static string GetWeather(string city) => $"Weather for {city}: Sunny (demo)"; } Security note (important): For Streamable HTTP, MCP recommends validating the Origin header to prevent DNS rebinding and binding locally to localhost when running locally Part 4 — Coding standards & best practices for MCP servers STDIO rule: stdout must be pure JSON-RPC Never log to stdout. Use stderr. Standard: Configure logging to stderr as shown in the STDIO sample. Keep tools small + deterministic Tool methods should be short, validate input, and return structured outputs. Avoid tools that do “too much” (hard to reason about / secure). Validate inputs like public APIs Even though an LLM is “calling” the tool, treat it like an untrusted client: Validate required fields Constrain sizes Apply allowlists where possible Prefer “read-only” tools first Start with: search/read/query tools Then move to “write” tools with extra safety checks. Remote servers must follow transport security guidance Streamable HTTP transport includes security requirements like Origin validation to mitigate DNS rebinding risks Conclusion .NET 10 makes it practical to build MCP servers using local STDIO transport for quick, secure local tooling, and Streamable HTTP for scalable, shared integrations. Start with a small set of safe tools, add observability and security early, and expand capabilities over time.
Introduction In modern AI applications, Retrieval-Augmented Generation (RAG) is one of the most powerful patterns for building intelligent assistants that go beyond generic responses. Instead of relying only on pre-trained knowledge, RAG allows your assistant to retrieve real data (files, database, CSV, APIs) and generate accurate, context-aware answers. In this blog, we will walk through how to build an OpenAI Assistants Bot using RAG, step by step. What is RAG? RAG (Retrieval-Augmented Generation) is a technique where: Data is stored (files, DB, vector store) User asks a question Relevant data is retrieved AI generates response based on retrieved data Instead of hallucinating, AI gives real, data-backed answers Step 1 — Register on OpenAI Platform Open: https://platform.openai.com/ Important Notes: Create an account using your email If using for organization: Ask admin to add you to organization OpenAI provides: Personal account Organization account Step 2 — Generate API Key Go to: Settings → API Keys Important: You need to manage two types of keys properly Type Usage Personal API Key For personal development Organization API Key For production / team usage Best Practice Never hardcode API keys Store in: .env file or Azure Key Vault `` Step 3 — Open Playground & Assistant Go to: OpenAI Dashboard → Playground → Assistants First time? It will prompt: Create a new Assistant OpenAI auto-generates: A random name You can rename it (recommended) What is an Assistant? An Assistant is: A configured AI agent With instructions With connected tools With uploaded data (RAG-ready) It acts as a "brain" that connects: Instructions Files APIs Conversations Step 4 — Add Data (Core of RAG) Example You are an AI assistant for an e-commerce store. Answer user questions based only on the provided data. Do not generate fake information. This is very important for RAG behavior. Now comes the most important part — giving data to your assistant. Supported Data Types PDF CSV TXT DOCX JSON Example: E-Commerce Data (CSV) If you have an e-commerce website, create CSV like: ProductId,Name,Category,Price,Stock 1,iPhone 15,Mobile,999,20 2,Samsung S23,Mobile,899,15 3,MacBook Pro,Laptop,1999,10 `` Upload Process Go to Assistant Open Files section Upload CSV / document Attach file to assistant How RAG Works in Assistants https://via.placeholder.com/1200x500?text=Assistant+RAG+Flow Flow: User asks: “What is price of iPhone 15?” Assistant: Searches uploaded data Retrieves relevant content AI generates answer: “The price of iPhone 15 is $999” Step 5 — Call Assistant via API Example (C# style for you ): var client = new OpenAIClient("API_KEY"); var thread = await client.CreateThreadAsync(); await client.AddMessageAsync(thread.Id, "What is the price of iPhone 15?"); var response = await client.RunAssistantAsync(thread.Id, assistantId); Console.WriteLine(response); Conclusion OpenAI Assistants + RAG is one of the most powerful ways to build: Intelligent Context-aware Production-ready AI applications Instead of building complex pipelines, you can now: Upload data Configure assistant Start querying
Exploring the Latest Features in C# 10 C# 10, the latest version of the C# programming language, brings exciting new features and enhancements that aim to improve developer productivity, code readability, and overall language capabilities. In this blog post, we'll take a closer look at some of the notable features introduced in C# 10. 1. Record Types Improvements Record types, introduced in C# 9, have proven to be a valuable addition for simplifying immutable data structures. In C# 10, record types receive enhancements that make them even more powerful. Example: csharpCopy code public record Person { public string FirstName { get; init; } public string LastName { get; init; } } // C# 10 allows you to simplify the instantiation of record types var person = new Person { FirstName = "John", LastName = "Doe" }; In C# 10, you can use the with expression to create a copy of a record with modified values more concisely: csharpCopy code var updatedPerson = person with { FirstName = "Jane" }; This syntax improves the readability of code when updating record instances. 2. Parameter Null Checking C# 10 introduces the notnull modifier for parameters, enabling developers to enforce non-null arguments at the call site. This can lead to more robust code by catching potential null reference exceptions early. Example: public void ProcessData(notnull string data) { // The 'data' parameter is guaranteed to be non-null within this method } The notnull modifier serves as a contract, making it clear that the method does not accept null arguments. 3. Global Usings C# 10 simplifies the process of importing namespaces by introducing global usings. Now, you can include common namespaces globally, reducing the need to include them in each file. Example: // C# 10 global using global using System; global using System.Collections.Generic; class Program { static void Main() { List<string> myList = new(); // ... } } By using global usings, you can make your code more concise and improve overall code readability. 4. File-scoped Namespace Declarations C# 10 introduces file-scoped namespace declarations, allowing you to define namespaces directly at the file level. This simplifies the organization of code and reduces the need for excessive indentation. Example: csharpCopy code // File-scoped namespace declaration namespace MyNamespace; class MyClass { // ... } This feature promotes cleaner code structure, making it easier to navigate and maintain. 5. Extended Property Patterns C# 10 enhances property patterns, enabling more expressive and concise matching when working with switch statements and patterns. Example: csharpCopy code public class Point { public int X { get; set; } public int Y { get; set; } } var point = new Point { X = 5, Y = 10 }; // C# 10 extended property patterns var result = point switch { { X: 0, Y: 0 } => "Origin", { X: var x, Y: var y } when x == y => "Diagonal", _ => "Unknown" }; These improvements make pattern matching even more powerful and expressive. Conclusion C# 10 introduces several features and enhancements that aim to make the language more expressive, concise, and developer-friendly. By leveraging these new capabilities, developers can write cleaner, more maintainable code. As always, staying updated with the latest language features empowers developers to make the most of the tools at their disposal. Explore these features in your projects and embrace the evolution of C# for a more enjoyable and efficient development experience.