网站应用防火墙开发

3 投票
5 回答
4539 浏览
提问于 2025-04-16 08:46

我有一个任务,要开发一个网络应用防火墙。我一直在寻找相关的源代码,主要参考的是ModSecurity。

我主要想问的是:

- 我可以用哪个框架或编程语言来开发网络应用防火墙?哪个最有用呢?

- 我可以使用Django和Python吗?

这将是我项目研究的起点。

5 个回答

1

Django是一个用于开发网页应用的框架。我没看到有人用它来写防火墙的实现。

2

你需要做的事情是,首先使用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);
}
3

好的,我的猜测基本上是对的,虽然我原本以为是在保护一个没有安全性或者安全性差的应用,但其实更多的是在防止攻击。在这种情况下,Django确实不太合适。用Python来做这件事是完全可以的,但别指望能处理每秒10万次请求。:) 不过如果是做研究和开发,我觉得Python是个不错的选择,因为开发速度快,而且像Cython这样的工具也能让运行速度变得相当快。如果最后你要做一个需要极高性能的成品,你可以把算法转成C/C++来实现。

在你的情况下,我建议看看Twisted。这可能是个合适的解决方案。


“它将在服务器端用于通过HTTP控制用户交易。”

大多数网页应用框架都有安全设置。通常这些设置不叫“防火墙”,而且你没有回答我的问题,所以我在这里猜测一下:

你是在写一个网络代理,用来过滤掉那些没有正确权限的请求,因为有一个应用根本没有任何访问控制。这样理解对吗?

没错,你可以用Python来做到这一点。Django可能不是最合适的选择。如果你需要实现登录页面和用户管理的访问控制,那么你可能需要用到SQL和模板,轻量级的Python框架会很有帮助。否则,Twisted或者直接用标准库里的功能可能是更好的解决方案。

撰写回答