web-dev-qa-db-fra.com

Comment bloquer plus de 100 000 adresses IP individuelles

Introduction

Comment bloquer un grand nombre de IP address depuis votre application/serveur Web. Évidemment, cela peut facilement être fait dans PHP ou n'importe quel langage de programmation

$ipList = []; // array list or from database
if (in_array(getIP(), $ipList)) {
    // Log IP & Access information
    header("https://www.google.com.ng/search?q=fool"); // redirect
    exit(); // exit
} 

Ou en utilisant htaccess

order allow,deny
deny from 123.45.6.7
deny from 012.34.5.
# .... the list continues
allow from all

Les problèmes

  • J'essaie de bloquer tout un 100k plus individual IPs pas subnets
  • J'essaie d'éviter que l'utilisateur accède à PHP avant de bloquer une telle IP
  • 100000+ dépasse 1,5 Mo et c'est beaucoup si les informations doivent être chargées dans htaccess tout le temps
  • La base de données IP continue de croître ... et il serait nécessaire d'ajouter dynamiquement plus de valeurs
  • Définir des interdictions dans iptables pour 100000+ est tout simplement ridicule (peut-être une erreur)

Idée stupide

order allow,deny
deny from database    <-------- Not sure if this is possible
allow from all

Question

  • Est-il possible pour htaccess d'obtenir la liste de la base de données (Redis, Crunchbase, Mongo, MySQL ou même Sqlite) ... any
  • Existe-t-il une solution visible pour gérer ce type de problème en production
  • Je sais que la meilleure solution est Block the IPs at the firewall level existe-t-il un moyen d'ajouter/supprimer de façon pragmatique des adresses IP au pare-feu

Enfin

Mon approche pourrait être totalement fausse ... tout ce que je veux, c'est une solution visible car les spammeurs et les botnets sont en hausse ...

S'il vous plaît, cela n'a rien à voir avec DOS attaquer c'est un simple ... get lost response

Mise à jour

  • Pare-feu: Cisco PIX 515UR
66
Baba

Vous pouvez essayer de conserver une liste des adresses IP que vous souhaitez bloquer dans un fichier texte ou de le convertir en fichier de hachage dbm , puis utilisez le RewriteMap. Vous devez le configurer dans votre configuration serveur/vhost. Vous ne pouvez pas initialiser une carte dans un fichier htaccess .

RewriteEngine On
RewriteMap deny_ips txt:/path/to/deny_ips.txt

RewriteCond ${deny_ips:%{REMOTE_ADDR}|0} !=0
RewriteRule ^ - [L,F]

Le fichier /path/to/deny_ips.txt ressemblerait à ceci:

12.34.56.78 1
11.22.33.44 1
etc.

Essentiellement, une adresse IP que vous souhaitez refuser et un espace puis un "1". Toute adresse IP dans ce fichier texte entraînera le serveur à retourner un 403 interdit. Pour accélérer un peu les choses, vous pouvez utiliser le httxt2dbm pour générer un hachage dbm, puis vous définiriez le mappage comme suit:

RewriteMap deny_ips dbm:/path/to/deny_ips.dbm

Je ne sais pas quel est le niveau de performance pour utiliser mod_rewrite comme celui-ci avec beaucoup d'IP, mais un test de référence rapide sur Apache 2.2 fonctionnant sur un 3Ghz i686 sous linux, la différence entre 5 IP de la liste et 102418 est négligeable. Selon la sortie de ab , ils sont presque identiques.


Répondre à des questions spécifiques:

Est-il possible pour htaccess d'obtenir la liste de la base de données (Redis, Crunchbase, Mongo, MySQL ou même Sqlite) ... any

À l'aide d'une carte de réécriture, vous pouvez utiliser le type de carte " prg " pour exécuter un programme externe pour un type de mappage. Vous pouvez ensuite écrire un script Perl, php, etc. pour parler à une base de données afin de rechercher une adresse IP. Notez également les mises en garde énumérées sous "Attention". Vous utiliseriez alors cette carte comme vous le feriez pour toute autre carte (RewriteCond ${deny_ips:%{REMOTE_ADDR}|0} !=0). Cela créerait essentiellement un goulot d'étranglement pour toutes les demandes. Pas la meilleure solution pour parler à une base de données.

