Thứ Sáu, 29 tháng 4, 2011

Kỹ thuật Slowloris - Apache HTTP DoS – Slowloris HTTP DoS

Đây là kĩ thuật tương tự như SYN flood (tạo nửa kết nối để làm cạn kiệt tài nguyên máy chủ) nhưng diễn ra ở lớp HTTP (lớp ứng dụng). Để tấn công, tin tặc gửi yêu cầu HTTP đến máy chủ, nhưng không gửi toàn bộ yêu cầu, mà chỉ gửi một phần (và bổ sung nhỏ giọt, để khỏi bị ngắt kết nối). Với hàng trăm kết nối như vậy, tin tặc chỉ tốn rất ít tài nguyên, nhưng đủ để làm treo máy chủ, không thể tiếp nhận các kết nối từ người dùng hợp lệ.


Ưu điểm khác của Slowloris là trong suốt quá trình tấn công, do kết nối chưa hoàn chỉnh, sẽ không có thông tin gì trong log. Chỉ đến khi ngưng kết nối, sẽ có hàng loạt lỗi 404 trong log do truy vấn sai.


Không phải mọi máy chủ đều bị ảnh hưởng bởi kiểu tấn công này. Các máy chủ như Microsoft IIS hoặc nginx không “hề hấn” gì trước Slowloris. Nếu dùng nginx làm proxy nghịch, tuy nhiên Apache chạy phía sau vẫn nghe trên IP công cộng (vì còn chạy một website thử nghiệm khác, không qua proxy) nên đã bị tấn công.


Có nhiều cách để khắc phục kiểu tấn công này, mỗi cách đều có ưu điểm riêng:

  • Không dùng Apache nữa! Nếu dùng Apache sau proxy nghịch, thì chỉ cho nghe trên cổng 127.0.0.1 hoặc các IP cục bộ.
  • Giảm Timeout cho Apache. Tuy nhiên cách này không mấy hiệu quả, tin tặc chỉ phải gửi thêm nhiều gói tin mà thôi.
  • Giới hạn số kết nối đến Apache cho mỗi IP. Có thể dùng mod_qos chẳng hạn để làm việc này.
  • Giải quyết ở lớp dưới: cấu hình firewall để giới hạn số kết nối đến cổng 80 trên mỗi IP.
Trích:
Yesterday an interesting HTTP DoS tool has been released. The tool performs a Denial of Service attack on Apache (and some other, see below) servers by exhausting available connections. While there are a lot of DoS tools available today, this one is particularly interesting because it holds the connection open while sending incomplete HTTP requests to the server.

In this case, the server will open the connection and wait for the complete header to be received. However, the client (the DoS tool) will not send it and will instead keep sending bogus header lines which will keep the connection allocated.
The initial part of the HTTP request is completely legitimate:

GET / HTTP/1.1\r\n
Host: host\r\n
User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; Trident/4.0; .NET CLR 1.1.4322; .NET CLR 2.0.503l3; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729; MSOffice 12)\r\n
Content-Length: 42\r\n


After sending this the client waits for certain time – notice that it is missing one CRLF to finish the header which is otherwise completely legitimate. The bogus header line the tools sends is currently:

X-a: b\r\n

Which obviously doesn’t mean anything to the server so it keeps waiting for the rest of the header to arrive. Of course, this all can be changed so if you plan to create IDS signatures keep that in mind.

According to the web site where the tool was posted, Apache 1.x and 2.x are affected as well as Squid, so the potential impact of this tool could be quite high considering that it doesn’t need to send a lot of traffic to exhaust available connections on a server (meaning, even a user on a slower line could possibly attack a fast server). Good news for Microsoft users is that IIS 6.0 or 7.0 are not affected.

At the moment I’m not sure what can be done in Apache’s configuration to prevent this attack – increasing MaxClients will just increase requirements for the attacker as well but will not protect the server completely. One of our readers, Tomasz Miklas said that he was able to prevent the attack by using a reverse proxy called Perlbal in front of an Apache server.
We’ll keep an eye on this, of course, and will post future diaries or update this one depending on what’s happening. It will be interesting to see how/if other web servers as well as load balancers are resistant to this attack. 
Xem thêm tại:
http://ha.ckers.org/blog/20090617/slowloris-http-dos/
Download code tại:
http://ha.ckers.org/slowloris/slowloris.pl

