开源版WEB应用防火墙-WAF

一、了解WAF

1.1、WAF概念

Web应用防护系统(也称:网站应用级入侵防御系统 。英文:Web Application Firewall,简称: WAF)。Web应用防火墙是通过执行一系列针对HTTP/HTTPS的安全策略来专门为Web应用提供保护的一款产品。

1.2、WAF的功能

支持IP白名单和黑名单功能,直接将黑名单的IP访问拒绝。

支持URL白名单,将不需要过滤的URL进行定义。

支持User-Agent的过滤,匹配自定义规则中的条目,然后进行处理(返回警告页面)。

支持CC攻击防护,单个URL指定时间的访问次数,超过设定值,直接返回警告页面。

支持Cookie过滤,匹配自定义规则中的条目,然后进行处理(返回警告页面)。

支持URL过滤,匹配自定义规则中的条目,如果用户请求的URL包含这些,返回警告页面。

支持URL参数过滤,原理同上。

支持日志记录,将所有拒绝的操作,记录到日志中去

1.3、WAF的特点

异常检测协议 

Web应用防火墙会对HTTP的请求进行异常检测,拒绝不符合HTTP标准的请求。也可以只允许HTTP协议的部分选项通过,从而减少攻击的影响范围。一些Web应用防火墙还可以严格限定HTTP协议中那些过于松散或未被完全制定的选项。 

增强的输入验证 

增强输入验证,可以有效防止网页篡改、信息泄露、木马植入等恶意网络入侵行为。从而减小Web服务器被攻击的可能性。

及时补丁 

修补Web安全漏洞,是Web应用开发者最头痛的问题,没人会知道下一秒有什么样的漏洞出现,会为Web应用带来什么样的危害。WAF可以为我们做这项工作了——只要有全面的漏洞信息WAF能在不到一个小时的时间内屏蔽掉这个漏洞。当然,这种屏蔽掉漏洞的方式不是非常完美的,并且没有安装对应的补丁本身就是一种安全威胁,但我们在没有选择的情况下,任何保护措施都比没有保护措施更好。

基于规则的保护和基于异常的保护 

基于规则的保护可以提供各种Web应用的安全规则,WAF生产商会维护这个规则库,并时时为其更新。用户可以按照这些规则对应用进行全方面检测。还有的产品可以基于合法应用数据建立模型,并以此为依据判断应用数据的异常。但这需要对用户企业的应用具有十分透彻的了解才可能做到,可现实中这是十分困难的一件事情。

状态管理 

WAF能够判断用户是否是第一次访问并且将请求重定向到默认登录页面并且记录事件。通过检测用户的整个操作行为我们可以更容易识别攻击。状态管理模式还能检测出异常事件(比如登陆失败),并且在达到极限值时进行处理。这对暴力攻击的识别和响应是十分有利的。

其他防护技术 

WAF还有一些安全增强的功能,可以用来解决WEB程序员过分信任输入数据带来的问题。比如:隐藏表单域保护、抗入侵规避技术、响应监视和信息泄露保护。 

二、深入实现WAF

2.1、WAF实现规划

分析步骤如下:解析HTTP请求==》匹配规则==》防御动作==》记录日志 

具体实现如下:

解析http请求:协议解析模块

匹配规则:规则检测模块,匹配规则库

防御动作:跳转到自定义界面

日志记录:记录到elk中,画出饼图,建议使用json格式  

2.2、安装nginx+lua

 由于nginx配置文件书写不方便,并且实现白名单功能很复杂,nginx的白名单也不适用于CC攻击,所以在这里使用nginx+lua来实现WAF,如果想使用lua,须在编译nginx的时候配置上lua,或者结合OpenResty使用,此方法不需要编译nginx时候指定lua

2.2.1、编译nginx的时候加载lua

环境准备(因此文档为几年前编写,软件测试版本可使用最新版):Nginx安装必备的Nginx和PCRE软件包。 

