Skip to main content

Insecure Access Control

Fixing Comparison that is Vulnerable to Timing Attackโ€‹

By their nature, string comparisons (or any comparison of contiguous memory) short-circuit. This means that the comparison will return false as soon as 2 different characters are detected. This helps improve a programming language's performance but can allow timing attacks to occur.

For example, say that passwords were compared using === a server. An attacker could then enumerate every single character that could be used at the start of a password and time how long each request took. The request that would take the longest would then be likely to contain the first character of the password, as the string comparison would short-circuit later in the comparison process.

Rule-specific references:

Option A: Compare Hashesโ€‹

This method requires the developer to compare hashes instead of comparing plain text. This option also encourages developers to store password hashes instead of plain-text passwords - this is best practice to avoid passwords being leaked in the event of a data breach.

The easiest way of implementing this is to use the bcrypt npm package, as it packs information such as the salt, the algorithm, and the cost into a single string.

Another option would be to use something like crypto.scrypt, however, you would need to store information such as the cost and the salt manually.

  1. Locate the vulnerable pattern (example below):

    const fastify = require('fastify')()
    // ...
    const authenticate = { realm: "TestRealm" }
    fastify.register(require('@fastify/basic-auth'), { validate, authenticate })

    function validate(user, pass, req, reply, done) {
    if(user in users && pass === users[user].pass) {
    done()
    } else done(new Error("Authentication isn't valid"));
    }
  2. Replace it with one of the following patterns (examples below):

    Using bcrypt

    const fastify = require('fastify')()
    const bcrypt = require('bcrypt')
    // ...
    const authenticate = { realm: "TestRealm" }
    fastify.register(require('@fastify/basic-auth'), { validate, authenticate })

    async function validate(user, pass, req, reply, done) {
    if(user in users) {
    const match = await bcrypt.compare(pass, users[user].passHash)
    done()
    } else done(new Error("Authentication isn't valid"));
    }

    // Somewhere else in the code ...

    async function newUser(username, pass) {
    users[user] = {
    username,
    passHash: await bcrypt.hash(pass, 20),
    }

    return users[user];
    }
  3. Test it

  4. Ship it ๐Ÿšข and relax ๐ŸŒด

Option B: Perform Constant Time String Comparisonโ€‹

This method entails using a time-agnostic string comparison algorithm. You can implement these yourself, but it is recommended that you use a package that implements this for you such as tsscmp

  1. Locate the vulnerable pattern (example below):

    const express = require('express')
    const bodyParser = require('body-parser')

    const app = express();

    app.use(bodyParser.json()); // to support JSON-encoded bodies
    app.use(bodyParser.urlencoded({ // to support URL-encoded bodies
    extended: true
    }));

    // ...
    const API_KEY = "abcdefghijklmnopqrstuvwxyz"
    // ...

    app.use('/api/v1', (req, resp, next) => {
    if(req.body.apiKey !== API_KEY) {
    resp.status(403).send('API key is invalid');
    return;
    }

    next()
    })
  2. Replace it with one of the following patterns (examples below):

    Using tsscmp

    const express = require('express')
    const bodyParser = require('body-parser')
    const timingSafeCompare = require('tsscmp')

    const app = express();

    app.use(bodyParser.json()); // to support JSON-encoded bodies
    app.use(bodyParser.urlencoded({ // to support URL-encoded bodies
    extended: true
    }));

    // ...
    const API_KEY = "abcdefghijklmnopqrstuvwxyz"
    // ...

    app.use('/api/v1', (req, resp, next) => {
    if(!timingSafeCompare(req.body.apiKey, API_KEY)) {
    resp.status(403).send('API key is invalid');
    return;
    }

    next()
    })

    Using a Custom XOR String Comparison Algorithm

    const express = require('express')
    const bodyParser = require('body-parser')

    const app = express();

    app.use(bodyParser.json()); // to support JSON-encoded bodies
    app.use(bodyParser.urlencoded({ // to support URL-encoded bodies
    extended: true
    }));

    // ...
    const API_KEY = "abcdefghijklmnopqrstuvwxyz"
    // ...

    app.use('/api/v1', (req, resp, next) => {
    let diff = 0;
    for(let i = 0; i < req.body.apiKey; i++) {
    let actualApiChar = API_KEY[i] || (req.body.apiKey[i] + 1)
    diff |= actualApiChar ^ req.body.apiKey[i]
    }

    // highlught-next-line
    if(diff !== 0) {
    resp.status(403).send('API key is invalid');
    return;
    }

    next()
    })
  3. Test it

  4. Ship it ๐Ÿšข and relax ๐ŸŒด

