博客 (231)

首先,禁止网站下所有 .php 等文件均不允许被访问到。

在 nginx 网站配置文件中,include enable-php-**.conf; 上方插入:

location ~ ^/.*\.(php|php5|py|sh|bash|out)$ { deny all; }

其中,^ 匹配开始,/.* 匹配所有目录和文件名,\.(php|php5|py|sh|bash|out) 匹配文件后缀名,$ 匹配结束。

即便如此,仍然忽略了 nginx 中 .php 文件名后加斜杠仍然能访问到的情况,譬如我们访问这个网址:

https://xoyozo.net/phpinfo.php/abc.html

nginx 仍然运行了 phpinfo.php,给了后门可趁之机,所以改进为:

location ~ ^/.*\.(php|php5|py|sh|bash|out)(/.*)?$ { deny all; }


Discuz! X3.4 需要写入权限的目录:

  • /自研目录/upload/

  • /config/

  • /data/

  • /uc_client/data/

  • /uc_server/data/

  • /source/plugin/

但,这些都不重要,我们已经禁止了所有目录的 .php 访问了。


第二步,解禁需要直接被访问到的文件路径。

Discuz! X3.4 根目录下的 .php 文件都是入口文件,需要能够被访问到:

/admin.php
/api.php
/connect.php
/forum.php
/group.php
/home.php
/index.php
/member.php
/misc.php
/plugin.php
/portal.php
/search.php

其它目录中需要被直接访问到的文件:

/archiver/index.php
/m/index.php
/uc_server/admin.php
/uc_server/avatar.php
/uc_server/index.php

部分插件文件需要能够被直接访问到:

/source/plugin/magmobileapi/magmobileapi.php
/source/plugin/smstong/accountinfo.php
/source/plugin/smstong/checkenv.php

另外如果有自建目录需要有 .php 访问权限,那么也需要在此处加白,本文以 /_/ 目录为例

最终拼成:工具

location ~ ^(?:(?!(/admin\.php|/api\.php|/connect\.php|/forum\.php|/group\.php|/home\.php|/index\.php|/member\.php|/misc\.php|/plugin\.php|/portal\.php|/search\.php|/archiver/index\.php|/m/index\.php|/uc_server/admin\.php|/uc_server/avatar\.php|/uc_server/index\.php|/source/plugin/magmobileapi/magmobileapi\.php|/source/plugin/smstong/accountinfo\.php|/source/plugin/smstong/checkenv\.php|/_/.*\.php))/.*\.(php|php5|py|sh|bash|out)(/.*)?)$ { deny all; }

这里用到正则表达式中的“不捕获”和“负向零宽断言”语法,格式为:

^(?:(?!(允许的目录或文件A|允许的目录或文件B))禁止的目录和文件)$

值得注意的是,此句负向零宽断言中的匹配内容是匹配前缀的,也就是说

https://xoyozo.net/index.php
https://xoyozo.net/index.php/abc.html

都可以访问到,而

https://xoyozo.net/forbidden.php
https://xoyozo.net/forbidden.php/abc.html

都访问不到,这是符合需求的。

如果自建目录 /_/ 下有写入需求,单独禁止即可,以 /_/upload/ 为例:

location ~ ^/_/upload/.*\.(php|php5|py|sh|bash|out)(/.*)?$ { deny all; }


ThinkPHP 网站有统一的访问入口,可按本文方法配置访问权限。


特别注意:上面的代码必须加在 PHP 引用配置(include enable-php-**.conf;)的上方才有效。


附:一键生成 nginx 访问控制 location URI { } 语句工具

xoyozo 3 年前
3,786

本文使用 ASP.NET 6 版本 Senparc.Weixin.Sample.MP 示例项目改造。


第一步 注册多公众号

方法一:打开 appsettings.json 文件,在 SenparcWeixinSetting 节点内添加数组节点 Items,该对象类型同 SenparcWeixinSetting。

//Senparc.Weixin SDK 设置
"SenparcWeixinSetting": {
  "IsDebug": true,
  "Token": "",
  "EncodingAESKey": "",
  "WeixinAppId": "",
  "WeixinAppSecret": "",
  "Items": [
    {
      "IsDebug": true,
      "Token": "a",
      "EncodingAESKey": "a",
      "WeixinAppId": "a",
      "WeixinAppSecret": "a"
    },
    {
      "IsDebug": true,
      "Token": "b",
      "EncodingAESKey": "b",
      "WeixinAppId": "b",
      "WeixinAppSecret": "b"
    }
  ]
}

