国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

express中間件當做前端服務器的安全漏洞處理

這篇具有很好參考價值的文章主要介紹了express中間件當做前端服務器的安全漏洞處理。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點擊"舉報違法"按鈕提交疑問。

使用express當做node服務器時,發(fā)現(xiàn)安全漏洞,記錄處理步驟:
PS:以下安全內容處理,需要使用到redis進行會話存儲、請求計數、請求唯一限制等。為盡量確保開發(fā)環(huán)境與部署環(huán)境一致,請開發(fā)環(huán)境安裝并啟動Redis服務。
** 此文檔只是說明記錄關鍵步驟。具體實現(xiàn)代碼可參照附件。**

1、cookie沒有加簽、缺少sameSite參數

  • 使用clientKey給cookie加簽,所有通過res.cookie()方法設置cookie的地方都需要設置,此處只是演示:
// app.js
...
// 設置cookie時,如果需要使用簽名加密,必須在此處設置簽名字符串
app.use(cookieParser(clientKey));
...
...
// 修改前
res.cookie('C2AT', data.access_token, { maxAge: parseInt(data.expires_in) * 1000});

// 修改后
res.cookie('C2AT', data.access_token, { maxAge: parseInt(data.expires_in) * 1000,sameSite:true,signed:true });
...
  • 加簽后的cookie獲取方式需要修改:
...
// 加簽前
let c2at = req.cookie.C2AT;

// 加簽后
let c2at = req.signedCookies.C2AT;
...

2、圖形驗證碼登錄,抓包登錄接口后使用相同驗證碼登錄(Replay)成功

原因: express使用了cookie-session中間件,該中間件是將session數據存放在了cookie之中,導致使用相同的cookie時,sessoin中拿到的驗證碼是同一個。
解決方案:
使用express-session中間件,將session存放在服務器,cookie中只有sessionId。中間件文檔:https://express.nodejs.cn/en/resources/middleware/session.html
demo項目環(huán)境:node V16.19.0、express ^4.15.5,版本不一致,插件版本可能也不一致(插件部分API可能不一致),根據各自項目版本適配。
此處使用redis數據庫作為session存儲,根據項目不同,還可以使用其他,具體參照文檔修改關鍵位置。

  • 依賴包準備:
npm install express-session@1.15.6 redis@3.1.2 connect-redis@6.1.3 dotenv@16.0.3
  • 環(huán)境變量,在express項目根目錄下新建.env文件,在該文件中添加環(huán)境變量(使用云平臺部署方式,該文件中的環(huán)境變量會被云平臺注入的同名環(huán)境變量覆蓋)
// .env
# redis配置
redis_host=127.0.0.1
redis_port=6379
redis_db=10
  • 關鍵代碼
