月度归档:2021年10月

建材网站建设解决方案

如何定位和展现建材企业的网络形象?让消费者能有全面清晰的认知?

如何表现建材企业的特色和服务品质,以区别于同行,获得更多精准消费者的认可?

如何利用网站与线下销售相结合,将消费者带到网络上,享受更好的服务,并长期粘滞住消费者?

如何通过网络营销带来更多的消费者,形成大面积良好的口碑效应,让消费者安全放心地消费?

建材行业网站建设的服务范围

网站定位分析及建议,包含网站策划布局和结构,形成一整套解决方案;

建材行业网站的形象界面创意设计以及DIV前台结构布局;

系统性的架构策略、程序模块功能分析、运营维护策略、培训及知识转移计划等;

可延展性分析、可扩充性考虑以及系统稳定性分析报告;

网站运营维护方案。根据企业的管理模式,建议网站日常信息提供、审核、发布的一整套运转方案,协助企业高效管理网站;

网站的营销推广计划,网站运维策略及网站价值分析、盈利情况分析等等;

建材网站建设模块设置

项目 模块内容详情
个性化页面建设 建材网站的页面设计应符合产品及其客户的需求:建材的厚重,钢铁的银白,营销的热情等等。
建材网站可自由编辑物品上下架、装修、调价及促销广告。
检索 (1)产品搜索:能让顾客按关键字、类别、促销宝贝、品牌、价格区间来组合搜索宝贝。保证每个页面都有搜索条。
(2)热门搜索词管理:顾客在搜索条内输入信息时,自动弹出下拉菜单显示热门搜索词。可仿照淘宝。
相关商品 在某件商品描述底部或者侧栏展示相关产品推荐。
购物车 仿照淘宝购物车的方式。进入购物车后,可查看商品单价,可进行商品数量修改、删除。
商品上传 建材网站所有者可进行单件商品上传,也可进行批量上传,上传后的宝贝可进行管理。
商品分类 目前主营产品家居建材、日后产品类型会拓展。所以后台可进行商品大类的新增与管理,并能根据自己需要设置3级子分类。
支付方式 采用支付宝网银在线支付方式,网银在线自动转账支付业务。
流量统计 安装百度统计,cnzz等第三方平台统计代码方便查阅数据。
排行榜 前台首页显示“销售排行榜”和“点击排行版”,为顾客提供购买参考信息。
商品排序 在顾客浏览商品时,可根据价格、新品、销量、人气对商品进行排序。
在线客服 载入QQ做客服沟通工具(QQ必须具备管理员与店铺后台沟通,店铺与买方沟通,商城客服沟通)
订单管理 可浏览、修改、关闭订单的详细信息
库存提醒 可设置当某件商品库存低于多少件时,进行库存提醒。
缺货登记 缺货的商品系统能自动登记,也可人工手动添加。
促销模板 提供多种促销模板以保证建材网站各种促销活动,以下几个必须具备:送、券、礼、分、折、免
可拓展版块 可以随着业务的需求,拓展一些新的版块,如话费充值和买火车票、汽车票等。
友情链接 提供相关网站的友情链接。

dedecms(织梦)数据提交防csrf请求校验

前言
很多人喜欢直接在前端写ajax向后台指定地址提交数据,这样的做法过于草率,当时可能比较省事,后面付出的代价必定是惨痛的。笔者曾“有幸”经历过几次这样的跨域攻击,服务器遇到来自四面八方的海量请求,瞬间崩溃,日志显示请求来自不同国家和城市,这样一来常用的IP防火墙策略收效甚微。最终发现,服务器诸多接口安全性过低,省去了诸多校验,看似化繁为简,实则漏洞百出,攻击者在网上收集“肉鸡”,同时向指定服务器发送请求,致使服务器瘫痪。本文通过常用的dede(织梦)二次开发,
展示安全程度较高的前台数据提交策略,抛砖引玉,望读者日常开发过程勿避重就轻,忽视数据与信息安全。
环境与需求

  1. Centos 6.8
  2. LANMP
  3. DEDEV5.7
    4 .需求:使用dede创建自定义表单,前台向后台自定义表单提交数据。 代码实现
    切换到dede根目录 创建自定义标签解析文件 /include/taglib/csrftoken.lib.php

<?php
require_once(dirname(FILE).”/../helpers/cache.helper.php”);
if(!defined(‘DEDEINC’)){
exit(“Request Error!”);
}
/**

  • 自定义csrf_token生成标签
    *
  • @version $Id: csrftoken.lib.php 1 10:11 2018年1月9日
  • @package DedeCMS.Taglib
  • @copyright Copyright (c) 2007 – 2010, DesDev, Inc.
  • @author underclounds underclounds@gmail.com
  • @license http://help.dedecms.com/usersguide/license.html
  • @link http://www.dedecms.com
    */