Lỗi này xảy ra cách đây cũng lâu rồi, nhưng nhiều bạn còn chưa hiểu lỗi này nên xin tái hiện lại lỗi này một tí và cũng nhấn mạnh những điểm quan trọng, vì hiện nay thấy tuy lỗi này đã được khắc phục triệt để nhưng nhiều bạn còn chưa khắc phục được, kết quả là đi làm những việc không mang lại hiệu quả hoặc thậm chí là làm tăng chi phí cho việc xây dựng server.

Tuy apache đã có cách chống triệt để, nhưng vì nhiều bạn (cũng là system admin chớ có ít gì) lại không chịu tìm hiểu vấn đề, dẫn đến kết quả là đi áp dụng những cách chống máy móc như là sử dụng 1 proxy phía trước như vanish, nginx. Việc này mang lại nhiều vấn đề rắc rối, đó là chi phí, công sức, ... trong khi đó chúng ta có thể giải quyết việc này rất dễ dàng.

Từ phiên bản apache httpd 2.2.15 có thêm module mod_reqtimeout. Module này cho phép ấn định giá trị "timeout" của request. Có hai phần chính trong directive RequestReadTimeout đó là timeout cho "body" và timeout cho "header". Đối với slowloris, giá trị timeout cho "header" chính là biện pháp chống chọi với dạng DDoS được nói ở trên. Nó tương tự với giá trị client_header_timeout của nginx.

Tuy nhiên, xét về mặt kỹ thuật thì RequestReadTimeout của apache hay hơn client_header_timeout của nginx ở chỗ nó có thêm giá trị "MinRate" đi kèm. Giá tri "MinRate" này là một phương tiện để ấn định khả năng gia giảm giá trị "timeout" dynamically. Bởi vậy, nó uyển chuyển hơn client_header_timeout của nginx.

Ví dụ:

Code:
RequestReadTimeout header=5-10,MinRate=500

Dòng ấn định trên có nghĩa nôm na là một request đi vô sẽ được httpd apache chờ đợi thông tin của request header (chỉ header chớ không bao gồm luôn body) là 5 giây. Nếu phía client gởi data cho phần header thì cứ mỗi 500 bytes mà httpd apache nhận được, nó sẽ gia tăng timeout thêm 1 giây. Nhưng nếu sau 10 giây mà không có thêm thông tin gì cho header nữa thì apache httpd sẽ huỷ connection của client đó. Tính uyển chuyển nằm ở chỗ nếu phía client có thật sự gởi data thì gia tăng giá trị "timeout" để tạo điều kiện cho client ấy tiếp tục gởi. Tuy nhiên, apache vẫn ấn định thời gian tối đa để gởi một http header là 10 giây.

Trong tình trạng bị slowloris tấn công, RequestReadTimeout giúp triệt tiêu các connections mà slowloris tạo ra trong một khoảng thời gian được ấn định cụ thể thay vì bị tràn ngập hết "MaxClients" và hoàn toàn không thể tiếp tục phục vụ.

Nói thêm một tí về lỗi trên, Apache mở socket và nhận data từ client (3-way handshakes đã hoàn tất) cho mọi request đi vào. Trong trường hợp slowloris tấn công ở đây, nó cố tình không gởi một cái CRLF để hoàn tất request header cho nên apache cứ việc chờ. Trạng thái chờ này sẽ kéo dài cho đến khi timeout (theo mặc định apache hiện chờ 300 giây - 5 phút). Quá 5 phút này, socket ấy sẽ bị hủy. Kẹt ở chỗ là trong vòng 5 phút ấy slowloris đã "nện" thêm khoảng 3000 cú (mở 3000 sockets nếu không có cái gì giới hạn ở phía apache hoặc phía trước apache). Cứ thế, cứ chồng chất lên.

