web-dev-qa-db-ja.com

100,000以上の個々のIPアドレスをブロックする方法

はじめに

Webアプリケーション/サーバーから多数のIP addressをブロックするにはどうすればよいですか。明らかに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
} 

またはhtaccessを使用

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

問題

  • subnetsではなく100k plus individual IPs全体をブロックしようとしています
  • このようなIPをブロックする前に、ユーザーがPHP
  • 100000+は1.5MBを超えており、常に情報をhtaccessにロードする場合は大量です
  • IPのデータベースはまだ成長しています...そして、彼らは動的により多くの値を追加する必要があります
  • iptablesで100000以上の禁止を設定するのはばかげています(間違っているかもしれません)

愚かなアイデア

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

質問

  • htaccessがデータベース(Redis、Crunchbase、Mongo、MySQL、またはSqlite)からリストを取得することは可能ですか?
  • 本番環境でこの種の問題を管理する目に見える解決策はありますか
  • 最善の解決策はBlock the IPs at the firewall levelであることを知っています。ファイアウォールにIPを実用的に追加/削除する方法はありますか

最終的に

私のアプローチは完全に間違っているかもしれません...スパマーとボットネットが増えているので、私が望むのは目に見えるソリューションだけです...

これはDOS攻撃とは何の関係もありません。単純な... get lost response

更新

  • ファイアウォール:Cisco PIX 515UR
66
Baba

試すことができるのは、ブロックするIPアドレスのリストを テキストファイル に保持するか、または dbmハッシュファイル に変換してから、mod_rewriteのRewriteMap。サーバー/ vhost構成でこれを設定する必要があります。 htaccessファイルでマップを初期化することはできません

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

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

/ path/to/deny_ips.txtファイルは次のようになります。

12.34.56.78 1
11.22.33.44 1
etc.

基本的に、拒否するIPとスペース、次に「1」。このテキストファイルにIPがあると、サーバーは403 Forbiddenを返します。少し速度を上げるには、httxt2dbm dbmハッシュを生成し、マッピングを次のように定義します。

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

多数のIPでこのようなmod_rewriteを使用した場合のパフォーマンスヒットはわかりませんが、Linuxで3Ghz i686を実行しているApache 2.2の簡単なベンチマークテストでは、リスト内の5つのIPと102418の違いは無視できます。 abの出力によると、それらはほとんど同じです。


特定の質問への対応:

Htaccessがデータベース(Redis、Crunchbase、Mongo、MySQL、またはSqlite)からリストを取得することは可能ですか?

書き換えマップを使用すると、「 prg 」マップタイプを使用して、マッピングタイプの外部プログラムを実行できます。次に、IPアドレスを検索するためにデータベースと通信するPerl、phpなどのスクリプトを作成できます。また、「注意」にリストされている警告にも注意してください。その後、他のマップと同じようにこのマップを使用します(RewriteCond ${deny_ips:%{REMOTE_ADDR}|0} !=0)。これは、本質的にすべてのリクエストのボトルネックを作成します。データベースと通信するのに最適なソリューションではありません。

ただし、Apache 2.4には dbd/fastdbd マップタイプがあり、 mod_dbd を使用してクエリを作成できます。これははるかに優れたオプションであり、mod_dbdモジュールはデータベースへの接続、プール接続などを管理します。したがって、マップ定義は次のようになります。

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

2つの列を持つテーブル(deny_ips)があると仮定します(source) (IPアドレス)および「active」(アクティブの場合は1、非アクティブの場合は0)。

本番環境でこの種の問題を管理する目に見える解決策はありますか

ブロックされたすべてのIPをデータベースに保存している場合、データベーステーブルの内容を管理する必要があります。 dbmマップタイプを使用している場合、少なくとも PerlにはDBIがあります を知っているので、それを使用して拒否リストからIPエントリを追加/削除できます。私はそれを使用したことがないので、それについてあまり語ることはできません。特にテキストファイルに追加するだけでなく、エントリを削除する場合は、フラットテキストファイルの管理が非常に複雑になります。データベースとApache 2.4のmod_dbdを使用する以外は、これらのソリューションのいずれもすぐに使用できる、または本番環境で使用できるとは思いません。カスタム作業が必要になります。