/*>>dede>>
csrf签名标签
全局标记
V55,V56,V57
生成指定长度的随机加密字符串,每十分钟过期一次,用于前后台数据交互的验证 防止csrf攻击
{dede:csrftoken name=’token名 默认csrf’ len=’长度 默认12位’ exp=’过期时间 默认600秒’ /}

dede>>*/

function lib_csrftoken(&$ctag,&$refObj)
{
global $dsql,$envs;

//读取sessionId
@session_start();
$sessionId = session_id();

//属性处理
$attlist="name|csrf_token,len|12,exp|600";

//填充属性默认值
FillAttsDefault($ctag->CAttribute->Items,$attlist);

//读取标签属性
$name = $ctag -> GetAtt('name');
$len = (int) $ctag -> GetAtt('len');
$expires = (int) $ctag -> GetAtt('exp');

//读取缓存token
$cacheToken = GetCache($sessionId, $name);
if(empty($cacheToken)) {
    //生成token
    $token = randStr($len);

    //将token存入缓存
    $token = substr(sha1($token), 3, 32);
    SetCache($sessionId, $name, $token, $expires);
} else {
    $token = $cacheToken;
}
return $token;

}

/**

  • 生成随机字符串
  • @param $len int 长度
  • @return string
    */
    function randStr($len)
    {
    $str = ‘abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789’;
    $token = ”;
    for($i=0; $i<$len; $i++)
    {
    $token .= $str[rand(0,strlen($str)-1)];
    }
    return $token;
    }
    创建处理ajax请求文件 /plus/ajaxOrder.php

<?php
//header(“Access-Control-Allow-Origin:*”);//释放跨域
require_once(dirname(FILE).”/../include/common.inc.php”);
require_once(dirname(FILE).”/../include/helpers/cache.helper.php”);
global $dsql;

/**

  • ———————————————
  • Class ajaxOrder
  • 处理ajax提交的用户预约信息
  • @name ajax
  • @package ajax
  • @author underclounds underclounds@gmail.com
  • / class ajaxOrder { /*
    • @var string 客户姓名
      */
      public $username;
    /**
    • @var string 预留手机
      */
      public $tel;
    /**
    • @var string 预约分类
      */
      public $sorts;
    /**
    • @var string 预留地址
      */
      public $address;
    /**
    • @var int 面积
      */
      public $area;
    /**
    • @var string 预约时间
      */
      public $ordertime;
    /**
    • @var object $dsql 全局数据库操作对象(dede)
      */
      private $dsql;
    /**
    • @var int 错误码
      */
      protected $errcode = 1000;
    /**
    • @var string 提示信息
      */
      protected $errmsg = ‘ok’;
    /**
    • 构造方法
    • ajax constructor.
      */
      public function __construct($dsql)
      {
      $this -> dsql = $dsql;
      if(!empty($_POST[‘username’])) {
      $this -> username = self::filter($_POST[‘username’]);
      }
      if(!empty($_POST[‘tel’])) {
      $this -> tel = self::filter($_POST[‘tel’]);
      }
      if(!empty($_POST[‘sorts’])) {
      $this -> sorts = self::filter($_POST[‘sorts’]);
      }
      if(!empty($_POST[‘address’])) {
      $this -> address = self::filter($_POST[‘address’]);
      }
      if(!empty($_POST[‘area’])) {
      $this -> area = self::filter($_POST[‘area’]);
      }
      if(!empty($_POST[‘ordertime’])) {
      $this -> ordertime = self::filter($_POST[‘ordertime’]);
      }
      }
    /**
    • 检查所有参数
    • @return bool
      */
      public function check()
      {
      if(!$this->checkToken()) return false;
      if(!$this->verifyName($this->username)) return false;
      if(!$this->verifyTel($this->tel)) return false;
      $this -> add();
      return true;
      }
    /**
    • 检查csrf_token是否正确
    • @return bool
      */
      private function checkToken()
      {
      @session_start();
      $sessionId = session_id();
      $token = $_SERVER[‘HTTP_X_CSRF_TOKEN’];
      $cacheToken = GetCache($sessionId,’csrf_token’);
      if(!empty($token) && !empty($cacheToken) && $token === $cacheToken) {
      return true;
      }
      $this -> errcode = 1005;
      $this -> errmsg = ‘非法请求’;
      return false;
      }
    /**
    • 写入预约数据
      */
      private function add()
      {
      $sql = “INSERT INTO #@__diyform1 (id, ifcheck, username, tel, sorts, address, area, ordertime) “;
      $sql .= “VALUES (NULL, 0, ‘$this->username’, ‘$this->tel’, ‘$this->sorts’, ‘$this->address’, ‘$this->area’, ‘$this->ordertime’); “;
      $this -> dsql->ExecuteNoneQuery($sql);
      }
    /**
    • 检验用户名
    • @param $username string 用户名
    • @return bool
      */
      public function verifyName($username)
      {
      if(!empty($username)) {
      return true;
      }
      $this -> errcode = 1001;
      $this -> errmsg = ‘用户名不能为空’;
      return false;
    } /**
    • 检验手机号码
    • @param $tel
    • @return bool
      */
      public function verifyTel($tel)
      {
      if(!empty($tel)) {
      if(!preg_match(‘/^1[345678]\d{9}$/’, $tel)) {
      $this -> errcode = 1003;
      $this -> errmsg = ‘手机号码格式不正确’;
      return false;
      }
      if($this->telExist($tel)) {
      $this -> errcode = 1004;
      $this -> errmsg = ‘手机号码已预约’;
      return false;
      }
      return true;
      }
      $this -> errcode = 1002;
      $this -> errmsg = ‘手机号码不能为空’;
      return false;
      }
    /**
    • 检验手机是否存在
    • @param $tel string 手机号
    • @return bool
      */
      public function telExist($tel)
      {
      $row = $this -> dsql ->GetOne(” SELECT id FROM #@__diyform1 WHERE tel = ‘{$tel}’ “);
      return !empty($row[‘id’]);
      }
    //以下可实现类似方法…….. /**
    • 一般过滤函数 防止注入
    • @param $str string 字符串
    • @return mixed
      */
      public static function filter($str)
      {
      return preg_replace(‘/(delete|insert|select|update|drop|truncate|where)/i’,””, $str);
      }
    public function returnMsg(){
    $errInfo = array(
    ‘errcode’ => $this -> errcode,
    ‘errmsg’ => $this -> errmsg
    );
    return json_encode($errInfo);
    }
    //魔术方法set
    public function __set($name, $value)
    {
    $this -> $name = $value;
    } //魔术方法get
    public function __get($name)
    {
    return $this -> $name;
    }
    }

