Skip to main content

Weak CAPTCHA

Need

Implementation of a strong and reliable CAPTCHA system

Context

  • Usage of TypeScript for type-checking and compiling JavaScript code
  • Usage of Express for building web applications and APIs

Description

Non compliant code

import express from 'express';

const app = express();

app.get('/login', (req, res) => {
const captcha = Math.random().toString(36).substring(7);
res.send(`
<form action="/login" method="POST">
<input type="text" name="username" placeholder="Username" required>
<input type="password" name="password" placeholder="Password" required>
<input type="text" name="captcha" placeholder="Captcha" required>
<img src="/captcha?code=${captcha}" alt="Captcha">
<button type="submit">Login</button>
</form>
`);
});

app.get('/captcha', (req, res) => {
const code = req.query.code;
res.send(`
<img src="http://insecure-captcha-service.com/captcha?code=${code}" alt="Captcha">
`);
});

app.post('/login', (req, res) => {
const { username, password, captcha } = req.body;

// Verify the captcha
if (captcha === req.session.captcha) {
// Perform login logic
res.send('Login successful!');
} else {
res.send('Invalid captcha. Please try again.');
}
});

app.listen(3000, () => {
console.log('Server is running on port 3000');
});

The vulnerability in this code is a weak CAPTCHA implementation. CAPTCHA is a security mechanism used to prevent automated attacks by requiring users to complete a challenge that is easy for humans but difficult for machines. However, in this code, the CAPTCHA implementation is insecure and can be easily bypassed.

The vulnerability lies in the way the CAPTCHA is generated and verified. When a user visits the '/login' route, a random CAPTCHA code is generated using Math.random() and displayed in an HTML form. The CAPTCHA code is also used to fetch the CAPTCHA image from an external service and display it on the page.

The problem is that the CAPTCHA code is exposed in the HTML code of the page, making it accessible to automated tools like optical character recognition (OCR) software. An attacker can easily extract the CAPTCHA code from the HTML and use OCR tools to automatically read and pass the CAPTCHA.

In the '/login' POST route, the code compares the user-provided CAPTCHA with the one stored in the session. If they match, the login is considered successful. However, since the CAPTCHA code is easily accessible, an attacker can simply extract it from the HTML, bypassing the CAPTCHA check and performing automated attacks.

To fix this vulnerability, the CAPTCHA response should be handled at the server level and not exposed in the HTML code. Additionally, measures should be taken to ensure that the CAPTCHA cannot be easily read by OCR tools.

Steps

  • Replace the insecure CAPTCHA implementation with a secure one that cannot be easily bypassed by optical recognition tools.
  • Move the CAPTCHA response generation and verification logic to the server-side instead of exposing it in the HTML code.
  • Ensure that the CAPTCHA image source URL is not directly pointing to an insecure CAPTCHA service.
  • Implement server-side session management to store the CAPTCHA response and verify it during the login process.
  • Update the login logic to check the CAPTCHA response stored in the session instead of comparing it directly with the request body.

Compliant code

import express from 'express';
import session from 'express-session';

const app = express();

app.use(express.urlencoded({ extended: true }));
app.use(session({
secret: 'your_secret_key',
resave: false,
saveUninitialized: true
}));

app.get('/login', (req, res) => {
const captcha = Math.random().toString(36).substring(7);
req.session.captcha = captcha;
res.send(`
<form action="/login" method="POST">
<input type="text" name="username" placeholder="Username" required>
<input type="password" name="password" placeholder="Password" required>
<input type="text" name="captcha" placeholder="Captcha" required>
<img src="/captcha" alt="Captcha">
<button type="submit">Login</button>
</form>
`);
});

app.get('/captcha', (req, res) => {
const code = req.session.captcha;
res.send(`
<img src="/captcha-image?code=${code}" alt="Captcha">
`);
});

app.get('/captcha-image', (req, res) => {
const code = req.query.code;
// Generate the CAPTCHA image using a secure CAPTCHA service
// Replace the following line with your secure CAPTCHA service implementation
const captchaImage = generateCaptchaImage(code);
res.send(captchaImage);
});

app.post('/login', (req, res) => {
const { username, password, captcha } = req.body;

// Verify the captcha
if (captcha === req.session.captcha) {
// Perform login logic
res.send('Login successful!');
} else {
res.send('Invalid captcha. Please try again.');
}
});

app.listen(3000, () => {
console.log('Server is running on port 3000');
});

// Secure CAPTCHA service implementation
function generateCaptchaImage(code: string): string {
// Implement your secure CAPTCHA service logic here
// Return the CAPTCHA image as a string
return `<img src="https://secure-captcha-service.com/captcha?code=${code}" alt="Captcha">`;
}

The fixed code addresses the vulnerability by implementing a secure CAPTCHA system. Here's an explanation of the changes made:

  1. The code now imports the necessary modules, express and express-session, to handle server and session management.

  2. The express.urlencoded middleware is used to parse the request body.

  3. The express-session middleware is configured with a secret key and default options to handle session management.

  4. The /login route is modified to generate a random CAPTCHA code and store it in the session. The CAPTCHA code is then included in the HTML response, along with an input field for the user to enter the CAPTCHA value.

  5. The /captcha route is added to serve the CAPTCHA image. It retrieves the CAPTCHA code from the session and includes it in the URL of the /captcha-image route.

  6. The /captcha-image route is added to generate the CAPTCHA image. This is where you would implement your secure CAPTCHA service logic. In the example code, a placeholder function generateCaptchaImage is used to generate the CAPTCHA image using a secure CAPTCHA service. The CAPTCHA code is passed as a query parameter in the URL.

  7. The /login route is modified to verify the CAPTCHA entered by the user. If the CAPTCHA matches the one stored in the session, the login logic is performed. Otherwise, an error message is displayed.

  8. The server is started on port 3000.

  9. The generateCaptchaImage function is a placeholder for your secure CAPTCHA service implementation. You would replace this function with your own logic to generate the CAPTCHA image using a secure CAPTCHA service.

By implementing a secure CAPTCHA service and ensuring that the CAPTCHA response is handled at the server level and not exposed in the HTML code, this code addresses the vulnerability of weak CAPTCHA implementation.

References