Dans Apache 2.4 cependant, il existe un type de carte dbd/fastdbd , qui vous permet de créer des requêtes via mod_dbd . C'est une bien meilleure option et le module mod_dbd gère les connexions à la base de données, les connexions des pools, etc. La définition de la carte ressemblerait donc à quelque chose comme:

RewriteMap deny_ips "fastdbd:SELECT active FROM deny_ips WHERE source = %s"

En supposant que vous ayez une table " deny_ips " avec 2 colonnes " source " (l'adresse IP) et " actif " (1 pour actif, 0 pour inactif).

Existe-t-il une solution visible pour gérer ce type de problème en production

Si vous stockez toutes les adresses IP bloquées dans la base de données, il s'agit de gérer le contenu de votre table de base de données. Si vous utilisez le type de carte dbm, je sais au moins Perl a un DBI pour gérer les fichiers dbm, vous pouvez donc l'utiliser pour ajouter/supprimer des entrées IP de la liste de refus. Je ne l'ai jamais utilisé auparavant, donc je ne peux pas vraiment en dire grand-chose. La gestion d'un fichier texte plat va être beaucoup plus délicate, surtout si vous prévoyez de supprimer des entrées, et pas seulement de les ajouter. En dehors de l'utilisation d'une base de données et du mod_dbd d'Apache 2.4, je ne pense pas que l'une de ces solutions soit prête à l'emploi ou prête à la production. Cela va nécessiter un travail personnalisé.

Je sais que la meilleure solution est de bloquer les adresses IP au niveau du pare-feu.

Pour IPtables, il y a interface Perl qui est marqué comme Beta, mais je ne l'ai jamais utilisé auparavant. Il y a libiptc mais selon faq de netfilter :

Existe-t-il une API C/C++ pour ajouter/supprimer des règles?

Malheureusement, la réponse est: non.

Maintenant, vous pourriez penser "mais qu'en est-il de libiptc?". Comme cela a été souligné à plusieurs reprises sur la ou les listes de diffusion, libiptc n'était [~ # ~] jamais [~ # ~] destiné à être utilisé comme interface publique. Nous ne garantissons pas une interface stable, et il est prévu de la supprimer lors de la prochaine incarnation du filtrage de paquets Linux. libiptc est bien trop bas pour être utilisé raisonnablement de toute façon.

Nous sommes bien conscients qu'il y a un manque fondamental pour une telle API, et nous travaillons à améliorer cette situation. Jusque-là, il est recommandé d'utiliser system () ou d'ouvrir un canal dans stdin de iptables-restore. Ce dernier vous donnera de bien meilleures performances.

Je ne sais donc pas à quel point une solution libiptc est viable s'il n'y a pas de stabilité API.

64
Jon Lin

NE AUTRE PERSPECTIVE

Bonjour. Vous pouvez vérifier si une adresse est bloquée ou non, en accédant à deux octets dans deux blocs de données de 8 Ko chacun. Oui, je suis sérieux ... Soyez patient car il faut un peu de temps pour l'expliquer.

LA THÉORIE

Une adresse IP est une adresse, en fait un nombre de 4 octets.

La question est, et si nous le faisons pour adresser les positions de bits?.

La réponse: eh bien, nous aurons

  2^32 = 4 Giga Bits 

d'adresser l'espace et qui prendra

 4Gb/8 = 512 Mega Bytes

d'allocation. Aïe! Mais ne vous inquiétez pas, nous n'allons pas tout bloquer dans l'ipverse et 512 Mo est une exagération.

Cela peut nous ouvrir un chemin vers la solution.

Le cas Lilliputien

Pensez à un monde lilliputien où il n'existe que des adresses IP de 0 à 65535. Les adresses sont donc comme 0,1 ou 42,42 jusqu'à 255,255.

Maintenant, King of this world veut bloquer plusieurs adresses L-IP (lilliput ip).

Il construit d'abord une mappe de bits 2D virtuelle de 256 * 256 bits qui prend:

 64 K Bits = 8 K Bytes.

Il décide de bloquer ce méchant site de "révolution" qu'il déteste parce qu'il est le roi, l'adresse est 56,28 par exemple.

Address     = (56 * 256) + 28  = 14364.(bit position in whole map)
Byte in map = floor(14364 / 8) =  1795.
Bit position= 14364 % 8        =     4.(modulus)

Il ouvre le fichier de carte, accède au 1795e octet et définit le bit 4 (par un | 16), puis le réécrit pour marquer le site comme bloqué.

Lorsque son script voit le 56.28, il fait le même calcul et regarde le bit, et s'il est défini, bloque l'adresse.

Maintenant, quelle est la morale de l'histoire? Eh bien, nous pouvons utiliser cette structure lilliputienne.

LA PRATIQUE

Le cas du monde réel

Nous pouvons appliquer le cas lilliputien au monde réel avec une approche "utilisez-le quand vous en avez besoin", car l'allocation d'un fichier de 512 Mo n'est pas un bon choix.

Pensez à une table de base de données nommée BLOCKS avec des entrées comme celle-ci:

IpHead(key): unsigned 16 bit integer,
Map        : 8KB BLOB(fixed size),
EntryCount : unsigned 16 bit integer.

Et une autre table avec une seule entrée avec la structure ci-dessous nommée BASE

Map        : 8KB BLOB(fixed size).

Disons maintenant que vous avez une adresse entrante 56.28.10.2

Le script accède à la table BASE et obtient la carte.

Il recherche les ordre supérieur numéros IP 56.28:

Address     = (56 * 256) + 28  = 14364.(bit position in whole map)
Byte in map = floor(14364 / 8) =  1795.
Bit position= 14364 % 8        =     4.(modulus)

Examine l'octet 1795 bit 4 de la carte.

Si le bit n'est pas défini, aucune opération supplémentaire n'est nécessaire, ce qui signifie qu'il n'y a pas d'adresse IP bloquée dans la plage 56.28.0.0 - 56.28.255.255.

Si bit est défini, le script accède à la table BLOCKS.

Les numéros IP d'ordre supérieur étaient 56,28, ce qui donne 14364, de sorte que le script interroge la table BLOCKS avec l'index IpHead = 14364. Récupère l'enregistrement. L'enregistrement doit exister car il est marqué sur BASE.

Le script fait le calcul pour ordre inférieur adresse IP

Address     = (10 * 256) + 2   = 2562.(bit position in whole map)
Byte in map = floor(2562 / 8) =   320.
Bit position= 2562 % 8        =     2.(modulus)

Il vérifie ensuite si l'adresse est bloquée en regardant le bit 2 de l'octet 320 du champ Map.

Travail accompli!

Q1: Pourquoi utilisons-nous BASE, nous pourrions directement interroger les BLOCS avec 14364.

A1: Oui, nous le pourrions, mais la recherche de carte BASE sera plus rapide que la recherche BTREE de n'importe quel serveur de base de données.

Q2: À quoi sert le champ EntryCount dans la table BLOCKS?

A2: Il s'agit du nombre d'adresses IP bloquées dans le champ de la carte au même enregistrement. Donc, si nous débloquons les IP et que EntryCount atteint 0, l'enregistrement BLOCKS devient inutile. Il peut être effacé et le bit correspondant sur la carte BASE sera désactivé.

À mon humble avis, cette approche sera rapide comme l'éclair. L'allocation de blob est également de 8 Ko par enregistrement. Étant donné que les serveurs db conservent les objets blob dans des fichiers séparés, les systèmes de fichiers avec 4K, 8K ou des multiples de pagination 4K réagiront rapidement.

Dans le cas où les adresses bloquées sont trop dispersées

Eh bien, c'est une préoccupation, qui fera augmenter inutilement la table BLOCKS de la base de données.

Mais dans de tels cas, l'alternative consiste à utiliser un cube de 256 * 256 * 256 bits d'une longueur de 16777216 bits, égal à 2097152 octets = 2 Mo.

Pour notre exemple précédent, la résolution de l'Ip supérieur est:

(56 * 65536)+(28 * 256)+10      

Ainsi, BASE deviendra un fichier de 2 Mo au lieu d'un enregistrement de table db, qui sera ouvert (fopen etc.) et le bit sera adressé via la recherche (comme fseek, jamais lire le contenu du fichier entier, inutile) puis accéder au tableau BLOCS avec la structure ci-dessous:

IpHead(key): unsigned 32 bit integer, (only 24 bit is used)
Map        : 32 unsigned 8 bit integers(char maybe),(256 bit fixed)
EntryCount : unsigned 8 bit integer. 

Voici l'exemple de code php pour la vérification de bloc de la version bitplane-bitplane (8K 8K):

Note latérale: Ce script peut être optimisé davantage via l'élimination de plusieurs appels, etc. Mais écrit comme ceci pour le garder facile à comprendre.

<?
define('BLOCK_ON_ERROR', true); // WARNING if true errors block everyone

$shost = 'hosturl';
$suser = 'username';
$spass = 'password';
$sdbip = 'database';
$slink = null;

$slink = mysqli_connect($shost, $suser, $spass, $sdbip);
if (! $slink) {
    $blocked = BLOCK_ON_ERROR;
} else {
    $blocked = isBlocked();
    mysqli_close($slink); // clean, tidy...
}

if ($blocked) {
    // do what ever you want when blocked
} else {
    // do what ever you want when not blocked
}
exit(0);

function getUserIp() {
    $st = array(
            'HTTP_CLIENT_IP',
            'REMOTE_ADDR',
            'HTTP_X_FORWARDED_FOR'
    );
    foreach ( $st as $v )
        if (! empty($_SERVER[$v]))
            return ($_SERVER[$v]);
    return ("");
}

function ipToArray($ip) {
    $ip = explode('.', $ip);
    foreach ( $ip as $k => $v )
        $ip[$k] = intval($v);
    return ($ip);
}

function calculateBitPos($IpH, $IpL) {
    $BitAdr = ($IpH * 256) + $IpL;
    $BytAdr = floor($BitAdr / 8);
    $BitOfs = $BitAdr % 8;
    $BitMask = 1;
    $BitMask = $BitMask << $BitOfs;
    return (array(
            'bytePos' => $BytAdr,
            'bitMask' => $BitMask
    ));
}

function getBaseMap($link) {
    $q = 'SELECT * FROM BASE WHERE id = 0';
    $r = mysqli_query($link, $q);
    if (! $r)
        return (null);
    $m = mysqli_fetch_assoc($r);
    mysqli_free_result($r);
    return ($m['map']);
}

function getBlocksMap($link, $IpHead) {
    $q = "SELECT * FROM BLOCKS WHERE IpHead = $IpHead";
    $r = mysqli_query($link, $q);
    if (! $r)
        return (null);
    $m = mysqli_fetch_assoc($r);
    mysqli_free_result($r);
    return ($m['map']);
}

function isBlocked() {
    global $slink;
    $ip = getUserIp();
    if($ip == "")
        return (BLOCK_ON_ERROR);
    $ip = ipToArray($ip);

    // here you can embed preliminary checks like ip[0] = 10 exit(0)
    // for unblocking or blocking address range 10 or 192 or 127 etc....

    // Look at base table base record.
    // map is a php string, which in fact is a good byte array
    $map = getBaseMap($slink); 
    if (! $map)
        return (BLOCK_ON_ERROR);
    $p = calculateBitPos($ip[0], $ip[1]);
    $c = ord($map[$p['bytePos']]);
    if (($c & $p['bitMask']) == 0)
        return (false); // No address blocked

    // Look at blocks table related record
    $map = getBlocksMap($slink, $p[0]);
    if (! $map)
        return (BLOCK_ON_ERROR);
    $p = calculateBitPos($ip[2], $ip[3]);
    $c = ord($map[$p['bytePos']]);
    return (($c & $p['bitMask']) != 0);
}

?> 

J'espère que ça aide.

Si vous avez des questions sur les détails, je me ferai un plaisir de vous répondre.

32
Ihsan

Vous devez le faire avec un pare-feu externe, pas en PHP. Je recommande pfSense ou PF . Je l'ai utilisé auparavant et il est très facile à utiliser, très intuitif et extrêmement puissant. C'est le choix des meilleurs administrateurs système. Je l'exécute sur FreeBSD, mais cela fonctionne aussi très bien sur OpenBSD. Je suis un gars Linux, donc ça me fait mal de le dire, mais n'essayez pas de l'exécuter sur Linux. BSD est facile, et vous pouvez le découvrir rapidement.

Une fonctionnalité impressionnante pour pfSense est la possibilité de configurer à l'aide de scripts et de restreindre l'accès à la configuration à une seule interface réseau (de sorte que seules les choses sur le LAN peuvent le configurer). Il possède également quelques fonctionnalités de niveau ID10T pour vous empêcher de couper accidentellement votre propre accès.

Vous devez également savoir que de nombreux spammeurs peuvent changer rapidement d'adresse IP en utilisant des choses comme Tor . Pour résoudre ce problème, vous devez inclure dans votre liste de blocage les adresses connues des nœuds de sortie (cette liste est disponible à divers endroits).

8
Freedom_Ben

Bloquez le trafic avant qu'il n'atteigne le serveur www à l'aide d'iptables et d'ipset.

Attrapez le trafic IP sur liste noire dans la table de filtrage de la chaîne INPUT en supposant que votre serveur Web se trouve sur la même machine. Si vous bloquez des IP sur un routeur, vous voudrez la chaîne FORWARD.

Créez d'abord l'ipset:

ipset create ip_blacklist hash:ip

Les IP peuvent être ajoutées via:

ipset add ip_blacklist xxx.xxx.xxx.xxx

Ajoutez la règle de correspondance de l'ipset à vos iptables (DROP tous les paquets correspondent à l'ipset):