最善の解決策は、ファイアウォールレベルでIPをブロックすることです。実際にIPアドレスをファイアウォールに追加/削除する方法はありますか

IPtablesには、 Perlインターフェース がありますが、これはベータ版としてマークされていますが、これまで使用したことがありません。 libiptc がありますが、 netfilterのfaq によると:

ルールを追加/削除するためのC/C++ APIはありますか?

残念ながら、答えは「いいえ」です。

今、あなたは「でもlibiptcについてはどうだろう」と思うかもしれません。メーリングリストで何度も指摘されているように、libiptcは[〜#〜] never [〜#〜]がパブリックインターフェイスとして使用されることを意図していました。安定したインターフェイスを保証するものではなく、Linuxパケットフィルタリングの次のインカネーションで削除する予定です。 libiptcは、とにかく合理的に使用するには低すぎる層です。

このようなAPIには根本的な不足があることを十分に認識しており、その状況の改善に取り組んでいます。それまでは、system()を使用するか、iptables-restoreのstdinへのパイプを開くことをお勧めします。後者を使用すると、パフォーマンスが向上します。

したがって、APIの安定性がない場合、libiptcソリューションがどの程度実行可能かはわかりません。

64
Jon Lin

別の観点

こんにちは。各8KBの長さの2つのデータチャンクの2バイトにアクセスすることにより、アドレスがブロックされているかどうかを確認できます。はい、私は真剣です...それを説明するのに少し時間がかかるので、辛抱してください。

理論

IPアドレスはアドレスであり、実際には4バイトの数字です。

問題は、ビット位置に対応するためにどうしたらよいかということです。

答え:まあまあ、我々は持っています

  2^32 = 4 Giga Bits 

アドレス空間の

 4Gb/8 = 512 Mega Bytes

割り当ての。 痛い!しかし、心配しないで、ipverseのすべてをブロックするつもりはありません。512MBは誇張です。

これにより、ソリューションへの道が開かれます。

リリプティアンの場合

0〜65535のIPアドレスのみが存在するリリプティアンの世界を考えてください。したがって、アドレスは0.1または42.42〜255.255のようになります。

現在、この世界の王はいくつかのL-IP(リリパットIP)アドレスをブロックしたいと考えています。

最初に、彼は256 * 256ビット長の仮想2Dビットマップを作成します。

 64 K Bits = 8 K Bytes.

彼は王であるため嫌いな「革命」サイトをブロックすることにしました。たとえば、住所は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)

彼はマップファイルを開き、1795番目のバイトにアクセスし、ビット4を(| 16で)設定し、それを書き戻してサイトをブロック済みとしてマークします。

彼のスクリプトが56.28を検出すると、同じ計算を実行してビットを調べ、設定されている場合はアドレスをブロックします。

今、物語の教訓は何ですか?さて、このリリプティアン構造を使用できます。

練習

実世界の場合

512MBのファイルを割り当てることは良い選択ではないため、「必要なときに使用する」アプローチを使用して、現実世界にLilliputianのケースを適用できます。

次のようなエントリを持つBLOCKSという名前のデータベーステーブルを考えてみましょう。

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

BASEという名前の以下の構造を持つエントリが1つだけある別のテーブル

Map        : 8KB BLOB(fixed size).

次に、着信アドレス56.28.10.2があるとしましょう。

スクリプトはBASEテーブルにアクセスし、マップを取得します。

高次 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)

マップのバイト1795ビット4を調べます。

ビットが設定されていない場合、56.28.0.0から56.28.255.255の範囲にブロックされたIPアドレスがないことを意味する追加の操作は不要です。

ビットが設定されている場合、スクリプトはBLOCKSテーブルにアクセスします。

高位IP番号は56.28であり、14364を与えるため、スクリプトはインデックスIpHead = 14364でBLOCKSテーブルを照会します。レコードをフェッチします。 BASEでマークされているため、レコードが存在する必要があります。

スクリプトは下位 IPアドレスの計算を行います

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

次に、フィールドマップのバイト320のビット2を見て、アドレスがブロックされているかどうかを確認します。

仕事完了!

