Sitemap

Avoid those Vulnerabilities (DDoS, SQL injection, Open Redirect, XSS, CSRF, Clickjacking, Replay Attack)

12 min readOct 27, 2024

--

Press enter or click to view image in full size

In today’s digital world, people enjoy browsing the internet, often overlooking the dangers of unsecured websites. While developers should address the vulnerability issues, very few can confess they are inexperienced when it comes to security 😅. Understanding common web threats and knowing how to mitigate them is what sets senior professionals apart from juniors.

Today we will explore different web vulnerabilities, like DDoS attack, SQL injection, Open Redirect, XSS, CSRF, Clickjacking, and Replay Attack. The damage they can cause and how to avoid them. We will explore both perspectives — the offensive tactics of a hacker and the defensive strategies of a security expert. By the end of the article, you will know how to address and fix each of them.

Without any further ado, let us begin.

DoS, DDoS

DoS attack (Denial-of-Service attack) is a cyber-attack in which the perpetrator seeks ways to make the server unavailable. Typically accomplished by flooding it with HTTP requests.

Press enter or click to view image in full size

DDoS attack (Distributed Denial-of-Service attack) is an advanced version of DoS when the attack is launched from multiple machines.

Press enter or click to view image in full size

detect 🔍

  1. Just spam your server with requests and see it going down 😂:
while (true) {  
fetch('https://example.com');
}

impact 🧨

A successful DDoS attack can cause significant damage, including:

  • server slowness. If resources like memory or CPU are exhausted it leads to operations delays
  • server unavailability. The main goal of the attack is to put the server down

prevent 👨‍🚒

Those are the most common ways to prevent DDoS attack:

  • investigate different rate-limiting strategies and implement those that suit your needs:
builder.Services.AddRateLimiter(options =>
{
options.AddFixedWindowLimiter("fixedWindowPolicy", policy =>
{
policy.PermitLimit = 100; // Max requests allowed in the window
policy.Window = TimeSpan.FromMinutes(1); // Time window (e.g., 1 minute)
});
});
  • configure monitoring for unusual traffic patterns, and set up alerts to rapidly response to a possible attack
  • consider paid DDoS protection services to stop malicious traffic and suspicious activity. Configure reCAPTCHA to detect bots, etc

It is common to receive HTTP requests. Due to the nature of this attack, preventing it can be challenging; however, there are some strategies to help make your server more robust:

  • optimize backend code. Seek endpoints that use lots of resources and can be improved
  • optimize frontend code. Make sure you don’t have redundant calls, or a recursion that can spam your server
  • scale server both horizontally and vertically on a need
  • it is common to retry HTTP calls in case the server is unavailable. Make sure to have a random delay between retries, not to overwhelm your own server:
var retryPolicy = Policy
.Handle<NetworkException>()
.WaitAndRetry(retriesCount: 5, () =>
{
// Adding random delay to avoid synchronized retries
var randomDelay = new Random().Next(0, 1000);
return TimeSpan.FromMilliseconds(randomDelay);
});
  • perform load testing from time to time, to see how many requests and active users your system can handle

SQL injection

SQL Injection is a security vulnerability that occurs when an attacker is able to insert or manipulate SQL queries through user input.

Press enter or click to view image in full size

detect 🔍

  1. Let’s say you have a page with an input to search for users by email. On the server, we have the next SQL query:
var sql = $@"
SELECT *
FROM [Users]
WHERE [Email] = '{inputEmail}'";

return db.Execute(sql);

2. Try entering the next value in that input:

john.doe@mail.com' OR '1'='1

or this one 😈:

john.doe@mail.com'; DROP TABLE [Users];

3. If your server returns more records than needed it is a sign of an SQL injection.

impact 🧨

This type of attack mostly focuses on the database:

  • an attacker can retrieve sensitive data, such as usernames, passwords, credit card details, personal information, etc
  • an intruder can execute an update, delete, or insert commands. This could corrupt the data or alter the behavior of the application
  • SQL injection can cause denial of service (DoS) by creating resource-intensive queries, deleting essential data, or inserting redundant records