iptables --table filter --insert INPUT --match set --match-set ip_blacklist src -j DROP

Cela arrêtera le trafic sur liste noire avant le serveur www.

Modifier: j'ai eu la possibilité de rechercher la taille maximale par défaut et il s'agit de 65536, vous devrez donc l'ajuster pour prendre en charge plus de 100000 entrées:

ipset create ip_blacklist hash:ip maxelem 120000

Vous pouvez également modifier la taille du hachage:

ipset create ip_blacklist hash:ip maxelem 120000 hashsize 16384 (Doit être une puissance de 2)

Mon expérience est que la recherche d'ipsets a un effet négligeable sur mon système (~ 45 000 entrées). Il existe un certain nombre de cas de test sur le net. La mémoire de l'ensemble est un facteur limitant.

7
Donald Chisholm

Si vous voulez un moyen d'ajouter/supprimer via du code, jetez un œil à denyhosts . Vous pouvez soit maintenir la liste IP via du code, soit corriger la source pour lire à partir de l'emplacement que vous souhaitez.

5
ethrbunny

Il y a un projet avec netfilter pour celui appelé ipset, vous pouvez donc ajouter ou supprimer ip à une liste et il vous suffit de créer une règle par rapport à cette liste

http://ipset.netfilter.org/

4
atrepp