Với mặc định 150 MaxClients thì mất bao lâu apache sẽ không còn phục vụ được ai nữa? Chỉ 15 giây không hơn, không kém. Giá trị TimeOut của apache (theo mặc định là 300) nhằm ấn định cho cái gì? Nó ấn định cho 3 điều chính:

1. Thời gian cần thiết để nhận trọn vẹn một cú GET hoặc
2. Thời gian giữa các gói tin TCP chuyên chở cú POST hoặc PUT (có chứa data).
3. Thời gian giữa các cú TCP ACK trong quá trình chuyển tải dữ liệu giữa client (browser) và web server (apache).

Với điểm số 1. ở trên, slowloris cố tình làm cho cú GET gởi đến apache không hoàn tất và buộc apache phải đợi. Bởi thế, nếu TimeOut ở trên rút ngắn xuống đến mức tối thiểu thì slowloris trở nên vô tác dụng. Tuy nhiên, nó cũng tạo vấn đề bởi vì nếu giá trị này quá ngắn thì một cú GET hợp lệ và vô tội cũng bị dính theo. Tệ hại hơn nữa, một cú POST (gởi bài lên một diễn đàn chẳng hạn) cũng sẽ bị chung số phận.

Cơ chế xử lý một request của apache khá chặt chẽ, nó có đến 7 phase khác nhau (unescapes URL, strips parent and elements from the URI, initial URI location walk, translate_name, Hook: map_to_storage, URI location walk, Hook: header_parser). Tuy nhiên, đến phase "parse" header của một HTTP request thì nó bị "chết" bởi slowloris vì apache hoàn toàn không có cơ chế nào kiểm tra nội dung của header cho đến khi nó nhận được đầy đủ (dựa vào CRLF character trên HTTP header). Socket do slowloris khởi tạo vẫn giữ nguyên ở đó và apache vẫn chờ cho đến khi TimeOut.

Nếu apache không dùng giá trị TimeOut chung như thế này mà gán TimeOut cụ thể cho từng loại GET, POST, HEAD.... thậm chí cụ thể hơn cho từng ấn định như LimitRequestLine, LimitRequestFieldSize.... thì TimeOut cho loại này có thể được kiểm soát chặt chẽ và hữu lý hơn. Ví dụ, nếu LimitRequestFieldSize là 2k và LimitRequestLine là 7 (chẳng hạn) thì tổng số dung lượng của cả cú GET đó không thể quá 20K (bao gồm cả URI). Cho dù một client dùng modem analog cỡ 14.4 bauds đi chăng nữa, cũng mất không quá vài giây để hoàn thành. Nếu lâu hơn giá trị TimeOut ấn định cụ thể cho loại GET này thì slowloris hoàn toàn bị khống chế. Ngay cả một cơ chế Timer dùng để đo thời gian tốn cho mỗi dòng HTTP header được đọc xuyên qua socket được ứng dụng cũng có thể vô hiệu hóa slowloris khá dễ dàng bởi lẽ chính apache đã có cơ chế đếm và giới hạn bao nhiêu dòng trên HTTP header.

Điểm yếu của apache là nó chờ cho request header hoàn tất rồi mới sanitize nội dung header. Với giá trị TimeOut được dùng chung như thế thì sẽ tạo ảnh hưởng tổng thể cho cả các request hợp lệ khác. Có lẽ phải đợi cho đến khi apache có ứng dụng tách rời các giá trị TimeOut ra thành từng mảng nhỏ cho từng trường hợp thì mới có thể khắc chế hữu hiệu trên tầng ứng dụng (application). Ngoài ra, để khắc chế dạng tấn công như slowloris phải cậy vào những cơ chế khác ở tầng thấp hơn (điều chỉnh kernel paramters, hạn chế connection trên firewall, hạn chê tầng số nhận SYN.....) thì mới chịu thấu. Trên apache, gia tăng MaxClients tối đa, gia giảm TimeOut, gia giảm KeepAliveTimeOut... có thể trợ giúp một phần. Tuy nhiên nếu chỉ đơn thuần điều chỉnh những giá trị này trên Apache mà không có gì khác chống che thì sớm muộn Apache cũng bị "denial of service" (mặc dù nó không chết).