wget http://nginx.org/download/nginx-1.9.4.tar.gz
wget ftp://ftp.csx.cam.ac.uk/pub/software/programming/pcre/pcre-8.37.tar.gz
wget http://luajit.org/download/LuaJIT-2.0..tar.gz
wget https://github.com/simpl/ngx_devel_kit/archive/v0.2.19.tar.gz
wget https://github.com/openresty/lua-nginx-module/archive/v0.9.16.tar.gz

最后,创建Nginx运行的普通用户

 useradd -s /sbin/nologin -M www

解压NDK和lua-nginx-module

tar zxvf v0.2.19.tar.gz

tar zxvf v0.9.16.tar.gz

安装LuaJIT Luajit是Lua即时编译器

tar zxvf LuaJIT-2.0.3.tar.gz 
cd LuaJIT-2.0.3
[root@openstack-compute-node5 LuaJIT-2.0.3]# make && make install

安装Nginx并加载模块

​
tar zxvf nginx-1.9.4.tar.gz 
cd nginx-1.9.4
export LUAJIT_LIB=/usr/local/lib
export LUAJIT_INC=/usr/local/include/luajit-2.0
./configure --prefix=/usr/local/nginx --user=www --group=www     --with-http_ssl_module --with-http_stub_status_module --with-file-aio --with-http_dav_module --add-module=../ngx_devel_kit-0.2.19/ --add-module=../lua-nginx-module-0.9.16/ --with-pcre=/usr/local/src/pcre-8.37 
[root@openstack-compute-node5 nginx-1.5.12]# make -j2 && make install
 ln -s /usr/local/lib/libluajit-5.1.so.2 /lib64/libluajit-5.1.so.2   #一定创建此软连接,否则报错

​

安装完毕后,下面可以测试安装了,修改nginx.conf 增加第一个配置

location /hello {

                default_type 'text/plain';

                content_by_lua 'ngx.say("hello,lua")';

        }

 /usr/local/nginx-1.9.4/sbin/nginx –t

 /usr/local/nginx-1.9.4/sbin/nginx

2.2.2 Openresty部署

安装依赖包

yum install -y readline-devel pcre-devel openssl-devel

下载并编译安装openresty

​yum-config-manager --add-repo https://openresty.org/yum/cn/centos/OpenResty.repo
yum install openresty

测试并启动nginx

/usr/local/openresty/nginx/sbin/nginx -t
nginx: the configuration file /usr/local/openresty/nginx/conf/nginx.conf syntax is ok
nginx: configuration file /usr/local/openresty/nginx/conf/nginx.conf test is successful
/usr/local/openresty/nginx/sbin/nginx

2.2.3 WAF部署

git clone GitHub - unixhot/waf: 使用Nginx+Lua实现的WAF(版本v1.0)

cp -a ./waf/waf /usr/local/openresty/nginx/conf/

修改Nginx的配置文件,加入(http字段)以下配置。注意路径,同时WAF日志默认存放在/tmp/日期_waf.log

#WAF

    lua_shared_dict limit 50m;  #防cc使用字典,大小50M

    lua_package_path "/usr/local/openresty/nginx/conf/waf/?.lua";

    init_by_lua_file "/usr/local/openresty/nginx/conf/waf/init.lua";

    access_by_lua_file "/usr/local/openresty/nginx/conf/waf/access.lua";

 /usr/local/openresty/nginx/sbin/nginx –t
 /usr/local/openresty/nginx/sbin/nginx

根据日志记录位置,创建日志目录

 tail -f 2017-04-24_waf.log 

{"user_agent":"Mozilla\/5.0 (Macintosh; Intel Mac OS X 10_12_4) AppleWebKit\/537.36 (KHTML, like Gecko) Chrome\/57.0.2987.133 Safari\/537.36","rule_tag":"\\.(bak|inc|old|mdb|sql|backup|java|class|tgz|gz|tar|zip)$","req_url":"\/dvwa\/setup.php.bak","client_ip":"10.20.66.79","local_time":"2017-04-24 14:03:14","attack_method":"Deny_URL","req_data":"-","server_name":"localhost"}