方法二:修改 Program.cs 文件,在 UseSenparcWeixin 方法中注册多个公众号信息。

var registerService = app.UseSenparcWeixin(app.Environment,
    null /* 不为 null 则覆盖 appsettings  中的 SenparcSetting 配置*/,
    null /* 不为 null 则覆盖 appsettings  中的 SenparcWeixinSetting 配置*/,
    register => { /* CO2NET 全局配置 */ },
    (register, weixinSetting) =>
    {
        //注册公众号信息(可以执行多次,注册多个公众号)
        //register.RegisterMpAccount(weixinSetting, "【盛派网络小助手】公众号");
        foreach (var mp in 从数据库或配置文件中获取的公众号列表)
        {
            register.RegisterMpAccount(new SenparcWeixinSetting
            {
                //IsDebug = true,
                WeixinAppId = mp.AppId,
                WeixinAppSecret = mp.AppSecret,
                Token = mp.Token,
                EncodingAESKey = mp.EncodingAeskey,
            }, mp.Name);
        }
    });

完成后,我们可以从 Config.SenparcWeixinSetting.Items 获取这些信息。


第二步 接入验证与消息处理

打开 WeixinController 控制器,将构造函数改写为:

public WeixinController(IHttpContextAccessor httpContextAccessor)
{
    AppId = httpContextAccessor.HttpContext!.Request.Query["appId"];
    var MpSetting = Services.MPService.MpSettingByAppId(AppId);
    Token = MpSetting.Token;
    EncodingAESKey = MpSetting.EncodingAESKey;
}

示例中 Services.MPService.MpSettingByAppId() 方法实现从 Config.SenparcWeixinSetting.Items 返回指定 appId 的公众号信息。

为使 IHttpContextAccessor 注入生效,打开 Program.cs,在行

builder.Services.AddControllersWithViews();

下方插入

builder.Services.AddHttpContextAccessor();

这样,我们就可以在构造函数中直接获取地址栏中的 appId 参数,找到对应的公众号进行消息处理。


第三步 消息自动回复中的 AppId

打开 CustomMessageHandler.cs,将 

private string appId = Config.SenparcWeixinSetting.MpSetting.WeixinAppId;
private string appSecret = Config.SenparcWeixinSetting.MpSetting.WeixinAppSecret;

替换为:

private string appId = null!;
private string appSecret = null!;

并在构造函数中插入

appId = postModel.AppId;
appSecret = Services.MPService.MpSettingByAppId(postModel.AppId).WeixinAppSecret;

这样,本页中使用的 appId / appSecret 会从 postModel 中获取,而非默认公众号。postModel 已在 WeixinController 中赋值当前的 appId。

改造后,我们可以在 OnTextRequestAsync() 等处理消息的方法中可以判断 appId 来处理不同的消息。


第四步 其它功能和接口

其它功能和接口均可用指定的 AppId 和对应的 AppSecret 进行调用。

xoyozo 3 年前
4,555

IMG_1452.PNG

文件已正常部署且能正常访问到,但点击“文件已部署,立即认证”按钮时,然后被提示:访问文本资源失败,请调整后重试。原因是微信验证请求是 http 协议,如果网站强制启用 https 则无法完成验证。

在 ASP.NET 6 中可打开 Program.cs 文件,临时注释掉下行即可:

app.UseHttpsRedirection();


xoyozo 3 年前
3,829

在 Notepad++ 工具菜单上选择:插件 - 插件管理,搜索“HEX-Editor”,选中并点击安装。

image.png

然后点击:插件 - HEX-Editor - View In HEX,就可以查看了。

image.png


也可以用 Notepad-- 这款轻量级的国产软件来查看,打开文件,在菜单-模式-以二进制模式打开。

xoyozo 3 年前
8,432
[
  {
    "outputFileName": "wwwroot/js/Admin/Blog/List.js",
    "inputFiles": [
      "Areas/Admin/Views/Blog/List.cshtml.js"
    ]
  }
]

JS 源文件在视图目录,独立于 .cshtml 文件,又折叠于 .cshtml 文件。

输出目录在 wwwroot 中,若文件名不以“.min.js”结尾,那么会自动生成“.js”和“.min.js”两个文件,分别在开发模式和生产模式的视图中引用。

