Repository navigation
Day 06
In Day 5, we built a live Community Guestbook where anyone can post a comment and everyone else can see it.
That is exciting... but there is a hidden danger in our Day 5 code! Right now, our server takes whatever text a visitor types into a form or URL and puts it directly into our HTML page.
Remember: HTML is not just plain text — it is code that the browser executes!
In Day 6, we will:
- Break our own website on purpose to see why trusting raw user input is dangerous (HTML Injection & XSS).
-
Understand how
WebApp.HtmlEncode()neutralizes dangerous characters (<,>,",&). -
Fix our
HandleGreet(),HandleGuestbook(), andHandleContact()methods step by step so our website is completely safe.
Before we write any new code, let's see the problem with our own eyes.
Run your Day 5 program (dotnet run or press Start in Visual Studio) and open http://localhost:8080/guestbook.
In the Guestbook form, type your name and post a comment with this exact text:
<b>I am BOLD!</b>
Look closely at the result in the guestbook list:
- Your comment is displayed as I am BOLD!
- The
<b>and</b>tags are invisible — because the browser thought your comment was part of the website's HTML code and ran it!
What if someone posted <h1>HUGE TEXT</h1> or <marquee>Moving text</marquee>? They could completely mess up the layout of your page for every visitor!
Now try something much more serious. Post this comment in the Guestbook:
<script>alert('Your website has been hacked!')</script>
As soon as the page reloads, a browser popup box appears on your screen! Even worse: because that comment is saved in guestbookEntries, that popup will now appear for every single person who visits your Guestbook!
In the cybersecurity world, this famous vulnerability is called Cross-Site Scripting (XSS). When a website lets a visitor inject <script> tags into a page, an attacker can run malicious JavaScript inside other visitors' browsers (for example, to steal login sessions or redirect visitors to a fake scam page).
Our /greet and /contact pages have the exact same problem! Try visiting this URL in your browser:
http://localhost:8080/greet?name=<i>Hello</i>
The greeting appears in italics (Hello, Hello!), because {name} was inserted directly into the HTML without any protection.
Why did this happen? Because the browser cannot tell the difference between:
-
Trusted HTML that you (the developer) wrote (
<h1>,<ul>,<li>), and - Untrusted text that a visitor typed into an input box.
Whenever the browser sees a < character, it assumes a new HTML tag is starting!
To stop the browser from treating visitor text as HTML tags, cshttp provides a built-in helper method:
string safeText = WebApp.HtmlEncode(rawText);It scans the string and replaces special HTML characters with safe HTML Entities:
| If the visitor types |
WebApp.HtmlEncode turns it into |
What the browser displays on screen |
|---|---|---|
< |
< |
< (as harmless text!) |
> |
> |
> (as harmless text!) |
" |
" |
" |
' |
' |
' |
& |
& |
& |
For example, if a mischievous visitor types:
<script>alert('hack')</script>
WebApp.HtmlEncode() converts it into:
<script>alert('hack')</script>
When the browser receives <script>, it knows not to run it as code—instead, it harmlessly draws the literal characters <script> on the screen as plain text!
The Golden Rule of Web Security: Never trust user input! Every time you place text that came from a visitor (
Request.QueryStringorRequest.Form) into an HTML string, wrap it withWebApp.HtmlEncode(...).
Let's fix our three interactive pages one by one, starting with the simplest one: HandleGreet().
In Day 5, the else block inside HandleGreet() looked like this:
else
{
greetingMessage = $"<h2>Hello, {name}! Welcome to C# Web Development!</h2>";
}Here, {name} comes directly from Request.QueryString["name"], which is untrusted user input.
Let's encode name into a new variable called safeName before putting it into greetingMessage:
else
{
// NEW IN DAY 6: Encode the name from the URL so HTML tags cannot run!
string safeName = WebApp.HtmlEncode(name);
greetingMessage = $"<h2>Hello, {safeName}! Welcome to C# Web Development!</h2>";
}Now, if someone visits /greet?name=<i>Hello</i>, safeName becomes <i>Hello</i>, and the browser safely prints Hello, <i>Hello</i>! without turning it italic!
Next, let's look at HandleGuestbook() where we loop through guestbookEntries to build our <ul> list.
In Day 5, our foreach loop looked like this:
StringBuilder listHtml = new StringBuilder();
listHtml.Append("<ul>");
foreach (string entry in guestbookEntries)
{
listHtml.Append($"<li style='margin-bottom: 8px;'>{entry}</li>");
}
listHtml.Append("</ul>");Notice that {entry} contains the visitor's name and comment, which was placed directly inside <li>...</li>.
Let's encode entry using WebApp.HtmlEncode(entry) before appending it into the <li> tag:
StringBuilder listHtml = new StringBuilder();
listHtml.Append("<ul>");
foreach (string entry in guestbookEntries)
{
// NEW IN DAY 6: Encode the entry before putting it into the HTML
string safeEntry = WebApp.HtmlEncode(entry);
listHtml.Append($"<li style='margin-bottom: 8px;'>{safeEntry}</li>");
}
listHtml.Append("</ul>");Notice what we encoded—and what we did NOT encode! We only encoded
entry(the visitor's text). We did not encode<li style='margin-bottom: 8px;'>or</li>, because we wrote those<li>tags and we want the browser to render them as a bulleted list!
Finally, let's check HandleContact(). When a visitor submits the Contact form (POST), we read three fields from Request.Form:
string senderName = Request.Form["sender"];
string email = Request.Form["email"];
string message = Request.Form["message"];And then we display all three fields on the "Thank You" confirmation page!
Let's encode all three variables (safeName, safeEmail, safeMessage) before inserting them into thankYouContent:
if (Request.HttpMethod == "POST")
{
string senderName = Request.Form["sender"];
string email = Request.Form["email"];
string message = Request.Form["message"];
Console.WriteLine($"[New Message] From: {senderName} ({email}) - {message}");
// NEW IN DAY 6: Encode everything the visitor typed before putting it into HTML
string safeName = WebApp.HtmlEncode(senderName);
string safeEmail = WebApp.HtmlEncode(email);
string safeMessage = WebApp.HtmlEncode(message);
string thankYouContent = $@"
<h1>Thank You, {safeName}!</h1>
<p>We received your message and will reply to <b>{safeEmail}</b> soon.</p>
<div style='background: #f0f0f0; padding: 10px; border-radius: 4px;'>
<i>""{safeMessage}""</i>
</div>
<p><a href='/contact'>Send another message</a></p>";
RenderPage("Message Received", thankYouContent);
return;
}(Note: We don't need to HTML-encode Console.WriteLine(...) because the console terminal is plain text, not a web browser running HTML!)
Here is the complete Program.cs with all three pages protected by WebApp.HtmlEncode():
using System;
using System.Collections.Generic;
using System.Text;
using cshttp;
namespace ConsoleApp1
{
internal class Program
{
static List<string> guestbookEntries = new List<string>()
{
"[2026-10-22 09:00] System: Welcome to our new Guestbook!",
"[2026-10-22 09:05] Alice: First comment! C# web dev is awesome."
};
static void Main(string[] args)
{
WebApp.Run(8080, OnRequest);
}
static void OnRequest()
{
string path = Request.Path.ToLower();
switch (path)
{
case "/":
case "/home":
HandleHome();
break;
case "/guestbook":
HandleGuestbook();
break;
case "/greet":
HandleGreet();
break;
case "/about":
HandleAbout();
break;
case "/contact":
HandleContact();
break;
default:
HandleNotFound();
break;
}
}
static void RenderPage(string title, string bodyContent)
{
string html = $@"
<html>
<head>
<title>{title}</title>
</head>
<body>
<nav>
<a href='/'>Home</a> |
<a href='/guestbook'>Guestbook</a> |
<a href='/greet'>Greet</a> |
<a href='/about'>About</a> |
<a href='/contact'>Contact</a>
</nav>
<hr />
{bodyContent}
<hr />
<footer>
<small>Server Time: {DateTime.Now:yyyy-MM-dd HH:mm:ss}</small>
</footer>
</body>
</html>";
Response.Write(html);
}
static void HandleGuestbook()
{
if (Request.HttpMethod == "POST")
{
string author = Request.Form["author"];
string comment = Request.Form["comment"];
if (!string.IsNullOrWhiteSpace(author) && !string.IsNullOrWhiteSpace(comment))
{
string timestamp = DateTime.Now.ToString("yyyy-MM-dd HH:mm");
string entry = $"[{timestamp}] {author}: {comment}";
guestbookEntries.Add(entry);
}
Response.Redirect("/guestbook");
return;
}
StringBuilder listHtml = new StringBuilder();
listHtml.Append("<ul>");
foreach (string entry in guestbookEntries)
{
// NEW IN DAY 6: Encode the entry before putting it into the HTML
string safeEntry = WebApp.HtmlEncode(entry);
listHtml.Append($"<li style='margin-bottom: 8px;'>{safeEntry}</li>");
}
listHtml.Append("</ul>");
string content = $@"
<h1>Community Guestbook</h1>
<p>Leave a message for everyone visiting this site!</p>
<form action='/guestbook' method='post' style='background: #f9f9f9; padding: 15px; border: 1px solid #ddd; max-width: 450px;'>
<div>
<label>Your Name:</label><br />
<input type='text' name='author' required style='width: 100%;' />
</div>
<br />
<div>
<label>Your Comment:</label><br />
<textarea name='comment' rows='3' required style='width: 100%;'></textarea>
</div>
<br />
<button type='submit'>Sign Guestbook</button>
</form>
<hr />
<h2>Recent Entries ({guestbookEntries.Count})</h2>
{listHtml.ToString()}";
RenderPage("Guestbook", content);
}
static void HandleContact()
{
if (Request.HttpMethod == "POST")
{
string senderName = Request.Form["sender"];
string email = Request.Form["email"];
string message = Request.Form["message"];
Console.WriteLine($"[New Message] From: {senderName} ({email}) - {message}");
// NEW IN DAY 6: Encode everything the visitor typed
string safeName = WebApp.HtmlEncode(senderName);
string safeEmail = WebApp.HtmlEncode(email);
string safeMessage = WebApp.HtmlEncode(message);
string thankYouContent = $@"
<h1>Thank You, {safeName}!</h1>
<p>We received your message and will reply to <b>{safeEmail}</b> soon.</p>
<div style='background: #f0f0f0; padding: 10px; border-radius: 4px;'>
<i>""{safeMessage}""</i>
</div>
<p><a href='/contact'>Send another message</a></p>";
RenderPage("Message Received", thankYouContent);
return;
}
string formContent = @"
<h1>Contact Us</h1>
<p>Send us a message below:</p>
<form action='/contact' method='post'>
<div>
<label>Your Name:</label><br />
<input type='text' name='sender' required style='width: 300px;' />
</div>
<br />
<div>
<label>Email Address:</label><br />
<input type='email' name='email' required style='width: 300px;' />
</div>
<br />
<div>
<label>Message:</label><br />
<textarea name='message' rows='5' cols='40' required></textarea>
</div>
<br />
<button type='submit'>Send Message</button>
</form>";
RenderPage("Contact", formContent);
}
static void HandleGreet()
{
string name = Request.QueryString["name"];
string greetingMessage = "";
if (string.IsNullOrEmpty(name))
{
greetingMessage = "<p>Please enter your name below, or try clicking a quick link!</p>";
}
else
{
// NEW IN DAY 6: Encode the name from the URL
string safeName = WebApp.HtmlEncode(name);
greetingMessage = $"<h2>Hello, {safeName}! Welcome to C# Web Development!</h2>";
}
string content = $@"
<h1>Interactive Greeting</h1>
{greetingMessage}
<form action='/greet' method='get'>
<label>Your Name:</label>
<input type='text' name='name' placeholder='Type your name...' />
<button type='submit'>Say Hello</button>
</form>
<p>Or test query links directly:
<a href='/greet?name=Alice'>Greet Alice</a> |
<a href='/greet?name=Bob'>Greet Bob</a>
</p>";
RenderPage("Greet", content);
}
static void HandleHome()
{
string content = @"
<h1>Welcome cshttp!</h1>
<p>Our <a href='/guestbook'>Guestbook</a> is now safe from HTML injection. Try to break it!</p>";
RenderPage("Home", content);
}
static void HandleAbout()
{
string content = @"
<h1>About Us</h1>
<p>It's Me!</p>";
RenderPage("About Us", content);
}
static void HandleNotFound()
{
Response.StatusCode = 404;
string content = @"
<h1>404 Not Found</h1>
<p>The page that you are looking for does not exist.</p>";
RenderPage("404 Not Found", content);
}
}
}- Stop the old server and run the updated Day 6 code (
dotnet runor press Start in Visual Studio). - Open
http://localhost:8080/guestbook. - Try our attacks again! Post
<b>I am BOLD!</b>and<script>alert('hacked')</script>. - Look at the result:
- Nothing turns bold, and no popup box appears!
- Instead, the literal characters
<b>I am BOLD!</b>and<script>alert('hacked')</script>are safely displayed on the screen as plain text.
- Right-click anywhere on the web page and select View Page Source. Look at your
<li>items in the HTML source code: you will see<b>and<script>—that isWebApp.HtmlEncode()doing its job! - Visit
http://localhost:8080/greet?name=<i>Hello</i>—the greeting now safely prints<i>Hello</i>without turning italic.
In Day 7, we will upgrade our Guestbook entries from a single glued-together string into a structured C# class (GuestbookEntry) so we can store and style the author's name, timestamp, and message separately!
Technical Specs
14 Days Tutorial