OpenSSL自签证书并基于Express搭建Web服务进行SSL/TLS协议分析

OpenSSL自签证书并基于Express搭建Web服务进行SSL/TLS协议分析

起因

最近在学习安全协议,大多数实验都是基于Windows下IIS,或者Linux下nginx搭建的Web服务,搭建环境和编写配置文件比较麻烦。而且我有多个不同环境的设备,折腾起来也比较麻烦,最好是开箱即用。

所以我就用nodejs + express写了个跨平台的项目专门用于对比没有使用SSL/TLS的http与使用了的https协议。

如果你对代码不感兴趣,仅是想要分析协议,可以到文章末尾的Github仓库中去下载该项目运行即可。

搭建环境

安装nodejs,访问官网https://nodejs.org,建议下载LTS版本并安装

node --version
npm --version

运行上方命令出现版本号即可。

在终端中运行如下命令安装yarn

npm -g install yarn
yarn --version

出现版本号即为成功。

另外需要搭建OpenSSL环境,这个教程网上有很多,请自行搜索。

代码部分

接下来主要分享代码编写思路。

目录结构

创建项目目录为http_ssl,并在终端中进入

mkdir http_ssl
cd http_ssl

使用yarn初始化,按照提示填写或者一路回车即可

yarn init

创建静态资源目录、配置文件目录、源码目录

mkdir asset
mkdir config
mkdir src

安装依赖

添加express包等

yarn add express

package.json文件中修改typemodule,将语法为ES6,并编写启动命令scripts。(如果没有直接添加)

"type":"module",
"scripts":{"start":"node ./src/index.js"
}

完整文件大致如下

{"name": "http_ssl","version": "1.0.0","main": "index.js","author": "cairbin","license": "MIT","dependencies": {"express": "^4.19.2"},"type":"module","scripts":{"start":"node ./src/index.js"}
}

初始文件

我们希望http和https的端口号、主机地址之类的都能够放在配置文件里,而非代码中,这样方便我们更改,于是编写配置文件。

touch config/config.js

我们这里为了让配置文件能够像模块一样倒入,直接为js后缀,编辑该文件

// config/config.js
export default {server:{static: '../asset',		//静态资源目录,注意'../',因为index.js在src下,否则找不到这个目录http:{host: "localhost",	//主机地址port: 8848					//端口号},https:{host:'localhost',port:8849}}
}

创建src/index.js为程序入口,我们编写的启动命令就是运行这个文件的

touch src/index.js

然后编写一个404.html的静态文件

mkdir asset/html/
touch asset/html/404.html
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>404 Page</title>
</head>
<body><h1>404 NOT FOUND</h1>
</body>
</html>

辅助模块

项目还需要日志功能,我们没必要搞太复杂,想要功能强大的可以引入winston包,我这里就简单封装下js自带的console.*

mkdir src/utils/
touch src/utils/logger.js
// src/utils/logger.js
const log = (options)=>{console[options.type](`[${options.type.toUpperCase()}] ${options.msg}`)
}const info = (msg)=>{log({type:"info",msg:msg});
}const warn = (msg)=>{log({type:'warn',msg:msg});
}const error = (msg)=>{log({type:'error',msg:msg});
}export default{info,warn,error
}

控制器

尽管是个Demo,但最好还是遵循MVC,我们创建目录和文件,编写一个简单控制器

mkdir src/controller/
touch src/controller/defaultController.js
// src/controller/defaultController.js
import logger from './../utils/logger.js';	//日志模块const defaultFunc = (req, res, next)=>{logger.info('exec defultController.defaultFunc');res.status(500).send('Hello World!');	//为了抓包分析方便,返回500状态码而非200
}export default{defaultFunc
}

这里注意,为了抓包方便观察,我们返回的Code为500,而不是200。

路由

创建Express的路由

mkdir src/route/
touch src/route/defaultRoute.js
// src/route/defaultRoute.js
import express from "express";
import defaultController from './../controller/defaultController.js';
import logger from './../utils/logger.js';	//日志模块const router = express.Router();router.get('/',(req,res,next)=>{logger.info('defaultRoute, path=/');defaultController.defaultFunc(req, res, next);
})export default router;

创建服务

创建http和https服务,指定静态资源目录,设置404页面,并集成使用以上编写的文件