Fixing Cross-Site Request Forgeryโ€‹

About Cross-Site Request Forgery (CSRF)

What is Cross-Site Request Forgery?โ€‹

Cross-site request forgery (CSRF or XSRF) is a type of web application vulnerability that allows an attacker to exploit the trust relationship between a user's web browser and a web application. The vulnerability occurs when a web application doesn't adequately verify that a request comes from an authorized user, and it can allow an attacker to force an unsuspecting user to perform actions on the application without their knowledge or consent.

In a CSRF attack, an attacker will create a malicious website or email that contains a link or form that automatically sends a request to the vulnerable web application. If the victim clicks on the link or submits the form, the request will be sent to the web application, and the application will assume that it was initiated by the legitimate user.

Check out this video for a high-level explanation:

What is the impact of Cross-Site Request Forgery?โ€‹

Some potential impacts of CSRF attacks are:

  • Unauthorized actions: An attacker can use a CSRF attack to force a user to perform unauthorized actions on the vulnerable web application, such as changing their password, making unauthorized purchases, or deleting or modifying data.
  • Data breaches: An attacker can use a CSRF attack to override sensitive information from the vulnerable web application, such as login credentials.
  • Financial loss: An attacker can use a CSRF attack to force a user to make unauthorized financial transactions, resulting in financial loss for the victim.
  • Reputation damage: A successful attack exploiting a CSRF vulnerability can lead to loss of customer trust and reputational damage for the organization.
  • Regulatory violations: A successful attack exploiting a CSRF vulnerability can lead to violations of regulatory requirements, such as data protection laws.

How to prevent Cross-Site Request Forgery?โ€‹

To prevent cross-site request forgery (CSRF) attacks, web applications can implement the following measures:

  • Implement CSRF tokens: Use CSRF tokens to ensure that requests are made by legitimate users and not by attackers. The token is a unique value that is generated for each user session, and it is included in each form or link request. The server-side application checks that the token matches the user's session, and if not, it rejects the request.
  • Use HTTP Referer header validation: Validate the HTTP Referer header to ensure that requests are coming from the expected source. This can help prevent attackers from spoofing the source of a request.
  • Use anti-CSRF frameworks: Use anti-CSRF frameworks or libraries that provide built-in protection against CSRF attacks, such as Django's CSRF protection, Spring Security's CSRF protection, or the OWASP CSRFGuard library.
  • Use SameSite cookies: Use SameSite cookies to prevent CSRF attacks that leverage session hijacking through cross-site scripting (XSS) vulnerabilities.
  • Limit the use of HTTP methods: Limit the use of sensitive HTTP methods (e.g. PUT, DELETE) to authenticated and authorized users only.

Referencesโ€‹

Taxonomiesโ€‹

Explanation & Preventionโ€‹

Trainingโ€‹

Insecure use of CSRF middlewareโ€‹

The method-override middleware allows the method of a request to be overridden either by a X-HTTP-Method-Override header, or a _method parameter in a form.

However, by default, CSRF middleware doesn't check GET/HEAD/OPTIONS requests, and is not aware of the method-override middleware - it won't check for X-HTTP-Method-Override headers or _method form data.

This means that a POST request can masquerade as a GET request, and bypass any CSRF checks. This is important as GET/HEAD/OPTIONS requests are (usually) not supposed to modify any important data.

Note that GET/HEAD/OPTIONS requests that modify important/user data are a potential security issue due to the fact most CSRF middleware isn't configured to check GET/HEAD/OPTIONS requests.

Using CSRF middleware before method-override middleware may cause CSRF vulnerabilities.

Rule-specific references:

Option A: Use the correct order of the CSRF middlewareโ€‹

This option is fairly self-explanatory: you simply put the method-override middleware before the CSRF middleware. This means the method-override middleware will apply the overridden HTTP method before the request reaches the CSRF middleware.

  1. Locate the vulnerable pattern (example below):

    const express = require('express');
    const csurf = require('csurf');
    const methodoverride = require('method-override');
    ...
    let app = express();
    app.use(csurf());
    app.use(methodoverride('x-http-method-override'));
    ...
  2. Replace it with one of the following patterns (examples below):

    const express = require('express');
    const csurf = require('csurf');
    const methodOverride = require('method-override');
    ...
    let app = express();
    app.use(methodOverride('X-HTTP-Method-Override'));
    app.use(csurf());
    ...
  3. Test it

  4. Ship it ๐Ÿšข and relax ๐ŸŒด