Vậy tại sao nginx chống đỡ được slowloris (với mức độ tấn công ít dồn dập) mà apache không thể (cũng với cùng mức độ này)? Đơn giản là vì nginx có ấn định "TimeOut" chi tiết hơn và hợp lý hơn chớ không như một "TimeOut" chung của apache. Song song vào đó, nginx có cơ chế buffering data nhận từ HTTP headers của clients. Những headers nào thuộc dạng invalid (ví dụ như thiếu CRLF) thì cho đi ngay. Tuy vậy, bản thân nginx cũng không tránh khỏi nạn "denial of service" nếu như số worker_connections đã bị dùng hết. Những connection sau đó sẽ bị nginx từ chối (cũng y hệt như apache MaxClients).

Thật ra nginx cũng phải dựa trên giới hạn connection (ngx_http_limit_zone) và từ chối tiếp nhận requests từ cùng một IP nếu IP này đụng đến giới hạn connection được ấn định sẵn. Nếu slowloris được dàn ra như một thế trận bots thì cả nginx cũng sẽ chết nhanh chóng. Việc nginx từ chối IP và giới hạn connection nếu mức độ tấn công đi đến một giới hạn nào đó nếu so ra thì cũng giống y hệt như ứng dụng phòng thủ trên HVA ngay lúc này. Điều này có nghĩa chính nginx, nếu dùng như một reverse proxy hoặc một web front end, khi bị DDoS nó cũng sẽ từ chối một IP của một proxy chính như của VNPT vậy.

PS: lighthttpd chịu không thấu slowloris. Những ai có ý định dùng mod_security trên apache để chống đỡ thì nên từ bỏ ý định bởi vì ngay cả ở phase 1 của mod_security (phase xảy ra sớm nhất mà mod_security có thể can thiệp) thì cũng thuộc giai đoạn xảy sau khi HTTP headers được apache nhận đủ. slowloris tấn công dừng lại ở bước chính apache phải chờ trước khi chuyển thông tin vào để xử lý (bước này mới đụng đến không gian của mod_security).

Install Nginx


Nginx (pronounced "engine x") is a free, open-source, high-performance HTTP server. Nginx is known for its stability, rich feature set, simple configuration, and low resource consumption. This tutorial shows how you can install Nginx on an Ubuntu 8.10 server with PHP5 support (through FastCGI) and MySQL support.
I do not issue any guarantee that this will work for you!

1 Preliminary Note

In this tutorial I use the hostname server1.example.com with the IP address 192.168.0.100. These settings might differ for you, so you have to replace them where appropriate.

2 Installing MySQL 5.0

In order to install MySQL, we run
apt-get install mysql-server mysql-client
You will be asked to provide a password for the MySQL root user - this password is valid for the user root@localhost as well as root@server1.example.com, so we don't have to specify a MySQL root password manually later on:
New password for the MySQL "root" user: <-- yourrootsqlpassword
Repeat password for the MySQL "root" user: <-- yourrootsqlpassword

3 Installing Nginx

Nginx is available as a package for Ubuntu 8.10 which we can install as follows:
apt-get install nginx
Start nginx afterwards:
/etc/init.d/nginx start
Type in your web server's IP address or hostname into a browser (e.g. http://192.168.0.100), and you should see the nginx welcome page:
Click to enlarge
To make nginx start at boot time, run
update-rc.d nginx defaults

4 Installing PHP5