Q1:なぜBASEを使用するのか、14364でBLOCKSを直接クエリできます。

A1:はい、できますが、BASEマップルックアップは、データベースサーバーのBTREE検索よりも高速です。

Q2: BLOCKSテーブルのEntryCountフィールドは何のためですか?

A2:これは、同じレコードのマップフィールドでブロックされたIPアドレスの数です。したがって、IPのブロックを解除し、EntryCountが0に達すると、そのBLOCKSレコードは不要になります。消去することができ、BASEマップ上の対応するビットは設定解除されます。

私見このアプローチは非常に高速です。また、BLOB割り当ての場合、レコードごとに8Kです。 dbサーバーは別のファイルにblobを保持するため、4K、8K、または4Kの倍数のページングを持つファイルシステムは高速に反応します。

ブロックされたアドレスが分散している場合

それは懸念事項です。これにより、データベースのBLOCKSテーブルが不必要に大きくなります。

しかし、このような場合の代替策は、長さ16777216ビットで、2097152バイト= 2MBに等しい256 * 256 * 256ビットのキューブを使用することです。

前の例のHigher Ip解決は次のとおりです。

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

したがって、BASEはdbテーブルレコードではなく2MBファイルになり、開かれ(fopenなど)、ビットはシークによってアドレス指定されます(fseek、neverファイル全体の内容を読み取る、不要)以下の構造でBLOCKSテーブルにアクセスします。

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. 

以下は、bitplane-bitplane(8K 8K)バージョンのブロックチェックのphpサンプルコードです。

サイドノート:このスクリプトは、いくつかの呼び出しなどを排除することでさらに最適化できます。しかし、わかりやすくするためにこのように書かれています。

<?
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);
}

?> 

これがお役に立てば幸いです。

詳細について質問がある場合は、お気軽にお答えします。

32
Ihsan

これは、PHPではなく、外部ファイアウォールで行う必要があります。 pfSense または [〜#〜] pf [〜#〜] をお勧めします。以前に使用したことがありますが、非常に使いやすく、非常に直感的で、非常に強力です。最高のシステム管理者の選択です。私はFreeBSDで実行していますが、OpenBSDでもうまく動作します。私はLinuxの男なので、これを言うのは苦痛ですが、しないでくださいLinux上でそれを実行しようとします。 BSDは簡単で、すぐに理解できます。

PfSenseのすばらしい機能は、スクリプトを使用して構成し、単一のネットワークインターフェイスへの構成アクセスを制限する機能です(したがって、LAN上のもののみが構成できます)。また、偶発的に自分のアクセスを遮断しないようにするためのID10Tレベルの機能がいくつかあります。

また、多くのスパマーは Tor のようなものを使用してIPをすばやく切り替えることができることに注意する必要があります。これを修正するには、ブロックリストに、出口ノードに既知のアドレスを含める必要があります(このリストはさまざまな場所から入手できます)。

8
Freedom_Ben

Iptablesとipsetを使用して、wwwサーバーに到達する前にトラフィックをブロックします。

Webサーバーが同じマシン上にあると想定して、INPUTチェーンのフィルターテーブルでブラックリストに登録されたIPトラフィックをキャッチします。ルーターでIPをブロックしている場合、FORWARDチェーンが必要になります。

最初にipsetを作成します。

ipset create ip_blacklist hash:ip

IPは次の方法で追加できます。

ipset add ip_blacklist xxx.xxx.xxx.xxx

Ipset一致ルールをiptablesに追加します(ipsetに一致するすべてのパケットを削除します)。

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

これにより、wwwサーバーの前にブラックリストに登録されたトラフィックが停止します。

編集:デフォルトの最大サイズを調べる機会がありましたが、それは65536ですので、これを調整して100000以上のエントリをサポートする必要があります:

ipset create ip_blacklist hash:ip maxelem 120000

ハッシュサイズを調整することもできます。

ipset create ip_blacklist hash:ip maxelem 120000 hashsize 16384(2のべき乗でなければなりません)

私の経験では、ipsetルックアップはシステムにほとんど影響しません(最大45000エントリ)。ネットには多くのテストケースがあります。セットのメモリは制限要因です。

7
Donald Chisholm

コードを使用して追加/削除する方法が必要な場合は、 denyhosts をご覧ください。コードを使用してIPリストを維持するか、ソースにパッチを適用して、任意の場所から読み取ることができます。

5
ethrbunny

Ipsetという名前のnetfilterを使用したプロジェクトがあるため、リストにipを追加または削除できます。このリストに対してルールを作成するだけです。

http://ipset.netfilter.org/

4
atrepp

IPをブロックしている場合は、ファイアウォールレベルでこれを実行する必要があります(望ましくないIPアドレスのユーザーがシステムに侵入するのは望ましくありません)。したがって、データベースを照会し、それに応じてファイアウォール構成ファイルを変更するbashスクリプトを作成することをお勧めします(これは、Webデータベースに保存されているIPアドレスを利用するソリューションが必要であることを前提としています-そのような情報を保存するより良い場所があるかもしれません)。

編集:IPアドレスをPHPレベルでブラックリストに追加したい場合、@ Populusが示唆したように、PHPでシステムコールを使用する方法に関するマニュアルは次のとおりです。 http: //php.net/manual/en/function.system.php

また、iptablesを使用している場合にブラックリストにIPアドレスを追加するために使用する必要があるコマンドは次のとおりです。 http://www.cyberciti.biz/faq/linux-iptables-drop/

3
2to1mux

方法を知っている
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
} 