缺点:只更改 JS 内容,执行“生成解决方案”项目不会重新编译更新 wwwroot 中的 .js 和 .min.js 文件,这时使用“重新生成解决方案”可以解决这个问题。

xoyozo 3 年前
3,203

开发环境正常,发布到 IIS 报错

Could not load file or assembly 'Microsoft.EntityFrameworkCore.Relational, Version=6.0.x.0, Culture=neutral, PublicKeyToken=adb9793829ddae60'. 系统找不到指定的文件。

尝试在 UI 层安装 Pomelo.EntityFrameworkCore.MySql 无果。

最终,在发布时文件发布选项中,去掉“启用 ReadyToRun 编译”就正常了。

当然这种情况不是在所有启用了 ReadyToRun 编译的项目中遇到,但该项目改为不启用 ReadyToRun 后恢复正常了,原因暂时未知。

xoyozo 3 年前
3,270
  1. 项目 - 属性 - 生成 - 输出 - 文档文件,勾选“生成包含 API 文档的文件。”,“XML 文档文件路径”可留空。

    image.png

  2. 在配置方法 AddSwaggerGen 中添加以下两行

    var xmlFilename = $"{System.Reflection.Assembly.GetExecutingAssembly().GetName().Name}.xml";
    options.IncludeXmlComments(Path.Combine(AppContext.BaseDirectory, xmlFilename));

参:Swashbuckle 和 ASP.NET Core 入门 | Microsoft Docs

xoyozo 3 年前
3,077

普通的 nginx http 反向代理 https 时是需要配置证书的,但我们又不可能由源域名的证书,所以要使用 nginx 的 stream 模块。普通的 nginx 反向代理属于第七层代理,而 stream 模块是第四层代理,通过转发的 tcp/ip 协议实现高功能,所以不需要证书。

我们这里以使用宝塔安装的 nginx 为例,其实其他系统也是类似,只要找到编译的 nginx 的源码目录就行了

编译前先将已经安装的 nginx 文件进行备份

通过 ps 命令查看 nginx 文件的路径。以下所有步骤都以自身 nginx 路径为准

# ps -elf | grep nginx
# cd /www/server/nginx/sbin/
# cp nginx nginx.bak

然后查看当前 nginx 编译的参数

/www/server/nginx/sbin/nginx -V

将 ./configure arguents:之后的内容复制到记事本备用(备注:我们这里其实使用的是 Tengine-2.3.1,所以下面的编译参数可能跟普通 nginx 不是很一样)

内容如下:

--user=www --group=www --prefix=/www/server/nginx --add-module=/www/server/nginx/src/ngx_devel_kit --with-openssl=/www/server/nginx/src/openssl --add-module=/www/server/nginx/src/ngx_cache_purge --add-module=/www/server/nginx/src/nginx-sticky-module --add-module=/www/server/nginx/src/lua_nginx_module --with-http_stub_status_module --with-http_ssl_module --with-http_v2_module --with-http_image_filter_module --with-http_gzip_static_module --with-http_gunzip_module --with-ipv6 --with-http_sub_module --with-http_flv_module --with-http_addition_module --with-http_realip_module --with-http_mp4_module --with-ld-opt=-Wl,-E --with-pcre=pcre-8.42 --with-cc-opt=-Wno-error --add-module=/www/server/nginx/src/ngx-pagespeed

进入 src 目录

cd /www/server/nginx/src

我们在上面的内容中加入两个参数

./configure --user=www --group=www --prefix=/www/server/nginx --add-module=/www/server/nginx/src/ngx_devel_kit --with-openssl=/www/server/nginx/src/openssl --add-module=/www/server/nginx/src/ngx_cache_purge --add-module=/www/server/nginx/src/nginx-sticky-module --add-module=/www/server/nginx/src/lua_nginx_module --with-http_stub_status_module --with-http_ssl_module --with-http_v2_module --with-http_image_filter_module --with-http_gzip_static_module --with-http_gunzip_module --with-ipv6 --with-http_sub_module --with-http_flv_module --with-http_addition_module --with-http_realip_module --with-http_mp4_module --with-ld-opt=-Wl,-E --with-pcre=pcre-8.42 --with-cc-opt=-Wno-error --add-module=/www/server/nginx/src/ngx-pagespeed --with-stream --with-stream_ssl_preread_module

并执行它

然后

make && make install

重启 nginx

service nginx restart

nginx -V 看看模块是不是加载了

image.png

新建个站点,配置反向代理


卸载