{"user_agent":"Mozilla\/5.0 (Macintosh; Intel Mac OS X 10_12_4) AppleWebKit\/537.36 (KHTML, like Gecko) Chrome\/57.0.2987.133 Safari\/537.36","rule_tag":"\\.(bak|inc|old|mdb|sql|backup|java|class|tgz|gz|tar|zip)$","req_url":"\/dvwa\/setup.php.bak","client_ip":"10.20.66.79","local_time":"2017-04-24 14:03:29","attack_method":"Deny_URL","req_data":"-","server_name":"localhost"}

2.3学习模块

2.3.1学习配置模块

WAF上生产之前,建议不要直接上生产,而是先记录日志,不做任何动作。确定WAF不产生误杀 

config.lua即WAF功能详解

cat config.lua

 --WAF config file,enable = "on",disable = "off" 
 --waf status    
 config_waf_enable = "on"   #是否开启配置
 --log dir 
config_log_dir = "/var/log/nginx/hack"    #日志记录地址
 --rule setting 
 config_rule_dir = "/usr/local/nginx/conf/waf/rule-config"       #匹配规则缩放地址
 --enable/disable white url 
 config_white_url_check = "on"  #是否开启url检测
 --enable/disable white ip 
 config_white_ip_check = "on"   #是否开启IP白名单检测
 --enable/disable block ip 
 config_black_ip_check = "on"   #是否开启ip黑名单检测
 --enable/disable url filtering 
 config_url_check = "on"      #是否开启url过滤
 --enalbe/disable url args filtering 
 config_url_args_check = "on"   #是否开启参数检测
 --enable/disable user agent filtering 
 config_user_agent_check = "on"  #是否开启ua检测
 --enable/disable cookie deny filtering 
 config_cookie_check = "on"    #是否开启cookie检测
 --enable/disable cc filtering 
 config_cc_check = "on"   #是否开启防cc攻击
 --cc rate the xxx of xxx seconds 
 config_cc_rate = "10/60"   #允许一个ip60秒内只能访问10此
 --enable/disable post filtering 
 config_post_check = "on"   #是否开启post检测
 --config waf output redirect/html 
 config_waf_output = "html"  #action一个html页面,也可以选择跳转
 --if config_waf_output ,setting url 
 config_waf_redirect_url = "http://www.baidu.com" 
 config_output_html=[[  #下面是html的内容
<html><head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>网站防火墙</title>
<style>
p {
        line-height:20px;
}
ul{ list-style-type:none;}
li{ list-style-type:none;}
</style>
</head>
<body style=" padding:0; margin:0; font:14px/1.5 Microsoft Yahei, 宋体,sans-serif; color:#555;">
 <div style="margin: 0 auto; width:1000px; padding-top:70px; overflow:hidden;">
  
  <div style="width:600px; float:left;">
    <div style=" height:40px; line-height:40px; color:#fff; font-size:16px; overflow:hidden; background:#6bb3f6; padding-left:20px;">网站防火墙 </div>
    <div style="border:1px dashed #cdcece; border-top:none; font-size:14px; background:#fff; color:#555; line-height:24px; height:220px; padding:20px 20px 0 20px; overflow-y:auto;background:#f3f7
f9;">
      <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600; color:#fc4f03;">您的请求带有不合法参数
,已被网站管理员设置拦截!</span></p>
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">可能原因:您提交的内容包含危险的攻击请求</p>
<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:1; text-indent:0px;">如何解决:</p>
<ul style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-bl
ock-indent:0; text-indent:0px;">1)检查提交内容;</li>
<li style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">2)如网站托管,请联系空间提供商;</li>
<li style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">3)普通网站访客,请联系网站管理员;</li></ul>
    </div>
  </div>
</div>
</body></html>
 ]] 

2.4 学习access.lua的配置

more waf/access.lua 

require 'init'
function waf_main()
    if white_ip_check() then
    elseif black_ip_check() then
    elseif user_agent_attack_check() then
    elseif cc_attack_check() then
    elseif cookie_attack_check() then
    elseif white_url_check() then
    elseif url_attack_check() then
    elseif url_args_attack_check() then
    --elseif post_attack_check() then
    else
        return
    end
end
waf_main()