prevent 👨‍🚒

  • do not use raw SQL queries and string concatenation. Sanitize input or use an ORM that escapes parameters by default:
var param = new 
{
InputEmail = inputEmail,
};

var sql = $@"
SELECT *
FROM [Users]
WHERE [Email] = @{nameof(param.InputEmail)}";

return db.Execute(sql, param); // param are sanitazied

Open Redirect

Open Redirect vulnerability occurs when an injected application redirects users to an attacker’s website.

Press enter or click to view image in full size

detect 🔍

  1. Locate all the inputs in your application where a URL can be entered.
  2. See which of those are used in the redirects:
                            .  .  .
string redirectUrl = Request.QueryString["url"];
return Redirect(redirectUrl);

3. Try inputting a malicious URL, and see if users are redirected to a harmful site. If it works, great; you are vulnerable to the Open Redirect attack 😁:

https://my.site.com/login?redirectURL=https://attacker.com

impact 🧨

An open redirect vulnerability mostly leads to a phishing attack. An attacker may build a website, that looks familiar to yours, and trick users into disclosing personal data.

prevent 👨‍🚒

To prevent open redirect vulnerability, sanitize your inputs to only allow redirections to a list of predefined, trusted URLs:

public class AccountController : Controller
{
. . .
[HttpPost]
public IActionResult SubmitForm(string redirectUrl)
{
var allowedUrls = new List<string>
{
"https://your.trusted.domain.com",
"https://in.domain.we.trust.com",
};

if (allowedUrls.Contains(redirectUrl))
{
// redirect to a valid, allowed URL
return Redirect(redirectUrl);
}

// redirect to a safe default page if validation fails
return Redirect("/Home/Index");
}
}

XSS

XSS (Cross-site scripting) is a type of attack when malicious Js-code is injected into a website structure and then executed on the user’s device.

Press enter or click to view image in full size

detect 🔍

  1. You likely have WYSIWYG-editor in your project. It probably stores text as HTML that is later rendered on the other pages.

Try entering one of these harmless scripts in the editor and saving it:

<iframe src="javascript:alert(document.cookie)" />

<img src="x" onerror="alert(42)" />

2. Open the page where HTML is rendered.

Get iamprovidence’s stories in your inbox

Join Medium for free to get updates from this writer.

3. In case you see the alert message, congratulations, it’s an XSS 👶.

impact 🧨

The potential damage can vary and depends on the attacker’s needs:

  • the script can cause recursions and slowly break the app for a user
  • if a JWT token is stored in the local storage, the attacker’s script could read and send it to his server
  • an attacker can monitor a user’s actions, entered keystrokes, etc
  • an attacker can create a DDoS attack using other people’s browsers
  • script can alter the content of web pages
  • etc

prevent 👨‍🚒

  • use cookies with the HttpOnly and Secure flags, so the attacker's script won't be able to read your cookie:
public void ConfigureServices(IServiceCollection services)
{
. . .
services.Configure<CookiePolicyOptions>(options =>
{
options.HttpOnly = HttpOnlyPolicy.Always;
options.Secure = CookieSecurePolicy.Always;
});
. . .
}
  • sanitize user inputs to avoid dangerous tags:
var sanitizer = new HtmlSanitizer();
string safeInput = sanitizer.Sanitize(userInput);

- don’t write your own sanitizer. Use already existing battle-proofed libraries
- sanitize not only the frontend but also the backend. An attacker can send a direct request to a server bypassing UI validation

  • do not render HTML with unsafe methods. Use frameworks and libraries that automatically handle output sanitization
@Html.RenderHtmlSafe(Model.Property)

It is still important to sanitize output, in case an intruder finds other ways to inject scripts, like SQL injection and so on

  • hardcore developers can also add Content-Security-Policy header to the HTTP response:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
. . .
app.Use(async (context, next) =>
{
context.Response.Headers.ContentSecurityPolicy = "connect-src https://api.example.com; script-src https://cdn.example.com";
await next();
});
. . .
}