// src/index.js
import express from 'express';
import https from 'https';
import http from 'http';
import defaultRoute from './route/defaultRoute.js'
import fs, { copyFile } from 'fs';
import path from 'path';
import { dirname } from "node:path";
import { fileURLToPath } from "node:url"
import config from './../config/config.js';
import logger from './utils/logger.js';const __filename = fileURLToPath(import.meta.url)
const __dirname = dirname(__filename)const app = express();
const httpsServer = https.createServer({key:fs.readFileSync('keys/private.pem'),cert:fs.readFileSync('keys/file.crt')
},app)
const httpServer = http.createServer({},app);//static path
app.use(express.static(path.join(__dirname, config.server.static)));
logger.info(`static file path '${path.join(__dirname, config.server.static)}'`);app.use('/',defaultRoute);
// 404 page
app.use('*',(req,res)=>{res.redirect('./html/404.html')
})httpsServer.listen(config.server.https.port, config.server.https.host, ()=>{logger.info(`https service is running on https://${config.server.https.host}:${config.server.https.port}`);
})httpServer.listen(config.server.http.port, config.server.http.host, ()=>{logger.info(`http service is running on http://${config.server.http.host}:${config.server.http.port}`);
})

你会发现keys这个用于存放证书的目录以及证书文件并不存在,我们接下来创建它们项目才能运行。

另外需注意,上面的静态资源目录用path.join()来设为绝对路径。

生成证书

我们使用OpenSSL自签一个证书,在macOS或者Linux下可编写脚本

touch generate.sh
# generate.sh
mkdir -p keys
openssl genrsa 4096 > keys/private.pem
openssl req -new -key keys/private.pem -out keys/csr.pem
openssl x509 -req -days 365 -in keys/csr.pem -signkey keys/private.pem -out keys/file.crt

然后运行脚本

sh generate.sh

如果是Windows的话也可以编写.bat批处理文件,或者直接执行命令:

mkdir keys
openssl genrsa 4096 > keys/private.pem
openssl req -new -key keys/private.pem -out keys/csr.pem
openssl x509 -req -days 365 -in keys/csr.pem -signkey keys/private.pem -out keys/file.crt

注意旧版本的CMD或PowerShell可能不支持/,可以改用\,可能需要转义字符\\

运行项目

执行我们编写好的指令(package.json那里的)运行项目:

yarn run start

正常情况下,控制台应该是在报[INFO]

浏览器地址栏输入http://localhost:8848,即可看到Hello World!页面(最指定协议头,有些浏览器会自动填充为https)

https的话请访问https://localhost:8849,正常来讲现代的浏览器会阻止你访问这个页面,并提示非私人连接不安全,因为我们用的是自签名的证书。

直接无视风险,继续访问(doge)

抓包分析

关闭页面,在浏览器的设置里,清除浏览器缓存(这一步很重要,因为我们之前访问过页面了,浏览器为了加速一般都会有缓存导致下次访问时不是向服务器请求,而是加载本地缓存的页面,导致抓包失败)

启动wireshark并进行抓包,与其他情况不同,由于是回环地址,所以我们要抓loopback的包

先来访问http未加密的页面,在wireshark中使用过滤器过滤http的包,该项目为了排除抓包的时候的干扰,访问页面返回的状态码为500,而不是200,所以我们只需要找到http status 为500的包即可

查看数据,果然是明文

再以https协议的方式访问一次并进行抓包

发现抓不到了,我们将过滤器条件改为tls

由此看到数据都被保护起来了,即使抓到也都是密文。

Wireshark是直接看到TLS封包的内容的,我们可以配置环境变量,让浏览器得到的preMasterKey放到指定log中,wireshark支持读取这个文件,然后再进行抓包就可以查看加密的数据了。

文章可以参考 https://segmentfault.com/a/1190000018746027

代码仓库

Click CairBin/SecurityProtocolLab

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/697651.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

01-02-4

1、中级阶段-day1作业 使用的代码 #include<stdio.h> typedef struct Student {int num;char name[20];char sex; }Stu; int main() {Stu s;scanf("%d%s %c", &s.num, s.name, &s.sex);//读取字符串时&#xff0c;scanf()的占位符用%s即可&#xff0c…

【错误的集合】力扣python