先检查白名单,通过即不检测;再检查黑名单,不通过即拒绝,检查UA,UA不通过即拒绝;检查cookie;URL检查;URL参数检查,post检查;

2.5 启用WAF并测试

2.5.1模拟url攻击

日志记录如下: 

{"user_agent":"Mozilla\/5.0 (Macintosh; Intel Mac OS X 10_12_4) AppleWebKit\/537.36 (KHTML, like Gecko) Chrome\/57.0.2987.133 Safari\/537.36","rule_tag":"\\.(bak|inc|old|mdb|sql|backup|java|class|tgz|gz|tar|zip)$","req_url":"\/dvwa\/setup.php.bak","client_ip":"10.20.66.79","local_time":"2017-04-24 14:03:29","attack_method":"Deny_URL","req_data":"-","server_name":"localhost"}

日志显示包含:记录了UA,匹配规则,URL,攻击IP,攻击时间,攻击的类型,请求的数据 ,访问域名

2.5.2 使用ab压测工具模拟防cc攻击

​
ab -c 100 -n 100 http://10.20.122.179/dvwa/instructions.php
This is ApacheBench, Version 2.3 <$Revision: 655654 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, חיים מיום ליום
Licensed to The Apache Software Foundation, Welcome to The Apache Software Foundation!
Benchmarking 10.20.122.179 (be patient).....done
Server Software:        openresty/1.11.2.2
Server Hostname:        10.20.122.179
Server Port:            80
Document Path:          /dvwa/instructions.php
Document Length:        12506 bytes
Concurrency Level:      100
Time taken for tests:   0.091 seconds
Complete requests:      100
Failed requests:        90
   (Connect: 0, Receive: 0, Length: 90, Exceptions: 0)
