Web应用防火墙开发

Posted

技术标签:

【中文标题】Web应用防火墙开发【英文标题】:web application firewall development 【发布时间】:2011-05-28 00:51:56 【问题描述】:

我的任务是开发 Web 应用程序防火墙。我一直在研究这方面的一些源代码。我的主要来源是 ModSecurity。

主要问题是:

-我可以使用哪种框架或编程语言来开发 Web 应用程序防火墙?哪个最有用?

-我可以使用 Django 和 Python 吗?

这将是项目研究的起点。

【问题讨论】:

什么是“网络应用防火墙”?我知道什么是 Web 应用程序和防火墙,但组合起来毫无意义。 想想你想阻止/允许什么。这就是防火墙的主要作用! 我还需要 Web 应用程序防火墙的代码。有人在 google 中找到任何链接吗? 不,我不知道@Novikov。我研究了很多资源,但没有一个明确的答案。有很多资源,但没有清楚地解释我必须用哪种语言编写代码。 @Lennart 等人:现在有许多 WAF 供应商——Imperva、Barracuda、F5 等。它基本上是对 HTTP/HTTPS 流量的深度数据包检查,以进一步识别合法与恶意交通。防火墙和 IDS 之间的交叉,但针对 Web 威胁。 【参考方案1】:

Django 是一个 Web 应用程序框架。我没有看到有人使用它编写防火墙实现。

【讨论】:

它不是客户端防火墙应用程序。它将在服务器端用于通过 HTTP 控制用户事务。 ModSecurity 有一个名为 CRS(Core Rule Set)的规则集。但我无法准确理解它们是用哪种语言编写的。谢谢你的链接。 我刚刚在 SO 中注册。我没有足够的声誉来降级答案。我认为其他人已经做到了。很抱歉。 无法再访问链接,甚至 archive.org 也没有该链接的历史【参考方案2】:

好的,所以我的猜测基本上是正确的,虽然我认为它是在保护一个没有安全性或安全性很差的应用程序,但它更多的是为了防止攻击。在那种情况下,Django 肯定是错误的。这在 Python 中显然是可行的,但不要期望每秒能够处理 100.000 个请求。 :) 但是如果是研究和开发,我认为 Python 可能是一个不错的选择,因为它的开发速度很快,并且使用 Cython 之类的工具运行起来也非常快。如果您最终制作出确实需要极高性能的成品,您可以采用算法并将其转换为 C/C++。

我会在你的情况下调查 Twisted。这可能是正确的解决方案。


"它将用于服务器端通过 HTTP 控制用户事务。"

大多数 Web 应用程序框架都有安全设置。这些通常不被称为“防火墙”,你没有回答我的问题,所以我在这里猜测:

您正在编写一个 Web 代理,它将过滤掉没有正确权限的请求,因为有一个应用程序根本没有任何访问控制。这是正确的吗?

是的,你可以在 Python 中做到这一点。 Django 可能不是正确的解决方案。如果您需要使用登录页面和用户管理来实现访问控制,那么您可能需要 SQL 和模板以及轻量级 Python 框架可能会有所帮助。否则 Twisted 或仅使用标准库中的功能可能是正确的解决方案。

【讨论】:

为什么投反对票?我的猜测并没有那么遥远,而且答案非常正确。 我将尝试通过Mod Security的例子来解释它。 - 它是一个 Apache 模块,其目的是加强 Web 应用程序的安全性。 -可嵌入Web服务器使用,是一种入侵检测与防御系统。 - 基本上,它分析传入的请求并过滤它们。 - 它可以用于对抗 URL 操作、后端身份验证、会话劫持、XSS 攻击、SQL 注入、缓冲区溢出等。 - 它有一种称为“虚拟修补”的方法,可以控制我猜想的请求。顺便一提;我如何开发它,用哪种语言。抱歉,我没有降级。 我刚刚在 SO 中注册。我没有足够的声誉来降级答案。我认为其他人已经做到了。很抱歉。 goo.gl/HBD0Z 我在这里找到了一个为 Apache 服务器编写模块的教程。看来我可以用 C 编写代码。我想问:“是否可以在 Python 中编写 Apache 模块?使用 Python 是否有效?” 这只是一个学术研究开发,之后我会寻找Cython以获得更好的性能。正如你所建议的;我将看看 Twisted。非常感谢。 :)【参考方案3】:

除非这只是某种学术练习并且 Python 可以帮助您快速完成它,否则我不认为像 Python 这样的高级语言是防火墙的最佳选择(老实说,我什至不知道这是否可能)。如果您正在计划某种代理/过滤器应用程序,那可能没问题,但无论如何都不需要 Django。

【讨论】:

可以做Apache服务器的模块吗?如何为 Apache Server 编写模块? apache 网站上有关于如何为 Apache 制作模块的文档。不知道能不能用 Python 写出来。 感谢您的回答。我刚刚找到了一些关于它的有用文档。看起来像;可以用 Python 为 Apache 编写模块。【参考方案4】:

这是你需要做的,开始使用 Linux,比 Windows 更容易处理,你有两个选择,第一个是通过挂钩系统调用来实现 NetFilter 驱动程序(更复杂!),第二个是使用 libnetfilter_queue 库和 iptables 将数据包传输到用户空间应用程序,主要思想是除了检查 IP 和 TCP 标头之外对有效负载进行深入分析,非常类似于 IDS、IPS 系统,但它专注于 Web 应用程序的安全漏洞。

我不认为你可以在没有更深层次的干扰的情况下用 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);

【讨论】:

【参考方案5】:

C、C++、Golang、Lua 都是开发 Web 应用防火墙或网关的可选语言,但 django 并不适合。 C、C++可以开发nginx插件或者后端WAF。 Golang 可以使用 WAF 开发网关,例如 Janusec Application Gateway。 Lua 可以扩展 nginx 访问控制并作为 WAF 工作。

【讨论】:

以上是关于Web应用防火墙开发的主要内容,如果未能解决你的问题,请参考以下文章

如何使用pFuzz以多种方法验证Web应用程序防火墙的安全性

WAF Web应用防火墙

WEB应用防火墙的功能描述

Web应用防火墙-网站安全防护

什么是web 应用防火墙

什么是web应用防火墙?