Fixing Open Redirectsโ€‹

About Open Redirects

What are open redirects?โ€‹

Open redirect is a web application vulnerability that occurs when an application uses unvalidated user input as part of a redirect URL.

An attacker can exploit this vulnerability by manipulating the input to redirect a victim to a malicious site. The impact of this vulnerability can range from phishing attacks to theft of sensitive information.

Check out this video for a high-level explanation:

What is the impact of open redirects?โ€‹

An open redirect vulnerability can allow an attacker to:

  • Conduct phishing attacks: An attacker can trick a victim into clicking a link that appears to be legitimate but actually redirects them to a malicious website where their sensitive information can be compromised.
  • Steal sensitive information: An attacker can craft a URL that appears to be legitimate but redirects to a site that steals the victim's sensitive information, such as login credentials or credit card information.
  • Distribute malware: An attacker can use an open redirect to send victims to a site that distributes malware, allowing them to gain control of the victim's device.

How to prevent open redirects?โ€‹

To prevent improper open redirect vulnerabilities, it is important to implement secure coding practices, including:

  • Validate redirect URLs: Ensure that all redirect URLs are validated and verified before being used in the application. This can include ensuring that the redirect URL is within the same domain as the current page or using an allow list of permitted domains.
  • Avoid using untrusted input: Avoid using untrusted user input as part of a redirect URL. Use a standard URL-building function instead, which allows you to control the parameters used in the redirect URL.
  • Implement security headers: Implement security headers such as Content-Security-Policy (CSP) and X-Frame-Options to help prevent open redirect attacks.
  • Use secure coding practices: Use secure coding practices to prevent open redirect vulnerabilities. This includes input validation, output encoding, and regular security audits.

Referencesโ€‹

Taxonomiesโ€‹

Explanation & Preventionโ€‹

Trainingโ€‹

Open Redirect (Frontend)โ€‹

Passing untrusted input into window.location.href can facilitate phishing attacks from legitimate domains.

Option A: Whitelist certain redirect locationsโ€‹

The safest way to fix this kind of vulnerability is to whitelist certain URLs.

  1. Locate the vulnerable pattern (example below):

       function vulnerable() {
    let req = new XMLHttpRequest();
    req.onload = function() {
    let redirect_loc = JSON.parse(req.responseText).new_location;
    window.location.href = redirect_loc;
    };

    req.open("https://example.com/get_redirect");
    req.send();
    }
  2. Replace it with one of the following patterns (example below):

    Example #1

       function vulnerable() {
    let req = new XMLHttpRequest();
    req.onload = function() {
    let redirect_loc = JSON.parse(req.responseText).new_location;
    let url = new URL(redirect_loc);
    if(url.hostname == window.location.hostname) {
    window.location.href = redirect_loc;
    }
    };

    req.open("https://example.com/get_redirect");
    req.send();
    }
  3. Test it

  4. Ship it ๐Ÿšข and relax ๐ŸŒด

Option B: Assign unique IDs to each valid URLโ€‹

This can be done by having a list of valid URLs, and each ID can be an array index. Other more complex solutions exist (i.e. have a database of authorized URLs) - but the idea is the same.

  1. Locate the vulnerable pattern (example below):

       function vulnerable() {
    let req = new XMLHttpRequest();
    req.onload = function() {
    let redirect_loc = JSON.parse(req.responseText).new_location;
    window.location.href = redirect_loc;
    };
  2. Replace it with one of the following patterns (example below):

       const REDIRECTS = [
    "#header-1",
    "#header-2",
    "/login",
    "/home"
    ];

    function safe() {
    let req = new XMLHttpRequest();
    req.onload = function() {
    let redirectID = JSON.parse(req.responseText).id;
    if(redirectID < REDIRECTS.length) {
    window.location.href = redirects[redirectID];
    }
    };

    req.open("https://example.com/get_redirect_id");
    req.send();
    }
  3. Test it

  4. Ship it ๐Ÿšข and relax ๐ŸŒด