あなたが書いたものを読みます。ちょっと待ってください。
これを行う理由は重要ではありませんが、なぜこのような状況であるのか。
なぜ速度が速いのか、またはすべてのページで関数を呼び出すのが非常に難しいために、phpへのアクセスを避けたいのですか?一部のIPがphpにアクセスできないようにする唯一の理由が、コンテンツを表示するためのテーマの回避です。
だから私はアイデアを得て、このように提案します。
1つのエントリポイントを使用します。

私はこのソリューションを使用しました。

まず、単純なhtaccessを使用して、すべてのリクエストをエントリポイントと呼ばれる1つのページに送信します(index.phpなど)。
単純な書き換えルールを使用して、それを提供します。ユーザーがリクエストしたとき

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

htaccessは、URLを変更せずに次のようなものを実行します。そのため、ユーザーは何も感じません。

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

そのため、すべてのリクエストは異なる$ _GET ['r']パラメータを持つ1つのリクエストになりました。したがって、リクエストごとにindex.phpが実行されます。そして今、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'];

とてもシンプルで、実際のものはとても複雑ですが、主なアイデアは同じです。どう思いますか?

3
ncm

私たちのほとんどはファイアウォールレベルでブロックに同意しているようです。

あなたのウェブサイトをリッスンして、IPをブロックし、スクリプトを生成するプログラムがあります:

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

スクリプトは次のようになります([an]は英数字の値、[ip]はブロックするIPです):

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]

次に、このスクリプトを、FW CLIへのリモートtelnetまたはssh呼び出しを実行する別のプログラムにロードします。

ログアウトすることを忘れないでください。また、おそらく100 ipsごとに実行設定をコピーして設定を開始してください。

わかりませんが、ファイアウォールの制限について知りたいと思うかもしれません。

ベスト、

2
Yannis

私見、この質問を考慮することができるいくつかの角度があります

  1. ブロックする一意のIPアドレスの非常に大きなセットがあります。サーバーでブロックするのが早ければ早いほど、無駄になる処理能力は少なくなります。 PHPからの適切なシステムコールを介して、ファイアウォールでIPを追加/削除できます。

上記のオプションを考慮すると、関連する質問は次のとおりです。

  • 頻繁に訪問しますか? =>そうであれば、解決策(1)が最善の策です。
  • ファイアウォールで効率的に処理できますか? =>そうでない場合は、他の解決策を検討してください。

.htaccessは私の2番目の選択肢です。

0
Jean

リスト内のIPをジオルックアップします。私自身の経験では、ほとんどの悪意のある(つまり、スパム)接続が中国から発信されていることが示されています。同じことがあなたに当てはまり、中国に仕える必要性が特にない場合は、ファイアウォールレベルで国全体を効率的にブロックできるかどうかを確認してください。

0
Jason