It instructs the browser which sources can be used to load content. In the example above, the issued header allows a client application to send requests only to https://api.example.com and load js-scripts from https://cdn.example.com

CSRF (XSRF)

CSRF (Cross-Site Request Forgery) attack tricks authenticated users by making them execute unwanted actions on the website.

Press enter or click to view image in full size

This attack mainly targets cookies, because they are automatically sent by the browser. JWT tokens are immune to CSRF.

detect 🔍

  1. Imagine we have a bank website that implements cookie-based authentication.
  2. And we know a user that is logged into his bank account.
  3. We can send this user an email asking him to click a link. The link points to a bank website, and will transfer money to our card:
<html>
<body>
<!-- valid bank URL -->
<form action="https://my.bank/transfer?amount=123" method="POST">
<button type="submit">Click here</button>
</form>
</body>
</html>

4. Because our user is logged in, the browser will automatically include the cookie in the request, and perform unwanted actions on the user’s behalf.

impact 🧨

The primary victims of this attack are authenticated users, since:

  • the attacker can initiate payments from the user’s account
  • the attacker might change account details, such as passwords or email addresses, allowing them to take over user accounts
  • the attacker could modify important data, such as profile settings or other sensitive information, without the user’s knowledge

prevent 👨‍🚒

In order for a CSRF attack to work, an attacker must identify a reproducible web request. Our goal is to secure those endpoints:

  • implement GET methods as read-only operations without side effects
  • sometimes you have to implement GET endpoints that execute actions

A common prevention technique is to include an anti-forgery token. Add a random hash based on the other parameters:

https://my.bank/reset-password?account=john.doe@mail.com&hash=lL73BSpa7h

Later, you can validate that the hash is correct:

public class AccountController : Controller
{
. . .
[HttpGet]
public ActionResult ResetPassword(string account, string hash)
{
var correctHash = HashService.Create(account);
if (correctHash != hash)
{
return RedirectToAction("Error");
}

return View();
}
}

Since an attacker does not know your hash function, he won’t be able to fake the URL.

  • similarly, POST requests can be protected with the anti-forgery token

Usually, frameworks have a support for anti-CSRF tokens. For example, in ASP.NET MVC, you can use the @Html.AntiForgeryToken() helper in the HTML:

<form action="https://my.bank/transfer?amount=123" method="POST">
<!-- Anti-forgery token added here -->
@Html.AntiForgeryToken()
<button type="submit">Click here</button>
</form>

That is validated on the backend:

[HttpPost]
[ValidateAntiForgeryToken] // validate CSRF token
public ActionResult ActionName(string username)
{
// Your logic here
return View();
}
  • use SameSite cookie attribute that controls whether a browser should send the cookies to a remote origin. It can accept one of three values:
    — None — cookies are sent with all requests
    — Strict — cookies are only sent when the request is from the same site
    — Lax(default) — cookies are not sent with most cross-site requests, like those initiated by third-party domains. However, they are sent if the request is the result of a user clicking a link (a top-level navigation)
  • implement CORS (Cross-Origin Resource Sharing)

CORS is a security feature implemented by browsers to control which domains can send requests to the server:

public void ConfigureServices(IServiceCollection services)
{
services.AddCors(options =>
{
options.AddPolicy("MyCorsPolicy", builder =>
{
builder
.WithOrigins("https://example.com") // Add your allowed origins
.AllowAnyHeader()
.AllowAnyMethod();
});
});

. . .
}

Keep in mind, CSRF attack can still come from the same origin, meaning CORS wouldn’t block them. CORS only addresses cross-origin issues, not same-origin attacks. Combine CORS with other techniques.

Clickjacking

Clickjacking is when an attacker uses a transparent layer to trick a user into clicking on a button or link on another page.

Press enter or click to view image in full size

The attack is similar to CSRF since both trick users into taking an unintended action, yet they work differently and target distinct vulnerabilities.

detect 🔍

  1. Email an innocent user (or trick him in other ways).
  2. In this email, add a button that appears harmless (e.g., “Click here” or “Unsubscribe”).
  3. On top of this button, place a transparent <iframe> with our website.