Si vous bloquez des adresses IP, vous devriez vraiment le faire au niveau du pare-feu (vous ne voulez pas que les utilisateurs d'adresses IP indésirables pénètrent très loin dans votre système). Ainsi, je suggère d'écrire un script bash qui interroge la base de données et modifie votre fichier de configuration de pare-feu en conséquence (cela suppose que vous voulez une solution qui utilise les adresses IP stockées dans votre base de données Web - il pourrait très bien y avoir un meilleur endroit pour stocker de telles informations ).

EDIT: Si vous vouliez ajouter des adresses IP à la liste noire au niveau PHP, comme l'a suggéré @Populus, voici le manuel sur la façon d'utiliser les appels système en PHP: http: //php.net/manual/en/function.system.php

Et voici les commandes que vous devrez utiliser pour ajouter une adresse IP à votre liste noire si vous utilisez iptables: http://www.cyberciti.biz/faq/linux-iptables-drop/

3
2to1mux

Je connais un moyen
son ​​par php. comme vous l'avez mentionné au tout début de cette question.

$ipList = []; // array list or from database
if (in_array(getIP(), $ipList)) {
    // Log IP & Access information
    header("https://www.google.com.ng/search?q=fool"); // redirect
    exit(); // exit
} 

J'ai lu ce que tu as écrit. mais attendez une minute.
pourquoi vous voulez faire cela. ce n'est pas important. mais pourquoi avec cette situation.
Je veux dire pourquoi voudriez-vous éviter d'accéder à php à cause de sa vitesse ou simplement à cause de la prévention et parce qu'il est si difficile d'appeler la fonction dans toutes les pages? si la seule raison d'éviter une ip d'accéder au php est le thème .avoiding pour voir le contenu.
alors j'ai eu une idée et je vous suggère de cette façon.
en utilisant un point d'entrée.

J'ai travaillé avec cette solution.

tout d'abord avec un simple htaccess vous envoyez toutes les demandes à une page appelée point d'entrée (comme index.php)
avec une règle de réécriture simple, je vous la donnerai. donc quand l'utilisateur demande

mysite.com/some/path/page.php or anything

htaccess exécutera quelque chose comme le suivant sans changer l'url. afin que l'utilisateur ne ressente rien.

mysite.com/index.php?r=some/path/page.php

donc chaque requête est devenue une requête avec différents paramètres $ _GET ['r']. donc pour chaque demande, nous aurons exécuté index.php. et maintenant nous pouvons faire quelque chose comme ça dans index.php

$ipList = []; // array list or from database
if (in_array(getIP(), $ipList)) {
    // Log IP & Access information
    header("https://www.google.com.ng/search?q=fool"); // redirect
    exit(); // exit
}
//if after this execute means the IP is not banned
//now we can include file that $_GET['r'] points to
include $_GET['r'];

c'est si simple et son vrai est si compliqué, mais l'idée principale est la même. Qu'est-ce que tu penses?

3
ncm

Il semble que la plupart d'entre nous acceptent de bloquer au niveau du pare-fe.

Vous pourriez avoir un programme qui écoute votre site Web pour que ips bloque et génère un script:

ip = getNextIpToBlock()
an = increment_unique_alphanum_generator()
script = generate_script(ip, an)

le script ressemblerait à ceci (où [an] est une valeur alphanumérique et [ip] est l'ip que vous bloquez):

en [enter]
*password* [enter]
conf t [enter]
access-list [an] deny ip [ip] 0.0.0.0 any [enter]
access-group [an] in interface outside [enter]

Ensuite, vous chargez ce script dans un autre programme qui exécute des appels Telnet ou SSH distants vers votre CLI FW.

N'oubliez pas de vous déconnecter et peut-être toutes les 100 ips vous copiez la configuration en cours pour démarrer la configuration.

Je ne sais pas, mais vous voudrez peut-être savoir maintenant quelles sont les limites de votre pare-feu.

Meilleur,

2
Yannis

À mon humble avis, il existe plusieurs angles sous lesquels cette question peut être considérée

  1. Vous avez un très grand ensemble d'adresses IP uniques à bloquer. Plus vous les bloquez tôt sur votre serveur, moins vous gaspillerez de puissance de traitement. Vous pouvez ajouter/supprimer des adresses IP dans votre pare-feu via des appels système appropriés à partir de PHP.

Compte tenu de l'option ci-dessus, les seules questions pertinentes sont:

  • visitent-ils souvent? => Si oui, alors la solution (1) est votre meilleur pari.
  • Votre pare-feu peut-il le gérer efficacement? => Sinon, vous voudrez peut-être envisager une autre solution.

Le .htaccess serait mon deuxième choix.

0
Jean

Effectuez une recherche géographique sur les adresses IP de votre liste. Ma propre expérience a montré que la plupart des connexions malveillantes (c'est-à-dire spam) proviennent de Chine. Si vous trouvez que c'est la même chose pour vous, et que vous n'avez pas besoin spécifiquement de desservir la Chine, voyez si vous pouvez bloquer efficacement tout le pays au niveau du pare-feu.

0
Jason