$ajax = new ajaxOrder($dsql);
$ajax -> check();
echo $ajax -> returnMsg();

前端测试代码

    var token = "{dede:csrftoken exp='10'/}";
    $.ajax({
        url: 'http://localhost/plus/ajaxOrder.php',
        type: 'post',
        headers: {"X-CSRF-TOKEN" : token},
        data: data,
        dataType: 'json',
        success:function (e) {
            console.log(e);
        }
    });

失败返回示例如下:

成功返回示例如下:

注意:笔者使用缓存方式储存csrf_token 以SESSIONID作为识别用户标识,默认十分钟过期。
本文意在使用signature方式校验,故服务端除csrf_token外的校验写法较为简略,读者可自行扩展。

BT宝塔Nginx设置只允许域名访问禁止IP访问 SSL同步设置防源站泄漏

对于用BT宝塔的小伙伴,很多时候建站基本程序都是用NGINX,用的没啥问题,但是大家有没有发现,如果你直接在地址栏上输入IP去访问的话,一般默认会返回一个BT宝塔的默认页面。如果你前面再加上https的话,那么估计就会看到你的网站了。因为宝塔默认没有禁止别人通过IP访问,因此很容易被扫描器扫描到,加上NGINX的通过IP访问HTTPS的话,会自动匹配第一个站点的SSL证书给IP使用,因此会造成IP泄露(证书带域名信息,网上有扫描全网IP并读取SSL证书中域名信息的方法)

那么,怎么设置BT宝塔Nginx设置只允许域名访问,禁止IP访问呢?特别涉及到HTTPS的话,这个源站IP如何防止泄漏呢?虾皮路也捣鼓了一下,分享一下过程。

为了安全起见,接下来需要做两个事情。

1、给IP配置上一张带错误域名的证书,防止泄露你自己的域名

2、禁止直接访问IP,将访问IP的请求,不管是HTTP还是HTTPS全部转错误页  返回状态码444 ERR_EMPTY_RESPONSE

两步其实可以合并成下面步骤操作:

1、在BT宝塔面版上新建站点