// app.js
// 引入環(huán)境變量,盡可能早
require('dotenv').config();
var express = require('express');
var app = express();
// 將session信息存放在服務端,必須要設置數據庫保存session
var session = require('express-session');
// 鏈接redis
var RedisStore = require("connect-redis")(session);
// redis 數據庫
const redis = require("redis");
...
...
// 初始化redis
const redisClient = (function(){
    let client = redis.createClient({
        url:`redis://${process.env.redis_host || '127.0.0.1'}:${process.env.redis_port || '6379'}/${process.env.redis_db || '0'}`,
        // username:'',
        // password:'',
    });
    client.on("error", function(error) {
        console.error('redis鏈接失敗',error)
    });
    client.on("connect", function() {
        console.log('redis鏈接成功')
    });
    return client;
})();
// 初始化session使用的store
const redisStore = (function(){
    let store = undefined;
    // 部署環(huán)境必須指定外部存儲會話
    if(process.env.NODE_ENV === 'production'){
        store = new RedisStore({
            client: redisClient,//redis客戶端,必須要指定db
            prefix: "webSession:",//在redis中的key名
        });
        // 初始化時,從存儲中刪除所有會話
        store.clear();
    }
    return store;
})();
// 初始化session中間件,會話信息存儲在服務器,只在cookie中設置sessionid
app.use(session({
    secret: clientKey,//對session數據進行加密的字符串.這個屬性值為必須指定的屬性(使用cookieParser時,兩個中間件的簽名需要一致)
    resave: true, // session(cookie中存在sessionId的session)沒有被修改,也保存session
    saveUninitialized: false, // 強制將“新的且未修改”的會話保存到存儲中。 
    rolling:false,//強制在每個響應上設置會話標識符 cookie。 到期重置為原來的maxAge,重置到期倒計時。默認值為false。
    cookie: { 
        maxAge: 1000 * 60 * 60 * 24 * 7, 
        signed: true,
        sameSite:true,//是否為同一站點的cookie
    },
    store:redisStore
}));
...
...
// 登出操作,清空cookie,前端再執(zhí)行重定向
app.post('/check_out', function (req, res) {
	...
    req.session.destroy()
	...
});
...
// 獲取用戶信息
app.get('/user_info', function (req, res) {
    ...
    UserUtils.commonCheckToken(res, req).then((tokenInfo) => {
        // 修改session內容,觸發(fā)session保存到redis、并向res cookie中設置sessionid
        req.session.userId=tokenInfo.userId;
		...
		...
...
  • 初始化express-session中間件后,在接口中正常使用req.session 即可。

3、抓包后,更改接口參數請求成功(參數篡改)、重復請求接口(Replay)請求成功(接口重放)

基于session進行參數加簽,請確保已經完成問題2的處理,同時需要前端配合使用MD5對關鍵接口加簽(通過瀏覽器重定向訪問的接口無法處理,需要在.env 中添加request_sign_ignore 忽略接口檢查)

  • 環(huán)境變量,在.env中添加:
// .env
# 請求簽名啟用(1啟用、默認關閉)ps:因為前端也需要這個變量所以添加前綴custom_
custom_request_sign_enable=1
# 請求簽名超時毫秒(同一個請求客戶端時間戳和服務器時間戳的過期閾值)
request_sign_time_out=30000
# 需要簽名的請求正則字符串
request_sign_reg=\/proxy|\/product-im|\/other-anonymous|\/uploadsFiles|\/downloadFiles|\/qrcode|\/qrcode\/scan|\/custom-code|\/custom-login|\/oauth2-login|\/end-login|\/check_out|\/user_info|\/custom\/env|\/company
# 忽略簽名校驗的請求正則字符串
request_sign_ignore=\/oauth2-login|\/downloadFiles
  • app.js 關鍵代碼
...
// 初始化私鑰和公鑰(!??!注意:私鑰不能暴露到外部,必須保留在服務器)
const rsaPublicKey = encryUtils.rsa_publicKey();
global.rsaPrivateKey = encryUtils.rsa_privateKey();
// 初始化自定義環(huán)境變量
const custom_env = (function(){
    const envs= process.env || {};
    const customEnvs = {};
    Object.keys(envs).forEach(key => {
        // 注意:只獲取自定義的環(huán)境變量,其他環(huán)境變量可能包含服務器信息,通過接口返回可能不安全,請謹慎處理
        if(typeof key === 'string' && key.startsWith('custom_')){
            customEnvs[key] = envs[key]
        }
    });
    return customEnvs;
})();
...
...
// 調試開發(fā)時進行跨域設置
app.use(cors({
	...
    headers: 'Authorization,x-requested-with,content-type,content-length,paramSign,sign,requestTime',// 開發(fā)環(huán)境跨域,需要添加paramSign,sign,requestTime允許跨域
}));
//應用的每個請求都會執(zhí)行該中間件
app.use(function (req, res, next) {
	...
    // 修改session內容,觸發(fā)session保存到redis、并向res cookie中設置sessionid
    req.session.lastTime=new Date().getTime();
	next();
});
// request 有效性驗證
app.use(new RegExp(process.env.request_sign_reg),function(req, res, next){
    if(process.env.custom_request_sign_enable === '1'){
        if(process.env.request_sign_ignore && new RegExp(process.env.request_sign_ignore).test(req.originalUrl)){
            // 存在忽略請求配置,且忽略正則匹配成功 則不校驗
            next();
        }else{
            validRequest({
                req,
                res,
                rsaPrivateKey,
                redisClient
            }).then(res => {
                logger.info('validRequest success ----------> ' + req.originalUrl);
                next();
            }).catch((err) => {
                console.error('validRequest fail ----------> ',req.originalUrl,err)
                const e = typeof err === 'string' ? {status:'500',errorCode:'error',errorMessage:err} : err;
                res.status(e.status).json(e);
            });
        }
    }else{
        next();
    }
})
...
...
//前端html模板引用configuration.js將環(huán)境變量等信息注入到前端全局變量
app.get('/configuration.js', function (req, res) {
    // 獲取自定義環(huán)境變量
    function loadCustomEnvVar(){
        let customEnvs = '';
        Object.keys(custom_env).forEach(key => {
                customEnvs += `${key}="${custom_env[key]}",`;
        });
        customEnvs = customEnvs.substring(0,customEnvs.lastIndexOf(','));
        return customEnvs;
    }
    res.setHeader('Content-type', 'application/javascript; charset=UTF-8');
    res.send(`const ${loadCustomEnvVar()},pubKey="${rsaPublicKey}";`);
});
...
  • express服務器EncryptionUtils.js 加密工具,見附件:express:EncryptionUtils.js

  • ValidateUtils.js 請求有效性校驗工具,見附件:express:ValidateUtils.js

  • 前端參數簽名關鍵代碼,request.ts

// request.ts
...
...
// 請求攔截器,為請求加簽
request.interceptors.request.use((url, options) => {
  const { params, data, } = options;
  const query = getQueryObject(url);
  const { paramSign, sign, requestTime } = requestSign({ ...query, ...params }, data);
  return {
    url, options: {
      ...options,
      headers: {
        ...options.headers,
        paramSign,
        sign,
        requestTime,
      }
    }
  }
});
...
...

/**
 * 請求簽名
 * @param params query參數對象
 * @param body body參數對象
 * @returns 
 */
export const requestSign = (params: object, body: object) => {
// 沒有開啟請求簽名校驗(要使用自定義環(huán)境變量,首先應當引入全局configuration.js)
  if (getCustomEnv('custom_request_sign_enable') !== '1') {
    return {};
  }
  // 時間戳簽名
  const signtimestamp = new Date().getTime().toString();
  // 參數簽名
  const sign = default_md5_key + signtimestamp;
  // 參數轉能簽名的字符串
  const signPramsStr = fomartSignParams2String(params, body);
  return {
    paramSign: md5_encode(signPramsStr, sign),
    sign: rsa_pub_encode(sign),
    requestTime: signtimestamp,
  }
}
  • 前端附件上傳接口簽名:
// 三種解決方案
// 1、為每個antd的Upload組件添加簽名內容
// 2、自定義組件包裝antd的Upload組件,并為其添加簽名內容,其他地方用到的Upload組件統(tǒng)一使用自定義的
// 3、express忽略接口中添加附件上傳相關接口正則
...
import { requestSign } from '@/utils/request';
...
<Upload headers={requestSign(...)}>...</Upload>
  • 前端EncryptionUtils.js加密工具,見附件web:EncryptionUtils.js

  • 前端獲取express中的環(huán)境變量函數:

// config.ts
...
const { 
	...
	NODE_ENV 
} = process.env;
const isDev = NODE_ENV === 'development';
...
export default {
...
context: {
    customConfigPath: (isDev ? constant.express : '') + '/configuration.js',
  },
...
}
// document.ejs
...
<head>
	...
	<script src="<%=context.customConfigPath %>"></script>
</head>
...
...
// utils.ts
...
/**
 * 獲取express提供的配置
 * @param name 配置的key
 * @returns 配置的值
 */
export const getCustomEnv = (name: string) => {
  if (typeof name !== 'string') {
    return '';
  }
  try {
    return eval(name) || '';
  } catch (error) {
    return '';
  }
}
...
...

修改完成后,具體實現(xiàn)效果應當為

  • 前端簽名外的地方直接請求express的接口會被攔截(如:瀏覽器直接訪問接口、postman請求接口)
  • 抓包后replay會被攔截

4、登錄、修改密碼等敏感數據未加密

  • 生成RSA秘鑰對:參考使用openssl生成RSA秘鑰:https://blog.csdn.net/qq_37819292/article/details/136320969
  • 敏感數據手動加密是有必要的,但數據傳輸的安全性不要依賴手動加密,請啟用HTTPS。
  • express將RSA公鑰傳輸給前端(在第3條中已經添加,此處只展示關鍵代碼,不用重復添加)
...
// 初始化私鑰和公鑰(!?。∽⒁猓核借€不能暴露到外部,必須保留在服務器)
const rsaPublicKey = encryUtils.rsa_publicKey();
global.rsaPrivateKey = encryUtils.rsa_privateKey();
// 初始化自定義環(huán)境變量
const custom_env = (function(){
    const envs= process.env || {};
    const customEnvs = {};
    Object.keys(envs).forEach(key => {
        // 注意:只獲取自定義的環(huán)境變量,其他環(huán)境變量可能包含服務器信息,通過接口返回可能不安全,請謹慎處理
        if(typeof key === 'string' && key.startsWith('custom_')){
            customEnvs[key] = envs[key]
        }
    });
    return customEnvs;
})();
...
...
app.get('/configuration.js', function (req, res) {
    // 獲取自定義環(huán)境變量
    function loadCustomEnvVar(){
        let customEnvs = '';
        Object.keys(custom_env).forEach(key => {
                customEnvs += `${key}="${custom_env[key]}",`;
        });
        customEnvs = customEnvs.substring(0,customEnvs.lastIndexOf(','));
        return customEnvs;
    }
    res.setHeader('Content-type', 'application/javascript; charset=UTF-8');
    res.send(`const ${loadCustomEnvVar()},pubKey="${rsaPublicKey}";`);
});
...
  • 前端使用RSA公鑰加密賬號密碼等敏感信息(只是登錄、修改密碼的信息,RSA加密即可;其他地方加密內容過多時,使用RSA+AES的方式)
// login.ts 登錄信息加密
...
import { rsaEncodeBodyInfo } from '@/utils/EncryptionUtils';
...
* login({ payload }, { call, put }) {
        // 登錄信息加密
        let paramObj = rsaEncodeBodyInfo(payload);
		let response = yield call(fakeAccountLogin, paramObj);
		...
    },
...
...
// user.ts 修改密碼信息加密
...
import { rsaEncodeBodyInfo } from '@/utils/EncryptionUtils';
...
*modifyModifyPwd({ payload, callback }, { call }) {
  const params = rsaEncodeBodyInfo(payload);
  const response = yield call(updateModifyPwd, params);
  if (callback) callback(response);
},
...
...
  • express使用RSA私鑰解密后登錄、修改密碼
// app.js
...
var encryUtils = require('./utils/EncryptionUtils');
...
// 統(tǒng)一認證登錄
app.post('/custom-login', function (req, res) {
    const bodyMap = encryUtils.RsaDecodeBodyInfo(req.body,rsaPrivateKey);
    const {sn,type,userName,password,code} = bodyMap;
	...
});
...
...
// proxy.js
...
const { RsaDecodeBodyInfo } = require('../utils/EncryptionUtils');
...
// 修改密碼要對字段信息進行解密
router.use("/edp/v1/users/loginModifyPwd", function (req, res, next) {
    req.body = RsaDecodeBodyInfo(req.body,global.rsaPrivateKey);
    next();
});
router.use("/", function (req, res, next) {
...
}
...

5、缺少安全相關Header設置

使用helmet可以快速設置安全相關的Header,顯著地提升你應用的安全性

  • 安裝插件
npm install helmet
  • 使用
// app.js
...
// 設置與安全相關的 HTTP 響應標頭
const helmet = require('helmet');
...
var app = express();
// 刪除x-powered-by 響應頭
app.set('x-powered-by',false) 
// 設置與安全相關的 HTTP 響應標頭
app.use(helmet({ 
    // 跨域資源策略 "same-origin" | "same-site" | "cross-origin" 
    crossOriginResourcePolicy: { policy: "same-site" },
}));
...

6、暴力請求,可無限次對服務器發(fā)起請求

通過循環(huán)等方式對接口暴力請求,常見登錄密碼暴力破解等腳本攻擊。使用rate-limiter-flexible可以限制用戶/IP對接口的訪問速率,超過訪問速率進行訪問限制。文章來源地址http://www.zghlxwxcb.cn/news/detail-843671.html

  • 安裝插件
npm install rate-limiter-flexible
  • 環(huán)境變量
//.env
# 請求速率限制啟用(1啟用、默認關閉)
request_limit_enable=1
# 請求速率限制周期內可消耗計數點
request_limit_points=60
# 請求速率限制周期內最小訪問次數限制(/proxy 和 /product-im 的接口外受此限制)
request_limit_min_count=5
# 請求速率限制重置周期s
request_limit_duration=5
# 請求速率超過計數點鎖定時長s
request_limit_blockDuration=60
  • 使用
// app.js
...
// 初始化redis客戶端
const redisClient = redis.createClient({
    url:`redis://${redis_host}:${redis_port}/${redis_db}`,
    // username:'',
    // password:'',
});
redisClient.on("error", function(error) {
    console.error('redis鏈接失敗',error)
});
redisClient.on("connect", function() {
    console.log('redis鏈接成功')
});
...
// 處理請求靜態(tài)資源中的gzip文件
app.use(function (req, res, next) {
...
});
//請求速率限制
const rateLimiter = (function(){
    let limiter = undefined;
    if(request_limit_enable === '1'){
        limiter = new RateLimiterRedis({
            storeClient: redisClient,
            keyPrefix: 'rateLimiter',
            points: Number(request_limit_points), // 限制周期內的可消耗計數點
            duration: Number(request_limit_duration), // 重置計數器周期
            blockDuration:Number(request_limit_blockDuration),// 超過計數點的鎖定時間
            // inMemoryBlockOnConsumed:Number(request_limit_points),// 超過設置的點時,阻止向存儲添加計數器
            // inMemoryBlockDuration:2,// 阻止向存儲添加計數器的時間
            insuranceLimiter:new RateLimiterMemory({
                points: Number(request_limit_points), 
                duration: Number(request_limit_duration),
            }),//保險,只有當外部存儲無法使用時生效
        });
    }
    return limiter;
})() 
...
// IP請求速率限制
app.use(function(req, res, next){
    if(!rateLimiter){
        next();
    }else{
        // 總點數
        const points = Number(request_limit_points);
        const minCout = Number(request_limit_min_count);
        // 請求消耗的計數點(/proxy 和 /product-im 的接口每次消耗一個計數點,其他類型的接口周期內只能請求5次)
        let pointsToConsume = req.path.includes('/proxy') || req.path.includes('/product-im') ? 1 : Math.floor(points / minCout);
        rateLimiter.consume(req.ip,pointsToConsume)
          .then(() => {
            next();
          })
          .catch(() => {
            logger.error('rateLimiter fail ----------> ' + req.originalUrl)
            res.status(429).json({errorMessage:'請求過快,請稍后再試'});
          });
    }
});
// request 有效性驗證
app.use(new RegExp(request_sign_reg),function(req, res, next){
...

到了這里,關于express中間件當做前端服務器的安全漏洞處理的文章就介紹完了。如果您還想了解更多內容,請在右上角搜索TOY模板網以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網!

本文來自互聯(lián)網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。如若轉載,請注明出處: 如若內容造成侵權/違法違規(guī)/事實不符,請點擊違法舉報進行投訴反饋,一經查實,立即刪除!

領支付寶紅包贊助服務器費用

相關文章

  • 初識express/路由/中間件

    初識express/路由/中間件

    ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ???????? ? ? ? ?

    2024年02月11日
    瀏覽(44)
  • node中間件-express框架

    node中間件-express框架

    方式一 : express提供的腳手架,直接創(chuàng)建一個應用的骨架 安裝腳手架npm install -g express-generator 創(chuàng)建項目 express express-demo 安裝依賴npm install 啟動項目 node bin/www 方式二 : 從零搭建自己的express應用結構; 初始化項目 npm init 安裝express npm i express 導入–創(chuàng)建–監(jiān)聽 使用參考文檔 中

    2024年02月16日
    瀏覽(34)
  • 93 # 實現(xiàn) express 錯誤處理中間件

    93 # 實現(xiàn) express 錯誤處理中間件

    上一節(jié)實現(xiàn)了 express 的中間件,這一節(jié)來實現(xiàn)錯誤處理中間件 執(zhí)行某一步出錯了,統(tǒng)一規(guī)定調用 next 傳遞的參數就是錯誤信息 先看 express 實現(xiàn)的demo 然后去訪問: http://localhost:3000/ 錯誤處理中間價,里面必須要有 4 個 參數(取函數的長度),放到棧的最底下 下面實現(xiàn)處理邏

    2024年02月07日
    瀏覽(29)
  • 【Express】文件上傳管理 multer 中間件

    Multer是Node.js中用于處理文件上傳的中間件。它可以幫助你處理文件上傳的相關邏輯,如接收和保存上傳的文件、限制文件大小、設置文件類型限制等。只能用于處理 multipart/form-data 類型的表單數據,它主要用于上傳文件。 下面是使用Multer中間件的基本步驟: 安裝multer:在命

    2024年02月07日
    瀏覽(24)
  • 編寫中間件以用于 Express 應用程序

    編寫中間件以用于 Express 應用程序

    中間件 函數能夠訪問請求對象?( req )、響應對象?( res ) 以及應用程序的請求/響應循環(huán)中的下一個中間件函數。下一個中間件函數通常由名為? next ?的變量來表示。 中間件函數可以執(zhí)行以下任務: 執(zhí)行任何代碼。 對請求和響應對象進行更改。 結束請求/響應循環(huán)。 調用堆棧

    2024年02月10日
    瀏覽(21)
  • Go重寫Redis中間件 - GO實現(xiàn)TCP服務器

    首先新建一個項目go-redis,將config和lib包放到項目中,config.go用來解析配置,比如端口、功能、DB數;lib包有兩個文件夾,分別是logger和sync,其中l(wèi)ogger.go是一個日志框架,sync包中的bool.go包裝了atomic操作,因為atomic原生沒有bool類型,所以將uint32類型改造成bool型的atomic,wait.g

    2024年02月15日
    瀏覽(23)
  • express學習筆記5 - 自定義路由異常處理中間件

    express學習筆記5 - 自定義路由異常處理中間件

    修改router/index.js,添加異常處理中間件 完整代碼 創(chuàng)建 utils/constant:(為了方便后期統(tǒng)一維護,單獨拉出來定義) 然后刷新http://localhost:8000/user ?這就完成了

    2024年02月14日
    瀏覽(31)
  • 中間件安全:Apache Tomcat 弱口令.(反彈 shell 拿到服務器的最高控制權.)

    中間件安全:Apache Tomcat 弱口令.(反彈 shell 拿到服務器的最高控制權.)

    Tomcat? 是 Apache 軟件基金會(Apache Software Foundation)的 Jakarta 項目中的一個核心項目,由 Apache、Sun 和其他一些公司及個人共同開發(fā)而成。 通過弱口令登錄后臺,部署 war 包 geshell . 中間件安全:Apache Tomcat?弱口令. Apache Tomcat 弱口令: 靶場準備:Web安全:Vulfocus 靶場搭建.(漏

    2024年02月05日
    瀏覽(28)
  • node 第十四天 基于express的第三方中間件multer node后端處理用戶上傳文件

    Multer 是一個 node.js 中間件,用于處理 multipart/form-data 類型的表單數據,它主要用于上傳文件。它是寫在 busboy 之上的所以非常高效。 前面我們已經知道了怎樣利用express提供的靜態(tài)資源處理中間件 express.static() 處理用戶請求靜態(tài)資源文件(圖片, js, css等) 接下來學習如何處理用

    2024年02月06日
    瀏覽(23)
  • 前端中間件Midway的使用

    前端中間件Midway的使用

    Midway 是阿里巴巴 - 淘寶前端架構團隊,基于漸進式理念研發(fā)的 Node.js 框架,通過自研的依賴注入容器,搭配各種上層模塊,組合出適用于不同場景的解決方案。 Midway 基于 TypeScript 開發(fā),結合了面向對象(OOP + Class + IoC)與函數式(FP + Function + Hooks)兩種編程范式,并在此之

    2024年02月06日
    瀏覽(23)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領取紅包,優(yōu)惠每天領

二維碼1

領取紅包

二維碼2

領紅包