<html>
<style>
.iframe {
opacity: 0;
}
</style>
<body>
<iframe src="https://my.bank/transfer?amount=123"></iframe>

<button>Ckick here!</button>
</body>
</html>

4. When the user tries to click the button, he actually clicks the iframe, which will execute unwanted actions on your website.

impact 🧨

Similarly to the previous attack, this one aims for the end users:

  • victims may make unintentional purchases, subscribe to services, or even transfer funds unknowingly
  • clickjacking can lead to actions like changing account settings, enabling permissions, or granting access to the attacker
  • users unknowingly perform actions such as liking posts, sharing content, subscribing to newsletters, etc

prevent 👨‍🚒

  • use SameSite cookie attribute (None,Strict, Lax)
  • (legacy) add X-Frame-Options HTTP header that is used to control whether a web page can be displayed within an <iframe>:
    — DENY — prevent any framing
    — ALLOW-FROM <origin>— allows the page to be displayed in a frame only on specific, whitelisted origins
    — SAMEORIGIN —allow framing only on the same origin as the page itself
public class Startup
{
. . .
public void Configure(IApplicationBuilder app)
{
. . .
app.Use(async (context, next) =>
{
context.Response.Headers.XFrameOptions = "deny";
await next();
});
. . .
}
}

Although this header is widely supported, it is not part of the official HTTP standard and may not properly work in all browsers.

  • add Content-Security-Policy HTTP header with frame-ancestors directive, which allows/disallows embedding of content using iframe:
    — frame-ancestors ‘none’ — prevent any framing
    — frame-ancestors www.example.com <origin> — allows framing on some origins
    — frame-ancestors ‘self’ — allow embedding of own content only

Replay Attack

Replay Attack happens when the intruder intercepts a legitimate HTTP request, and replays it multiple times.

Press enter or click to view image in full size

detect 🔍

  1. Find a POST request in your system and execute it multiple times with the same parameters using Postman or similar tools.
  2. If the system processes the repeated requests as valid, it may lead to side effects such as data duplication or other unintended outcomes.

impact 🧨

The consequences of replay attacks can be severe, affecting both users and the system:

  • an attacker intercepts a payment request (e.g. transfer $100 from Alice to Bob) and resends it to the server, causing the server to process the same transaction again
  • an intruder can replay a request that inserts redundant entries in the database, slowing down the server by exhausting its memory or CPU
  • replaying computation-intensive operations will cause denial of service

prevent 👨‍🚒

To protect against replay attacks, systems should implement mechanisms to differentiate between a legitimate new request and a previously valid one:

  • add a unique, random number to each transaction. The server should accept the request with this value only once. This value is called nonce (number used once):
[HttpPost]
public ActionResult CreateNewUser(string userName, string nonce)
{
// Check if the nonce has already been used
if (IsNonceUsed(nonce))
{
return BadRequest("Replay attack detected: Nonce has already been used.");
}

// Mark the nonce as used
MarkNonceAsUsed(nonce);

// Your logic here

return View();
}
  • add a sequence number to each message. The server tracks the last valid sequence number and rejects older or duplicate messages
  • add timestamp information to the request to ensure that messages are only valid for a specific time window

Wrapping it up

As you can see, security is not that complicated 🙃. Now you know common web vulnerabilities, how to detect and fix them, you learned how to… Wait. Did I just show you how to break other’s people websites 🤔? Please don’t. And if you do, don’t tell about me 😅.

Of course, in practice, everything is more complicated. This is only the tip of the iceberg. There are other vulnerabilities, there are other cyber attacks, other threats, and all that can be explored in another article 😉.

Press enter or click to view image in full size

Let me know in the comment section how often you have to deal with security on your project 💬
Give this article a clap or a few if you like it 👏
You can support me with a link below ☕️
Don’t forget to follow to receive more security advice✅
Read the article one more time, meanwhile, I will 🏃🚓

--

--

Written by iamprovidence

👨🏼‍💻 Full Stack Dev writing about software architecture, patterns and other programming stuff https://www.buymeacoffee.com/iamprovidence