首先在宝塔中创建一个默认站点,这里域名随意填写,只要不是你的域名就行。

这里我们可以输入一个 default.com ,或者你也可以输入123.com、456.com等等

BT宝塔Nginx设置只允许域名访问禁止IP访问 SSL同步设置防源站泄漏插图

2、修改默认站点

我们需要将默认的站点改为default.com

BT宝塔Nginx设置只允许域名访问禁止IP访问 SSL同步设置防源站泄漏插图1

3、给默认站点设置证书

给IP配置上一张带错误域名的证书,我们这里利用了CLOUDFLARE来作为错误证书颁发源,利用CF 接入域名,可以颁发15年的仅CF CDN网络体系承认的证书的功能。

大家可以使用我们已经生成的这张证书,反正只要域名不是你真实的域名就行了,提供如下。

公共证书(PEM格式)

-----BEGIN CERTIFICATE-----
MIIDITCCAsagAwIBAgIUTcEWLzynkLCFCoAC1iDH2vG3EkYwCgYIKoZIzj0EAwIw
gY8xCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQHEw1T
YW4gRnJhbmNpc2NvMRkwFwYDVQQKExBDbG91ZEZsYXJlLCBJbmMuMTgwNgYDVQQL
Ey9DbG91ZEZsYXJlIE9yaWdpbiBTU0wgRUNDIENlcnRpZmljYXRlIEF1dGhvcml0
eTAeFw0xOTAxMTMxNDMxMDBaFw0zNDAxMDkxNDMxMDBaMGIxGTAXBgNVBAoTEENs
b3VkRmxhcmUsIEluYy4xHTAbBgNVBAsTFENsb3VkRmxhcmUgT3JpZ2luIENBMSYw
JAYDVQQDEx1DbG91ZEZsYXJlIE9yaWdpbiBDZXJ0aWZpY2F0ZTBZMBMGByqGSM49
AgEGCCqGSM49AwEHA0IABAg/hZ9lDHj/f+0jDRAN23TkNEqIi46mCGnwZVD3glxL
l+a1mpfXLHSEFTipnSyQgmvkPYzQGaEIFD0q6W/ZgMujggEqMIIBJjAOBgNVHQ8B
Af8EBAMCBaAwHQYDVR0lBBYwFAYIKwYBBQUHAwIGCCsGAQUFBwMBMAwGA1UdEwEB
/wQCMAAwHQYDVR0OBBYEFCEZF6Eyem01XPbgwr6DXLZV1qsQMB8GA1UdIwQYMBaA
FIUwXTsqcNTt1ZJnB/3rObQaDjinMEQGCCsGAQUFBwEBBDgwNjA0BggrBgEFBQcw
AYYoaHR0cDovL29jc3AuY2xvdWRmbGFyZS5jb20vb3JpZ2luX2VjY19jYTAjBgNV
HREEHDAaggwqLmRuc3BvZC5jb22CCmRuc3BvZC5jb20wPAYDVR0fBDUwMzAxoC+g
LYYraHR0cDovL2NybC5jbG91ZGZsYXJlLmNvbS9vcmlnaW5fZWNjX2NhLmNybDAK
BggqhkjOPQQDAgNJADBGAiEAnrequCk/QZOOrcPH6C3Hgcy4SPNUy5rQtku/aYkj
qQoCIQCN6IyYNiXuwG+8jUgJrveiirBjiz2jXZSTEfVAyibjTg==
-----END CERTIFICATE-----

密钥(KEY)

-----BEGIN PRIVATE KEY-----
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgK0HE3hTJQDg6p/fj
nS92eSuRKZEZ5F4grT6tWFKNYVmhRANCAAQIP4WfZQx4/3/tIw0QDdt05DRKiIuO
pghp8GVQ94JcS5fmtZqX1yx0hBU4qZ0skIJr5D2M0BmhCBQ9Kulv2YDL
-----END PRIVATE KEY-----

把以上两个证书直接复制到默认站点的ssl选项中的其他证书里面,注意不要搞反了。

BT宝塔Nginx设置只允许域名访问禁止IP访问 SSL同步设置防源站泄漏插图2

4、修改站点配置

以上操作完成后,修改默认站点的配置文件

在后面新增一个返回代码:

return 444;

如下图示意

BT宝塔Nginx设置只允许域名访问禁止IP访问 SSL同步设置防源站泄漏插图3

这个时候,无论是通过http://IP访问,还是通过https://IP访问,都将返回无法访问的页面。如果用网上的漏洞扫描程序,扫到的这个IP的证书就是dnspod的了,这样你的IP就不会泄露。