Skip to main content

Lack of protection against brute force attacks - Credentials

Need

Implementation of strong and effective protection against brute force attacks on credentials

Context

  • Usage of Dart for building cross-platform mobile, web, and desktop applications
  • Usage of Shelf for managing and organizing data in a Node.js application

Description

Non compliant code

import 'package:shelf/shelf.dart' as shelf;

class PromoCodeController {
static const List<String> validPromoCodes = ['PROMO1', 'PROMO2', 'PROMO3'];

shelf.Response checkPromoCode(shelf.Request request) {
final promoCode = request.url.queryParameters['promoCode'];
if (validPromoCodes.contains(promoCode)) {
return shelf.Response.ok('Promo code is valid!');
} else {
return shelf.Response(400, body: 'Invalid promo code.');
}
}
}

In the above code, we are using the shelf framework in Dart to handle HTTP requests. The PromoCodeController class has a method checkPromoCode which is used to check if a promo code is valid or not.

This method takes a shelf.Request object as a parameter and reads the promoCode query parameter from the request URL. It then checks if this promo code is in the list of valid promo codes. If it is, it returns a 200 OK response with a message saying that the promo code is valid. If the promo code is not in the list, it returns a 400 Bad Request response with a message saying that the promo code is invalid.

The vulnerability in this code is that it does not have any protection against brute force attacks. An attacker could potentially send a large number of requests with different promo codes in an attempt to guess a valid one. Since there is no rate limiting or other protection mechanism in place, the application could be overwhelmed by these requests, leading to a denial of service. Furthermore, if the attacker is able to guess a valid promo code, they could potentially gain unauthorized access to whatever the promo code provides.

Steps

  • Implement a rate limiting mechanism to limit the number of requests a user can make within a certain time period.
  • Add a CAPTCHA challenge to the promo code submission process to prevent automated submissions.
  • Consider using account lockouts or delays after a certain number of failed attempts to deter brute force attacks.
  • Use a more secure method for storing and validating promo codes, such as hashing them and comparing the hashes instead of the plain text codes.
  • Monitor for suspicious activity, such as a high number of requests from a single IP address, and block or limit such activity.

Compliant code

import 'package:shelf/shelf.dart' as shelf;
import 'package:shelf_rate_limiter/shelf_rate_limiter.dart';
import 'package:shelf_router/shelf_router.dart';
import 'dart:io';

class PromoCodeController {
static const List<String> validPromoCodes = ['PROMO1', 'PROMO2', 'PROMO3'];
static final Map<String, int> requestCounts = {};

shelf.Response checkPromoCode(shelf.Request request) {
final promoCode = request.url.queryParameters['promoCode'];
final ipAddress = request.headers['x-forwarded-for'] ?? request.remoteAddress.address;

if (requestCounts.containsKey(ipAddress) && requestCounts[ipAddress]! > 10) {
return shelf.Response(429, body: 'Too many requests. Please try again later.');
}

if (validPromoCodes.contains(promoCode)) {
return shelf.Response.ok('Promo code is valid!');
} else {
requestCounts.update(ipAddress, (count) => count + 1, ifAbsent: () => 1);
return shelf.Response(400, body: 'Invalid promo code.');
}
}
}

void main() {
var app = Router();

var rateLimiter = createRateLimiterMiddleware(
rateLimiter: RateLimiter(
maxOperationsPerPeriod: 10,
period: Duration(minutes: 1),
),
);

app.post('/checkPromoCode', rateLimiter(PromoCodeController().checkPromoCode));

var server = shelf_io.serve(app, InternetAddress.loopbackIPv4, 8080);
server.then((value) => print('Server started on port: ${value.port}'));
}

The updated code now includes a rate limiting mechanism to protect against brute force attacks. The shelf_rate_limiter package is used to limit the number of requests a user can make within a certain time period. In this case, the limit is set to 10 requests per minute.

The checkPromoCode method now also keeps track of the number of invalid promo code attempts from each IP address. If a user makes more than 10 invalid attempts, they will receive a 'Too many requests. Please try again later.' response.

This solution does not include a CAPTCHA challenge or account lockouts/delays, as these would require significant changes to the application and potentially the user interface. However, these are also effective measures to consider for protecting against brute force attacks.

The promo codes are still stored and compared in plain text. For a more secure solution, consider hashing the promo codes and comparing the hashes.

Finally, the application now monitors for suspicious activity by tracking the number of requests from each IP address. If a high number of requests is detected, the application could block or limit such activity. This would require additional logic and potentially a separate service or tool for monitoring and blocking IP addresses.

References