Write errors:           0
Non-2xx responses:      90
Total transferred:      159220 bytes
HTML transferred:       140810 bytes
Requests per second:    1102.79 [#/sec] (mean)
Time per request:       90.679 [ms] (mean)
Time per request:       0.907 [ms] (mean, across all concurrent requests)
Transfer rate:          1714.71 [Kbytes/sec] received
Connection Times (ms)
                  min  mean[+/-sd] median   max
Connect:        1    5   1.4      5       7
Processing:    12   38  16.8     37      75
Waiting:        7   38  16.9     37      75
Total:         18   43  16.4     42      82

Percentage of the requests served within a certain time (ms)

  50%     42
  66%     50
  75%     54
  80%     56
  90%     68
  95%     75
  98%     82
  99%     82
 100%     82 (longest request)
 再测试结果如下:
curl http://10.20.122.179/dvwa/instructions.php
<html>
<head><title>403 Forbidden</title></head>
<body bgcolor="white">
<center><h1>403 Forbidden</h1></center>
<hr><center>openresty/1.11.2.2</center>
</body>
</html>

​

2.5.3 模拟sql注入攻击

浏览器输入http://10.20.122.179/dvwa/instructions.php?a=select * from table--

显示结果如下 

2.5.4 模拟ip白名单

将请求ip放入ip白名单中,此时将不对此ip进行任何防护措施,所以sql注入时应该返回302

echo “10.20.71.170”>>/usr/local/openresty/nginx/conf/waf/rule-config/whiteip.rule

显示结果如下

10.20.71.170 - - [24/Apr/2017:15:10:48 +0800] "GET /dvwa/?a=select * from table HTTP/1.1" 302 5 "-" "curl/7.19.7 (x86_64-redhat-linux-gnu) libcurl/7.19.7 NSS/3.16.2.3 Basic ECC zlib/1.2.3 libidn/1.18 libssh2/1.4.2"

2.5.5 模拟ip黑名单

将请求ip放入ip黑名单中

echo “10.20.71.170” >>/usr/local/openresty/nginx/conf/waf/rule-config/blackip.rule

详细规定在arg.rule中有规定,对请求进行了规范

/usr/local/openresty/nginx/conf/waf/rule-config/cat args.rule 

\.\./
\:\$
\$\{
select.+(from|limit)
(?:(union(.*?)select))
having|rongjitest
sleep\((\s*)(\d*)(\s*)\)
benchmark\((.*)\,(.*)\)
base64_decode\(
(?:from\W+information_schema\W)
(?:(?:current_)user|database|schema|connection_id)\s*\(
(?:etc\/\W*passwd)
into(\s+)+(?:dump|out)file\s*
group\s+by.+\(
xwork.MethodAccessor
(?:define|eval|file_get_contents|include|require|require_once|shell_exec|phpinfo|system|passthru|preg_\w+|execute|echo|print|print_r|var_dump|(fp)open|alert|showmodaldialog)\(
xwork\.MethodAccessor
(gopher|doc|php|glob|file|phar|zlib|ftp|ldap|dict|ogg|data)\:\/
java\.lang
\$_(GET|post|cookie|files|session|env|phplib|GLOBALS|SERVER)\[
\<(iframe|script|body|img|layer|div|meta|style|base|object|input)
(onmouseover|onerror|onload)\=

三、WAF上线

初期上线只记录日志,不开启WAF,防止误杀;WAF规则管理使用saltstack工具,要知道并不是有了WAF就安全,存在人为因素

WAF与网络防火墙的区别

  网络防火墙作为访问控制设备,主要工作在OSI模型三、四层,基于IP报文进行检测。只是对端口做限制,对TCP协议做封堵。其产品设计无需理解HTTP会话,也就决定了无法理解Web应用程序语言如HTML、SQL语言。因此,它不可能对HTTP通讯进行输入验证或攻击规则分析。针对Web网站的恶意攻击绝大部分都将封装为HTTP请求,从80或443端口顺利通过防火墙检测。 

  一些定位比较综合、提供丰富功能的防火墙,也具备一定程度的应用层防御能力,如能根据TCP会话异常性及攻击特征阻止网络层的攻击,通过IP分拆和组合也能判断是否有攻击隐藏在多个数据包中,但从根本上说他仍然无法理解HTTP会话,难以应对如SQL注入、跨站脚本、cookie窃取、网页篡改等应用层攻击。 

  web应用防火墙能在应用层理解分析HTTP会话,因此能有效的防止各类应用层攻击,同时他向下兼容,具备网络防火墙的功能。 

未开启waf功能扫描结果: 

开启waf功能扫描结果如下: 

四、防cc攻击利器之httpgrard4.1 httpgrard介绍

HttpGuard

是基于openresty,以lua脚本语言开发的防cc攻击软件。而openresty是集成了高性能web服务器Nginx,以及一系列的Nginx模块,这其中最重要的,也是我们主要用到的nginx lua模块。HttpGuard基于nginx lua开发,继承了nginx高并发,高性能的特点,可以以非常小的性能损耗来防范大规模的cc攻击。

4.2 httpguard

防cc特效

限制访客在一定时间内的请求次数

向访客发送302转向响应头来识别恶意用户,并阻止其再次访问

向访客发送带有跳转功能的js代码来识别恶意用户,并阻止其再次访问

向访客发送cookie来识别恶意用户,并阻止其再次访问

支持向访客发送带有验证码的页面,来进一步识别,以免误伤

支持直接断开恶意访客的连接

支持结合iptables来阻止恶意访客再次连接

支持白名单功能

支持根据统计特定端口的连接数来自动开启或关闭防cc模式

GitHub - centos-bz/HttpGuard: prevent cc attack

五、waf日志分析

使用ELK对收集的waf日志进行分析

其中logstash、elasticsearch、kibana安装与配置忽略

效果展示如下: 

其中logstash日志切割配置如下:

waf.conf

input { 
  file { 
    type => "waf" 
    path => "/var/log/nginx/hack/*_waf.log" 
    codec => "json" 
  } 
} 
filter { 
    date { 
        match => ["timestamp", "YYYY-MMM-dd HH:mm:ss"] 
        target => "@timestamp" 
    } 
    mutate { 
      rename => [ "rule_tag", "Rule_tag" ] 
      rename => [ "req_url", "Req_url" ] 
      rename => [ "client_ip", "Client_ip" ] 
      rename => [ "agentip", "AgentIP" ] 
      rename => [ "user_agent", "User_Agent" ] 
      rename => [ "local_time", "Local_Time" ] 
      rename => [ "attack_method", "Attack_Method" ] 
      rename => [ "req_data", "data" ] 
      rename => [ "server_name", "Server_Name" ] 
      remove_field => [ "timestamp" ] 
    }
   }
output { 
  #stdout { codec => rubydebug } 
  elasticsearch { 
         hosts => ["127.0.0.1:9200"] 
         index => "waf-%{+YYYY.MM.dd}" 
         document_type => "waf_log" 
         template => "/etc/logstash/waf_log.json" 
         template_name => "waf_log" 
         template_overwrite => true 
    }
}

waf_log.json 

{ 
  "order": 0, 
  "template": "waf*", 
  "settings": { 
    "index.refresh_interval": "5s" 
  }, 
  "mappings": { 
    "waf": { 
      "dynamic_templates": [ 
        { 
          "notanalyzed": { 
            "match": "*", 
            "match_mapping_type": "string", 
            "mapping": { 
              "type": "string", 
              "index": "not_analyzed", 
              "doc_values": "true" 
            } 
          }
        } 
      ], 
      "properties": { 
        "@timestamp": { 
          "type": "date", 
          "format": "dateOptionalTime", 
          "index": "not_analyzed" 
        }, 
        "@version": { 
          "type": "string" 
        }, 
        "Rule_tag": { 
          "type": "string", 
          "index": "not_analyzed", 
          "doc_values": "true" 
        }, 
        "AgentIP": { 
          "type": "string", 
          "index": "not_analyzed", 
          "doc_values": "true" 
        }, 
        "User_Agent": { 
          "type": "string" 
        }, 
        "GeoLocation": { 
          "properties": { 
            "area_code": { 
              "type": "long" 
            }, 
            "city_name": { 
              "type": "string", 
              "index": "not_analyzed", 
              "doc_values": "true" 
            }, 
            "continent_code": { 
              "type": "string" 
            }, 
            "coordinates": { 
              "type": "double" 
            }, 
            "country_code2": { 
              "type": "string" 
            }, 
            "country_code3": { 
              "type": "string" 
            }, 
            "country_name": { 
              "type": "string", 
              "index": "not_analyzed", 
              "doc_values": "true" 
            }, 
            "dma_code": { 
              "type": "long" 
            }, 
            "ip": { 
              "type": "string", 
              "index": "not_analyzed", 
              "doc_values": "true" 
            }, 
            "latitude": { 
              "type": "double" 
            }, 
            "location": { 
              "type": "geo_point" 
            }, 
            "longitude": { 
              "type": "double" 
            }, 
            "postal_code": { 
              "type": "string" 
            }, 
            "real_region_name": { 
              "type": "string", 
              "index": "not_analyzed", 
              "doc_values": "true" 
           }, 
            "region_name": { 
              "type": "string", 
              "index": "not_analyzed", 
              "doc_values": "true" 
            }, 
            "timezone": { 
              "type": "string" 
            } 
          } 
        }, 
        "Server_Name": { 
          "type": "string", 
          "index": "not_analyzed", 
          "doc_values": "true" 
        }, 
        "AgentName": { 
          "type": "string", 
          "index": "not_analyzed", 
          "doc_values": "true" 
        }, 
        "Attack_Method": { 
          "type": "string", 
          "index": "not_analyzed", 
          "doc_values": "true" 
        }, 
        "Req_url": { 
          "type": "string", 
          "index": "not_analyzed", 
          "doc_values": "true" 
        }, 
        "data": { 
          "type": "string", 
          "index": "not_analyzed", 
          "doc_values": "true" 
        }, 
        "type": { 
          "type": "string" 
        } 
      } 
    } 
  } 
} 

参考链接:

GitHub - unixhot/waf: 使用Nginx+Lua实现的WAF(版本v1.0)

极客企业版

GitHub - centos-bz/HttpGuard: prevent cc attack

http://www.dongcoder.com/detail-457839.html

ELK分析ngx_lua_waf软件防火墙日志_51CTO博客_ngx_lua_waf

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值