使用 nginx.bak 文件替换掉自编译的 nginx 文件,替换后重启 nginx。

转自 笨牛网 3 年前
2,911

前提:

  • 监控摄像头有推流功能

  • 抖音有直播权限(粉丝数超过 1000)

  • 一个已备案的域名


流媒体服务器抖音直播流程.png

名词解释:

推流地址:由阿里云生成的接收摄像头视频流推送的地址。

播流地址:由阿里云生成可用于终端播放的视频流地址。


各环节功能解释:

摄像头:负责采集视频信息,并推送到阿里云。

阿里云视频直播服务:接收视频推流并分发到用户端(收费)。

OBS 软件:负责将视频流转化为可供直播伴侣调用的视频源。

直播伴侣:将视频源分发到短视频终端用户。


第一步:配置阿里云视频直播服务

1.1 在阿里云控制台中开通视频直播,在域名管理中添加域名。

image.png

这里需要添加两个域名,一个是推流域名,一个是播流域名。顾名思义,“推流”指从摄像头将流推送到阿里云,“播流”是用户播放视频流。

以添加域名推流域名“rtmp.xoyozo.net”和播流域名“live.xoyozo.net”为例:

image.png

然后按列表中的 CNAME 地址对域名作解析即可。

解析生效后,点击任意一个域名进行配置,将推流域名和播流域名相互绑定。成功后,可以在推流域名中的播流信息中看到播流域名,在播流域名的推流信息中也能看到推流域名。

1.2 生成推流地址/播流地址

在工具箱中点击地址生成器。

image.png

选择推流域名和播流域名,AppName 和 StreamName 任意填写,生成成功后获取以下地址:

image.png

注:鉴权串是按照鉴权规则生成的串,默认有效期 30 分钟,超出时间即中断推流。可以在域名管理中修改该值,或直接关闭 URL 鉴权。


第二步:在摄像机控制面板中配置推流域名

该步骤因摄像机的品牌和型号不同而有所差异。本文以 IP CAMERA 为例。

首先使用 IP 搜索工具在局域网上搜索摄像机,找到摄像机的 IP 地址。

image.png

在浏览器上打开,输入用户名和密码进入管理面板。

image.png

在“参数设置”-“网络设置”- RTMP Publish 中,将推流地址填入到“预定网址”中,点击“应用”。

image.png

刷新后查看“直播状态”和“直播网址”是否已生效。

若配置成功,可将播流地址在视频播放器中打开观看,或直接在 iPhone 或安卓手机中打开播流地址。


第三步:使用 OBS Studio 将直播流信号转换为虚拟摄像头信号

因抖音直播伴侣无法使用播流地址作为视频源进行直播输出,故使用 OBS 将播流信号转化为虚拟的本机摄像头信号源供直播伴侣调用。

下载安装 OBS Studio,添加一个“场景”:

image.png

然后添加一个“媒体源”:

image.png

去掉“本地文件”前面的勾,将播流地址填到“输入”框中:

image.png

确定后即可预览到直播内容:

image.png

拖动视频区域可调整输出范围。

点击“启动虚拟摄像机”。

提示:OBS 创建的虚拟摄像机默认会从物理麦克风和桌面系统音频拾音,请按需在“混音器”中配置。


第四步:OBS 使用 VLC 视频源(此步可略过)

若 OBS 直接添加媒体源不稳定,可以使用 VLC 视频源。

下载安装 VLC 播放器

在 OBS Studio 中添加来源,选择“VLC 视频源”:

image.png

点击“播放列表”右侧的“+”,选择“添加路径 /URL”:

image.png

填写播流地址并确定。


第五步:配置抖音直播伴侣使用 OBS 的虚拟摄像机信号源

下载安装抖音直播伴侣,在任意场景中添加素材,选择“摄像头”:

image.png

摄像头选择“OBS Virtual Camera”:

image.png

点击“开始直播”:

image.png


下一步:如何配置视频号/微博直播的推流直播监控摄像头画面

xoyozo 3 年前
9,883

SshNet 向一个已存在的文件 WriteAllText 的时候,是在原内容的基础上从第一个字节依次覆盖新内容。

因此,如果新内容的长度小于原内容,则最终的文件内容将是新内容与结尾部分原内容的结合。

若新内容的长度大于等于原内容,则不存在问题。

例:原文件内容:"abc",WriteAllText 的内容为:“de”,那么最终的文件内容是:“dec”。

xoyozo 4 年前
1,782