We can make PHP5 work in nginx through FastCGI. Fortunately, Ubuntu provides a FastCGI-enabled PHP5 package which we install like this (together with some PHP5 modules like php5-mysql which you need if you want to use MySQL from your PHP scripts):
apt-get install php5-cgi php5-mysql php5-curl php5-gd php5-idn php-pear php5-imagick php5-imap php5-mcrypt php5-memcache php5-mhash php5-ming php5-pspell php5-recode php5-snmp php5-sqlite php5-tidy php5-xmlrpc php5-xsl
Then open /etc/php5/cgi/php.ini and add the line cgi.fix_pathinfo = 1 right at the end of the file:
vi /etc/php5/cgi/php.ini
[...]
cgi.fix_pathinfo = 1
There's no standalone FastCGI daemon package for Ubuntu, therefore we use the spawn-fcgi program from lighttpd. We install lighttpd as follows:
apt-get install lighttpd
You will see an error message saying that lighttpd couldn't start because port 80 is already in use. That's how it's supposed to be because nginx is already listening on port 80. Run
update-rc.d -f lighttpd remove
so that lighttpd will not start at boot time.
We've installed lighttpd because we need just one program that comes with the package, /usr/bin/spawn-fcgi, which we can use to start FastCGI processes. Take a look at
spawn-fcgi --help
to learn more about it.
To start a PHP FastCGI daemon listening on port 9000 on localhost and running as the user and group www-data, we run the following command:
/usr/bin/spawn-fcgi -a 127.0.0.1 -p 9000 -u www-data -g www-data -f /usr/bin/php5-cgi -P /var/run/fastcgi-php.pid
Of course, you don't want to type in that command manually whenever you boot the system, so to have the system execute the command automatically at boot time, open /etc/rc.local...
vi /etc/rc.local
... and add the command at the end of the file (before the exit line):
[...]
/usr/bin/spawn-fcgi -a 127.0.0.1 -p 9000 -u www-data -g www-data -f /usr/bin/php5-cgi -P /var/run/fastcgi-php.pid
[...]

5 Configuring nginx