最初想法 def findErrorNums(nums):n len(nums)duplicate -1missing -1for num in nums:if nums[abs(num) - 1] < 0:duplicate abs(num)else:nums[abs(num) - 1] * -1for i in range(n):if nums[i] > 0:missing i 1breakreturn [duplicate, missing] 遇到力扣大佬…

【自动驾驶技术栈学习】1-硬件《大话自动驾驶》| 综述要点总结 by.Akaxi

----------------------------------------------------------------------------------------------------------------- 致谢&#xff1a;感谢十一号线人老师的《大话自动驾驶》书籍&#xff0c;收获颇丰 链接&#xff1a;大话自动驾驶 (豆瓣) (douban.com) -------------…

GPU性能实时监控的几种软件

在深度学习服务器上&#xff0c;各种模型的训练&#xff0c;需要监控GPU的情况&#xff0c;并且需要根据使用状态来切换不同的GPU上。 有以下四款软件&#xff0c;可以很好的进行GPU状态监控。 1. nvidia-smi 一个跨平台工具&#xff0c;用于监控和管理NVIDIA GPU的状态和性…

【数据结构】栈和队列OJ面试题

20. 有效的括号 - 力扣&#xff08;LeetCode&#xff09; 思路&#xff1a;由于C语言没有栈的接口&#xff0c;所以我们需要自己造一个“模子”。我们直接copy之前的实现的栈的接口就可以了&#xff08;可以看我之前的博客【数据结构】栈和队列-CSDN博客copy接口&#xff09;&…

Linux进程间几种通信机制

一. 简介 经过前一篇文章的学习&#xff0c; 我们知道Linux下有两种标准&#xff1a;system V标准和 Posix标准。 System V 和 POSIX 标准是操作系统提供的接口标准&#xff0c;它们规定了操作系统应该如何实现一些基本功能&#xff0c;比如线程、进程间通信、文件处理等。 …

通过金山和微软虚拟打印机转换PDF文件,流程方法及优劣对比

文章目录 一、WPS/金山 PDF虚拟打印机1、常规流程2、PDF文件位置3、严重缺陷二、微软虚拟打印机Microsoft Print to Pdf1、安装流程2、微软虚拟打印机的优势一、WPS/金山 PDF虚拟打印机 1、常规流程 安装过WPS办公组件或金山PDF独立版的电脑,会有一个或两个WPS/金山 PDF虚拟…

校园志愿者管理系统带万字文档

文章目录 校园志愿者管理系统一、项目演示二、项目介绍三、10000字论文参考四、部分功能页面五、部分代码展示六、底部获取项目源码和万字论文参考&#xff08;9.9&#xffe5;带走&#xff09; 校园志愿者管理系统 一、项目演示 校园志愿者管理系统 二、项目介绍 基于Spring…

vue3中通过自定义指令实现loading加载效果

前言 在现代Web开发中&#xff0c;提升用户体验一直是开发者们追求的目标之一。其中&#xff0c;一个常见的场景就是在用户与应用程序进行交互时&#xff0c;特别是当进行异步操作时&#xff08;如网络请求&#xff09;&#xff0c;为用户提供即时的反馈&#xff0c;避免用户因…

利用香港多IP服务器进行大数据分析的潜在优势?

利用香港多IP服务器进行大数据分析的潜在优势? 在当今数据驱动的时代&#xff0c;大数据分析已经成为企业获取竞争优势的不二选择。而香港作为一个拥有世界级通信基础设施的城市&#xff0c;提供了理想的环境来部署多IP服务器&#xff0c;从而为大数据分析提供了独特的优势。…

[数据结构1.0]选择排序

鼠鼠前面的博客介绍过选择排序是常见的排序算法&#xff0c;选择排序有但不限于直接选择排序和堆排序&#xff01;那么鼠鼠今天浅谈一下选择排序&#xff01; 鼠鼠本博客用排升序来介绍选择排序&#xff01; 目录 1.直接选择排序 1.1.直接选择排序 1.2.直接选择排序特性 2…

20240513,常用算法(查找,排序,拷贝替换)

做着一些和考试无关的事情 常用查找算法——续 FIND_IF find_if //按条件查找元素&#xff0c;返回迭代器POS / END()find_if(beg,end,_Fred) _Fred函数或谓词&#xff08;返回BOOL类型的仿函数&#xff09; #include<iostream> #include<string> #includ…