网站应用防火墙开发
我有一个任务,要开发一个网络应用防火墙。我一直在寻找相关的源代码,主要参考的是ModSecurity。
我主要想问的是:
- 我可以用哪个框架或编程语言来开发网络应用防火墙?哪个最有用呢?
- 我可以使用Django和Python吗?
这将是我项目研究的起点。
5 个回答
Django是一个用于开发网页应用的框架。我没看到有人用它来写防火墙的实现。
你需要做的事情是,首先使用Linux,这比Windows更容易操作。你有两个选择,第一个是通过钩住系统调用来实现NetFilter驱动(这个比较复杂!),第二个是使用libnetfilter_queue库和iptables把数据包转移到用户空间的应用程序。主要的想法是对数据包的内容进行深入分析,除了检查IP和TCP头部外,还要关注数据的具体内容,这和入侵检测系统(IDS)和入侵防御系统(IPS)很相似,但它更专注于网络应用的安全漏洞。
我觉得你用Python可能很难做到这一点,因为这需要更深入的系统干预。你问的问题比较棘手,涉及到系统的底层。
你可以用这个例子开始分析数据:
配置iptables
#: iptables -A INPUT -j NFQUEUE --queue-balance 0:3
#: iptables -A OUTPUT -j NFQUEUE --queue-balance 4:8
队列0到3是用于所有输入的(最好把它们分成不同的队列,因为每个队列能容纳的数据包是有限的),其他的则是用于所有输出。
用C语言编写应用程序(iptables会把数据包从内核转移到用户空间)
filterQueue.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <netinet/in.h>
#include <linux/types.h>
#include <string.h>
/* for ethernet header */
#include<net/ethernet.h>
/* for UDP header */
#include<linux/udp.h>
/* for TCP header */
#include<linux/tcp.h>
/* for IP header */
#include<linux/ip.h>
/* -20 (maximum priority) */
#include <sys/time.h>
#include <sys/resource.h>
/* for NF_ACCEPT */
#include <linux/netfilter.h>
/* for Threads */
#include <pthread.h>
/* for Queue */
#include <libnetfilter_queue/libnetfilter_queue.h>
#define NUM_THREADS 15
pthread_t threads[NUM_THREADS];
void printTCP(unsigned char *buffer) {
unsigned short iphdrlen;
struct iphdr *iph = (struct iphdr *) (buffer + sizeof(struct ethhdr));
iphdrlen = iph->ihl * 4;
struct tcphdr *tcph = (struct tcphdr *) (buffer + iphdrlen
+ sizeof(struct ethhdr));
int header_size = sizeof(struct ethhdr) + iphdrlen + tcph->doff * 4;
printf("| Packet Type: TCP \n");
printf("|-Source Port : %u\n", ntohs(tcph->source));
printf("|-Destination Port : %u\n", ntohs(tcph->dest));
printf("|-Sequence Number : %u\n", ntohl(tcph->seq));
printf("|-Acknowledge Number : %u\n", ntohl(tcph->ack_seq));
printf("|-Header Length : %d DWORDS or %d BYTES\n",
(unsigned int) tcph->doff, (unsigned int) tcph->doff * 4);
printf("|-CWR Flag : %d\n", (unsigned int) tcph->cwr);
printf("|-ECN Flag : %d\n", (unsigned int) tcph->ece);
printf("|-Urgent Flag : %d\n", (unsigned int) tcph->urg);
printf("|-Acknowledgement Flag : %d\n", (unsigned int) tcph->ack);
printf("|-Push Flag : %d\n", (unsigned int) tcph->psh);
printf("|-Reset Flag : %d\n", (unsigned int) tcph->rst);
printf("|-Synchronise Flag : %d\n", (unsigned int) tcph->syn);
printf("|-Finish Flag : %d\n", (unsigned int) tcph->fin);
printf("|-Window : %d\n", ntohs(tcph->window));
printf("|-Checksum : %d\n", ntohs(tcph->check));
printf("|-Urgent Pointer : %d\n", tcph->urg_ptr);
}
void printUDP(unsigned char *buffer) {
unsigned short iphdrlen;
struct iphdr *iph = (struct iphdr *) (buffer + sizeof(struct ethhdr));
iphdrlen = iph->ihl * 4;
struct udphdr *udph = (struct udphdr*) (buffer + iphdrlen
+ sizeof(struct ethhdr));
int header_size = sizeof(struct ethhdr) + iphdrlen + sizeof udph;
printf("| Packet Type: UDP \n");
printf("|-Source Port : %u\n", ntohs(udph->source));
printf("|-Destination Port : %u\n", ntohs(udph->dest));
printf("|-UDP Length : %u\n", ntohs(udph->len));
printf("|-UDP Checksum : %u\n", ntohs(udph->check));
}
char * getText(unsigned char * data, char Size) {
char * text = malloc(Size);
int i = 0;
for (i = 0; i < Size; i++) {
if (data[i] >= 32 && data[i] <= 128)
text[i] = (unsigned char) data[i];
else
text[i] = '.';
}
return text;
}
u_int32_t analyzePacket(struct nfq_data *tb, int *blockFlag) {
//packet id in the queue
int id = 0;
//the queue header
struct nfqnl_msg_packet_hdr *ph;
//the packet
char *data;
//packet size
int ret;
//extracting the queue header
ph = nfq_get_msg_packet_hdr(tb);
//getting the id of the packet in the queue
if (ph)
id = ntohl(ph->packet_id);
//getting the length and the payload of the packet
ret = nfq_get_payload(tb, &data);
if (ret >= 0) {
printf("Packet Received: %d \n", ret);
/* extracting the ipheader from packet */
struct sockaddr_in source, dest;
unsigned short iphdrlen;
struct iphdr *iph = ((struct iphdr *) data);
iphdrlen = iph->ihl * 4;
memset(&source, 0, sizeof(source));
source.sin_addr.s_addr = iph->saddr;
memset(&dest, 0, sizeof(dest));
dest.sin_addr.s_addr = iph->daddr;
printf("|-Source IP: %s\n", inet_ntoa(source.sin_addr));
printf("|-Destination IP: %s\n", inet_ntoa(dest.sin_addr));
printf("|-Checking for Protocol: \n");
if (iph->protocol == 6) {
printTCP(data);
} else if (iph->protocol == 17) {
printUDP(data);
}
printf("|-Extracting Payload: \n");
char * text = getText(data, ret);
//filtering requests for facebook
if (text && text[0] != '\0') {
printf("\n %s \n", text);
ret = strstr(text, "facebook");
if (ret == 0)
//not found in string
*blockFlag = 0;
else
//found in string
*blockFlag = 1;
}
//release the packet
free(text);
}
//return the queue id
return id;
}
int packetHandler(struct nfq_q_handle *qh, struct nfgenmsg *nfmsg, struct nfq_data *nfa,
void *data) {
printf("entering callback \n");
//when to drop
int blockFlag = 0;
//analyze the packet and return the packet id in the queue
u_int32_t id = analyzePacket(nfa, &blockFlag);
//this is the point where we decide the destiny of the packet
if (blockFlag == 0)
return nfq_set_verdict(qh, id, NF_ACCEPT, 0, NULL);
else
return nfq_set_verdict(qh, id, NF_DROP, 0, NULL);
}
void *QueueThread(void *threadid) {
//thread id
long tid;
tid = (long) threadid;
struct nfq_handle *h;
struct nfq_q_handle *qh;
char buf[128000] __attribute__ ((aligned));
//pointers and descriptors
int fd;
int rv;
int ql;
printf("open handle to the netfilter_queue - > Thread: %d \n", tid);
h = nfq_open();
if (!h) {
fprintf(stderr, "cannot open nfq_open()\n");
return NULL;
}
//unbinding previous procfs
if (nfq_unbind_pf(h, AF_INET) < 0) {
fprintf(stderr, "error during nfq_unbind_pf()\n");
return NULL;
}
//binding the netlink procfs
if (nfq_bind_pf(h, AF_INET) < 0) {
fprintf(stderr, "error during nfq_bind_pf()\n");
return NULL;
}
//connet the thread for specific socket
printf("binding this socket to queue '%d'\n", tid);
qh = nfq_create_queue(h, tid, &packetHandler, NULL);
if (!qh) {
fprintf(stderr, "error during nfq_create_queue()\n");
return NULL;
}
//set queue length before start dropping packages
ql = nfq_set_queue_maxlen(qh, 100000);
//set the queue for copy mode
if (nfq_set_mode(qh, NFQNL_COPY_PACKET, 0xffff) < 0) {
fprintf(stderr, "can't set packet_copy mode\n");
return NULL;
}
//getting the file descriptor
fd = nfq_fd(h);
while ((rv = recv(fd, buf, sizeof(buf), 0)) && rv >= 0) {
printf("pkt received in Thread: %d \n", tid);
nfq_handle_packet(h, buf, rv);
}
printf("unbinding from queue Thread: %d \n", tid);
nfq_destroy_queue(qh);
printf("closing library handle\n");
nfq_close(h);
return NULL;
}
int main(int argc, char *argv[]) {
//set process priority
setpriority(PRIO_PROCESS, 0, -20);
int rc;
long balancerSocket;
for (balancerSocket = 0; balancerSocket < NUM_THREADS; balancerSocket++) {
printf("In main: creating thread %ld\n", balancerSocket);
//send the balancer socket for the queue
rc = pthread_create(&threads[balancerSocket], NULL, QueueThread,
(void *) balancerSocket);
if (rc) {
printf("ERROR; return code from pthread_create() is %d\n", rc);
exit(-1);
}
}
while (1) {
sleep(10);
}
//destroy all threads
pthread_exit(NULL);
}
好的,我的猜测基本上是对的,虽然我原本以为是在保护一个没有安全性或者安全性差的应用,但其实更多的是在防止攻击。在这种情况下,Django确实不太合适。用Python来做这件事是完全可以的,但别指望能处理每秒10万次请求。:) 不过如果是做研究和开发,我觉得Python是个不错的选择,因为开发速度快,而且像Cython这样的工具也能让运行速度变得相当快。如果最后你要做一个需要极高性能的成品,你可以把算法转成C/C++来实现。
在你的情况下,我建议看看Twisted。这可能是个合适的解决方案。
“它将在服务器端用于通过HTTP控制用户交易。”
大多数网页应用框架都有安全设置。通常这些设置不叫“防火墙”,而且你没有回答我的问题,所以我在这里猜测一下:
你是在写一个网络代理,用来过滤掉那些没有正确权限的请求,因为有一个应用根本没有任何访问控制。这样理解对吗?
没错,你可以用Python来做到这一点。Django可能不是最合适的选择。如果你需要实现登录页面和用户管理的访问控制,那么你可能需要用到SQL和模板,轻量级的Python框架会很有帮助。否则,Twisted或者直接用标准库里的功能可能是更好的解决方案。