Weak CAPTCHA - Typescript
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
Insecure Code Example
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.
Secure Code Example
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
Last updated
2023/09/18