The nginx configuration is in /etc/nginx/nginx.conf which we open now:
vi /etc/nginx/nginx.conf
The configuration is easy to understand (you can learn more about it here: http://wiki.codemongers.com/NginxFullExample and here:http://wiki.codemongers.com/NginxFullExample2)
First (this is optional) increase the number of worker processes and set the keepalive_timeout to a reasonable value:
[...]
worker_processes  5;
[...]
    keepalive_timeout  2;
[...]
The virtual hosts are defined in server {} containers. The default vhost is defined in the file /etc/nginx/sites-available/default - let's modify it as follows:
vi /etc/nginx/sites-available/default
[...]
server {
        listen   80;
        server_name  _;

        access_log  /var/log/nginx/localhost.access.log;

        location / {
                root   /var/www/nginx-default;
                index  index.php index.html index.htm;
        }

        location /doc {
                root   /usr/share;
                autoindex on;
                allow 127.0.0.1;
                deny all;
        }

        location /images {
                root   /usr/share;
                autoindex on;
        }

        #error_page  404  /404.html;

        # redirect server error pages to the static page /50x.html
        #
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
                root   /var/www/nginx-default;
        }

        # proxy the PHP scripts to Apache listening on 127.0.0.1:80
        #
        #location ~ \.php$ {
                #proxy_pass   http://127.0.0.1;
        #}

        # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
        #
        location ~ \.php$ {
                fastcgi_pass   127.0.0.1:9000;
                fastcgi_index  index.php;
                fastcgi_param  SCRIPT_FILENAME  /var/www/nginx-default$fastcgi_script_name;
                include        fastcgi_params;
        }

        # deny access to .htaccess files, if Apache's document root
        # concurs with nginx's one
        #
        location ~ /\.ht {
                deny  all;
        }
}
[...]
server_name _; makes this a default catchall vhost (of course, you can as well specify a hostname here like www.example.com).
In the location / part, I've added index.php to the index line. root /var/www/nginx-default; means that the document root is the directory /var/www/nginx-default.
The important part for PHP is the location ~ \.php$ {} stanza. Uncomment it to enable it. Please make sure that you change the fastcgi_param line tofastcgi_param SCRIPT_FILENAME /var/www/nginx-default$fastcgi_script_name; (replace /var/www/nginx-default with your vhost's document root) because otherwise the PHP interpreter won't find the PHP script that you call in your browser.
Make sure that there are some spaces between include and fastcgi_params; - in the default file this is written as one word which is a bug.
Now save the file and restart nginx:
/etc/init.d/nginx restart
Now create the following PHP file in the document root /var/www/nginx-default:
vi /var/www/nginx-default/info.php
<?php
phpinfo();
?>
Now we call that file in a browser (e.g. http://192.168.0.100/info.php):
Click to enlarge
As you see, PHP5 is working, and it's working through FastCGI, as shown in the Server API line. If you scroll further down, you will see all modules that are already enabled in PHP5, including the MySQL module:

Thứ Ba, 26 tháng 4, 2011

Option khi build Lamp từ source

Apache2:
./configure --prefix=/usr/local/apache2 --enable-so --enable-auth-digest --enable-rewrite --enable-setenvif --enable-mime --enable-deflate
PHP5 (with APC)
./configure --prefix=/usr/local/php --enable-apc --with-apxs2=/usr/local/apache2/bin/apxs --with-mysql=/usr/local/mysql --with-pdo-mysql=/usr/local/mysql --enable-calendar --enable-soap --with-curl --with-gd --with-jpeg-dir=/usr/lib --with-png-dir=/usr/lib --with-zlib-dir=/usr/lib --with-freetype-dir=/usr/include/freetype2/ --with-freetype

1 vài chú ý:
--enable-apc-mmap: enable apc ( opcode cache )


Trường hợp lỗi do thiếu lib liên quan đến GD:  cp /usr/lib64/libpng.a or .so /usr/lib/

Thứ Bảy, 23 tháng 4, 2011

Doctrine use raw SQL

public function executeZzz(sfWebRequest $request) {

$conn = Doctrine_Manager::connection();
$sql = "SHOW TABLES";
$pdo = $conn->execute($sql);
$pdo->setFetchMode(Doctrine_Core::FETCH_ASSOC);
$this->result = $pdo->fetchAll();
    }

Thứ Sáu, 22 tháng 4, 2011

How to install Apache, PHP and MySQL on Linux: Part 3


PHP 5 installation

We will set up PHP as a shared module, being loaded into Apache2 dynamically during the server startup. These instructions are known to work for PHP versions: 5.0.4 through 5.3.3 .

Prerequisites

At this point Apache web server must be installed. If you want MySQL support in PHP, MySQL server also must have been installed prior to the next steps.

Download source

Get the source from http://www.php.net/downloads.php . At the time of writing this tutorial the best available version was 5.3.3 ( php-5.3.3.tar.gz ).

Unpack, configure, compile

Go to the directory with the downloaded file and enter:
1tar -xzf php-5.3.3.tar.gz
2cd php-5.3.3
3./configure --prefix=/usr/local/php --with-apxs2=/usr/local/apache2/bin/apxs --with-mysql=/usr/local/mysql
The configuration options ought to be self-explaining; –prefix specifies the location where PHP is to be installed, –with-apxs2 with correct path pointing to bin/apxs in the Apache installation directory is mandatory for the installator to work. Since PHP 5, you need to explicitly bundle PHP with MySQL by–with-mysql directive (make sure you specified path to where MySQL is installed on your system).
There are many other options which turn on additional features. For all available configuration options and their default values type ./configure –help.
Tip: If you are performing an upgrade, you may want to copy config.nice from the old PHP installation directory (if available) to where you unpacked the new PHP tarball file. Run ./config.nice instead of./configure. This way all the previous configure options will be applied to the new installation effortlessly.
Once you entered ./configure with all the options you need, compile and install the software:
1make
2make install

Edit httpd.conf

All necessary changes to httpd.conf (Apache configuration file) should have already been made automatically during the installation, so usually you need not do anything. Anyways, check that following lines were added to your httpd.conf file:
1LoadModule php5_module modules/libphp5.so
2AddType application/x-httpd-php .php
If not, add them manually.

Create php.ini file

Importanly, you have to create php.ini configuration file. Choose one of the pre-made files (preferablyphp.ini-recommended) residing inside the php-5.3.3/ directory (it’s the folder to which the downloaded archive was extracted). Copy the file to the lib/ directory in the PHP installation directory.
1cp php-5.3.3/php.ini-recommended /usr/local/php/lib/php.ini
If you need to, edit the php.ini file:
1vi /usr/local/php/lib/php.ini
However, the default settings should work for everyone in most cases.

Restart apache server

After everything is set up, restart Apache:
1/usr/local/bin/apachectl stop
2/usr/local/bin/apachectl start
Alternatively, simply enter:
1/usr/local/bin/apachectl restart