检查网站的TLS版本

有时候需要知道某个网站支持的TLS的版本。现在SSL 2.0和SSL 3.0都已经被淘汰了。其中TLS 1.0,TLS 1.1,TLS 1.2是目前的的主流,相对也是安全的。主要看加密的算法。TLS 1.3是目前最新的协议版本,也是相对最安全的版本了。

通过网页查看

通过命令行

1
2
3
openssl s_client -connect www.baidu.com:443 -tls1_2
openssl s_client -connect www.baidu.com:443 -tls1_1
openssl s_client -connect www.baidu.com:443 -tls1

以上分别检查了tls1.2,tls1.1和tls1`。如果握手失败的话,那么就是不支持了。

NMAP

依赖于nmap

1
nmap --script ssl-enum-ciphers -p 443 baidu.com

结果如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
Starting Nmap 7.91 ( https://nmap.org ) at 2020-11-29 09:51 CST
Nmap scan report for baidu.com (39.156.69.79)
Host is up (0.0068s latency).
Other addresses for baidu.com (not scanned): 220.181.38.148

PORT STATE SERVICE
443/tcp open https
| ssl-enum-ciphers:
| SSLv3:
| ciphers:
| TLS_ECDHE_RSA_WITH_RC4_128_SHA (secp256r1) - C
| TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA (secp256r1) - A
| TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA (secp256r1) - A
| TLS_RSA_WITH_AES_128_CBC_SHA (rsa 2048) - A
| TLS_RSA_WITH_AES_256_CBC_SHA (rsa 2048) - A
| TLS_RSA_WITH_RC4_128_SHA (rsa 2048) - C
| compressors:
| NULL
| cipher preference: server
| warnings:
| Broken cipher RC4 is deprecated by RFC 7465
| CBC-mode cipher in SSLv3 (CVE-2014-3566)
| TLSv1.0:
| ciphers:
| TLS_ECDHE_RSA_WITH_RC4_128_SHA (secp256r1) - C
| TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA (secp256r1) - A
| TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA (secp256r1) - A
| TLS_RSA_WITH_AES_128_CBC_SHA (rsa 2048) - A
| TLS_RSA_WITH_AES_256_CBC_SHA (rsa 2048) - A
| TLS_RSA_WITH_RC4_128_SHA (rsa 2048) - C
| compressors:
| NULL
| cipher preference: server
| warnings:
| Broken cipher RC4 is deprecated by RFC 7465
| TLSv1.1:
| ciphers:
| TLS_ECDHE_RSA_WITH_RC4_128_SHA (secp256r1) - C
| TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA (secp256r1) - A
| TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA (secp256r1) - A
| TLS_RSA_WITH_AES_128_CBC_SHA (rsa 2048) - A
| TLS_RSA_WITH_AES_256_CBC_SHA (rsa 2048) - A
| TLS_RSA_WITH_RC4_128_SHA (rsa 2048) - C
| compressors:
| NULL
| cipher preference: server
| warnings:
| Broken cipher RC4 is deprecated by RFC 7465
| TLSv1.2:
| ciphers:
| TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (secp256r1) - A
| TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 (secp256r1) - A
| TLS_ECDHE_RSA_WITH_RC4_128_SHA (secp256r1) - C
| TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA (secp256r1) - A
| TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA (secp256r1) - A
| TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (secp256r1) - A
| TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384 (secp256r1) - A
| TLS_RSA_WITH_AES_128_GCM_SHA256 (rsa 2048) - A
| TLS_RSA_WITH_AES_256_GCM_SHA384 (rsa 2048) - A
| TLS_RSA_WITH_AES_128_CBC_SHA256 (rsa 2048) - A
| TLS_RSA_WITH_AES_128_CBC_SHA (rsa 2048) - A
| TLS_RSA_WITH_AES_256_CBC_SHA256 (rsa 2048) - A
| TLS_RSA_WITH_AES_256_CBC_SHA (rsa 2048) - A
| TLS_RSA_WITH_RC4_128_SHA (rsa 2048) - C
| compressors:
| NULL
| cipher preference: server
| warnings:
| Broken cipher RC4 is deprecated by RFC 7465
|_ least strength: C

Nmap done: 1 IP address (1 host up) scanned in 3.22 seconds

Scrapy中Request()、FormRequest()、FormRequest.from_response()的小结

一、Request()类的实例化
对于爬取普通网站,不需要验证码,不需要登入的界面,我们一般用scrapy.Request类直接去爬取信息就行,下面是Request类的定义:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Request(object_ref):

def __init__(self, url, callback=None, method='GET', headers=None, body=None,
cookies=None, meta=None, encoding='utf-8', priority=0,
dont_filter=False, errback=None, flags=None):
# url是要爬取的网址
# callback是回调函数
# method是请求的方式post还是get
# headers是浏览器伪装的头信息
# body是网页源代码信息
# cookies是登入某网站后,网站在你电脑上保留的信息
# meta要携带或者传递的信息
# encoding是编码方式
# priority用来设置访问网站的优先级
# dont_filter是否允许重复爬取网站

可以看见,其中url,即要爬取的目标网站是必填的,其他的是选填的,method=‘GET’,说明此类是get请求,实例化该对象后,会得到一个response。

1
2
3
4
def start_requests(self):
# 首先爬一次登陆页,看是否有验证码。回调函数为parse(),'cookiejar'的值为1表示开启状态
# 这里要返回一个可迭代对象,用yield本身就是可迭代对象,但是,这里是return,所以用[]将其构造成一个可迭代对象
return [Request("https://accounts.douban.com/passport/login", meta={'cookiejar': 1}, headers=self.header, callback=self.parse)]

二、scrapy.FormRequest与scrapy.http.FormRequest

(1)普通请求使用scrapy.Request类就可以实现,但是遇到模拟表单或Ajax提交post请求的时候,Request类就不如 子类 FormRequest类方便了,因为他自带 formdata ,专门用来设置表单字段数据,即填写账号、密码,实现登入,默认method也是POST。
(2)FormRequest相当于是手动指定post。
(3)事实上,scrapy.FormRequest()与scrapy.http.FormRequest()使用起来的区别不大,你可以将两种方法等价互换。

1
2
3
4
5
6
7
def start_requests(self):
form_data = {'f1':'1', 'f2':'100'} # 表单数据,字典格式,注意数字也要用引号引起来,否则报错。
yield scrapy.FormRequest(url, formdata=form_data) # 还可以通过callback修改回调函数等
# 也可以
def start_requests(self):
form_data = {'f1':'1', 'f2':'100'} # 表单数据,字典格式,注意数字也要用引号引起来,否则报错。
yield scrapy.http.FormRequest(url, formdata=form_data) # 还可以通过callback修改回调函数等

三、FormRequest.from_response()

(1)FormRequest.from_response也可以进行设置formdata,用来填写并提交表单,实现模拟登入。
(2)FormRequest.from_response相当于是自动识别post。
(3)FormRequest.from_response与FormRequest.http.from_response也没有区别。

1
2
3
4
5
6
def parse(self, response):
return scrapy.FormRequest.from_response(
response,
formdata={'username': 'john', 'password': 'secret'},
callback=self.after_login
)

四、scrapy.FormRequest与FormRequest.from_response 的区别

1.什么情况下分别使用什么

先找到填写表单时发送的post请求的地址,下面以豆瓣登入为例子:

可以看见其post的地址为:https://accounts.douban.com/j/mobile/login/basic
然后用浏览器访问https://accounts.douban.com/j/mobile/login/basic

  1. 发现页面没有表单信息(也就是没有填写账号、密码的地方),所以,我们只能采用scrapy.FormRequest,手动的去发送post请求。
  2. 如果浏览器访问某post网址时,里面有表单信息,这时候你可以用FormRequest.from_response也可以用scrapy.FormRequest实现模拟登入

2.参数的不一样

scrapy.FormRequest的必填参数是目标网址,而FormRequest.from_response的必填参数是response

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
def parse(self, response):
# 如果有验证码
# 没有验证码,就进行登入
# 设置要发送的post信息
data = {
'name': '18371417971',
'password': 'wanghao211',
'remember': 'false'
}
print("登入中...")
return FormRequest(
url='https://accounts.douban.com/j/mobile/login/basic',
method='post',
formdata=data,
meta={'cookiejar':response.meta['cookiejar']},
# 如果需要多次提交表单,且url一样,那么就必须加此参数dont_filter,防止被当成重复网页过滤掉了
dont_filter=True,
callback=self.next
)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
def parse(self, response):
# 如果有验证码
# 没有验证码,就进行登入
# 设置要发送的post信息
data = {
'name': '18371417971',
'password': 'wanghao211',
'remember': 'false'
}
print("登入中...")
return FormRequest(
url=response,
method='post',
formdata=data,
meta={'cookiejar':response.meta['cookiejar']},
# 如果需要多次提交表单,且url一样,那么就必须加此参数dont_filter,防止被当成重复网页过滤掉了
dont_filter=True,
callback=self.next
)

3.小结

总的来说,scrapy.FormRequest的功能更加强大,如果FormRequest.from_response 不能解决就用scrapy.FormRequest来解决模拟登入,毕竟是手动设置目标网址,比自动识别要精准。

Docker修改Docker0网桥默认网段

Docker 服务启动后默认会创建一个 docker0 网桥(其上有一个 docker0 内部接口),它在内核层连通了其他的物理或虚拟网卡,这就将所有容器和本地主机都放到同一个物理网络。

Docker 默认指定了 docker0 接口 的 IP 地址和子网掩码,让主机和容器之间可以通过网桥相互通信,它还给出了 MTU(接口允许接收的最大传输单元),通常是 1500 Bytes,或宿主主机网络路由上支持的默认值。这些值都可以在服务启动的时候进行配置。

  1. 默认docker0 网桥信息
    默认的docker0网桥ip为“172.17.0.1”,尤其是在自建机房和办公环境在一起的情况下,会与办公网络发生冲突,所以我们在使用docker的时候需要修改调整默认的网桥信息。

  2. 修改步骤
    修改文件vim /etc/docker/daemon.json(这里没有这个文件的话,自行创建)

    1
    2
    3
    {
    "bip":"10.120.0.1/16"
    }
  3. 关闭docker服务

    1
    systemctl stop docker
  4. 再启动docker服务器

    1
    systemctl start docker
  5. 再重新看网段

xpath获取内容的方式

以百度为例:’https://tieba.baidu.com'

1.获取最外面标签,遍历内部所有的字标签,获取标签文本

content_list =div.xpath(‘.//div[@class=”d_post_content j_d_post_content “]/text()’).extract()

2.正则去掉所有标签 <.*?> re.compile.sub()

content_list=div.xpath(‘.//div[@class=”d_post_content j_d_post_content “]’)

pattern=re.compile(r(‘<.*?>’),re.S)

content=pattern.sub(‘’,content_list)

3./text() 获取标签的文本 //text()获取标签以及子标签的文本

content_list = div.xpath(‘.//div[@class=”d_post_content j_d_post_content “]//text()’).extract()

4 使用xpath(‘string(.)’)这种方式获取所有文本 并且拼接

content_list=div.xpath(‘.//div[@class=”d_post_content j_d_post_content “]’).xpath(‘string(.)’).extract()[0]+’\n’

文本内容获取之后print(content_list)查看内容,如需处理格式,则如下:

remove = re.compile(‘\s’)
content = ‘’
for string in content_list:
string = remove.sub(‘’,string)
content += string
print(content)

————————————————
版权声明:本文为CSDN博主「sklsxy」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/sklsklsklsxy/article/details/81448478

Nginx配置禁止IP访问

在配置安装好Nginx之后,打开浏览器直接输入ip地址是可以直接访问项目的,或者只要有域名指向ip可以直接用域名进行访问。
但是现在要求禁止使用ip地址直接访问,打开文件/etc/nginx/nginx.conf,在server上面添加代码

1
2
3
4
5
6
7
server {
listen 80 default_server;
listen [::]:80 default_server;
server_name _;

return 404;
}

并将原server中的两个default_server去掉

代表的意思是所有除了已经配置的server_name可以访问外,其他地址返回404并断开连接,也可以返回其他状态码。

Docker中设置Tomcat的内存大小配置

安装在docker中的tomcat,在下载大文件或者某些情况下,会出现tomcat的内存溢出等情况,所以需要配置tomcat的内存大小,docker中的tomcat内存大小配置有两种方式:

一、挂载docker中的配置文件

1
通过将docker中tomcat的catalina.sh配置文件挂载到宿主机中,然后再catalina.sh配置jvm内存大小即可。
  1. 需要重新运行一个tomcat容器(注意: 端口和容器名称不能重复)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    docker run -d \
    -v /server/webapps:/usr/local/tomcat/webapps/ \
    -v /server/catalina.sh:/usr/local/tomcat/bin/catalina.sh \
    -v /server/logs/demo:/server/logs/demo \
    -e TZ="Asia/Shanghai" \
    --privileged=true \
    --name demo \
    -p 8080:8080 \
    tomcat8

说明:

  • -v /server/webapps:/usr/local/tomcat/webapps/ 挂载当前项目的运行目录到tomcat下的webapps
  • -v /server/catalina.sh:/usr/local/tomcat/bin/catalina.sh 挂载宿主机的catalina.sh到docker中tomcat下的catalina.sh
  • -v /server/logs/demo:/server/logs/demo 挂载日志到宿主机中
  • -e TZ=”Asia/Shanghai” 设置时区
  • –privileged=true 设置拥有容器中的真正的root权限
  • -p 8080:8080 映射端口
  • tomcat8 镜像的名称

2 .catalina.sh中的配置内存

cygwin=false下面加入:

1
JAVA_OPTS="-server -Xms1024m -Xmx2048m -XX:MaxNewSize=256m -XX:PermSize=128m -XX:MaxPermSize=256m"

-xms 初始化堆内存
-xmx 最大堆内存

二、直接修改tomcat容器的catalina.sh的配置

1
可以直接进入容器中修改tomcat的catalina.sh的配置文件,不过该方法不推荐,因为以后再次秀挂比较麻烦,并且不方便配置,如果需要重新运行容器的时候,不知道容器中的内存大小的配置

进入容器
docker exec -it <container_name> /bin/bash

找到catalina.sh配置文件, 并增加配置(第一步中增加的配置语句即可)

// 如果没有vi 命令,则需要安装
vi /usr/local/tomcat/bin/catalina.sh
// 退出容器,并重新启动
exit
docker restart <containner_name>

注意: 如果修改Catalina.sh的时候提示该文件是一个只读文件或者提示没有权限修改,那么则需要以root权限进入容器,具体的方法见: Docker容器中的root权限获取

参考资料

升级ShadowSocks支持aes-256-gcm算法(ShadowsocksX-NG)

以前安装的Shadowsocks是2.8.2 不支持aes-256-gcm模式,需要升级到3.x才可以。

1
2
pip install --upgrade pip
sudo pip install https://github.com/shadowsocks/shadowsocks/archive/master.zip -U

然后修改/etc/shadowsocks.conf配置文件算法为aes-256-gcm。

下载最新的ShadowsocksX-NG客户端进行配置即可。

在一台新的政务云服务器上的部署操作

  1. 安装vim
    yum install vim

  2. 关闭系统自带防火墙,安装iptables
    systemctl stop firewalld
    systemctl disable firewalld
    vi /etc/selinux/config
    yum install iptables-services iptables-devel
    开启
    systemctl enable iptables.service
    systemctl start iptables.service
    systemctl enable iptables.service
    关闭firewall

  3. 安装nginx
    yum install epel-release
    yum install nginx

  4. 安装docker

先卸载旧版本的docker
sudo yum remove docker \
docker-client \
docker-client-latest \
docker-common \
docker-latest \
docker-latest-logrotate \
docker-logrotate \
docker-engine

安装新的docker
yum install -y yum-utils
yum-config-manager \
–add-repo \
https://download.docker.com/linux/centos/docker-ce.repo
yum install docker-ce-18.09.6 docker-ce-cli-18.09.6 containerd.io

升级docker
yum -y upgrade docker-ce docker-ce-cli containerd.io

systemctl start docker
systemctl enable docker

  1. 安装kk
    docker pull keking/kkfileview

  2. 安装minio
    docker pull minio/minio

kkfilepreview nginx redis
anaconda

  1. 解决docker中控制台乱码问题
    1
    docker run --restart=always -d --name zzb10001 -p 10001:8080 -e "LC_ALL=zh_CN.UTF-8" -e "LANG=zh_CN.UTF-8" -v /data/zzb10001/webapps:/tomcat/webapps/ -v /data/zzb10001/logs:/tomcat/logs spdk/jdk8 > /data/zzb10001/id

SSL Pinning on Android

简介

众所周知,我们在做手机端开发的时候,网络访问如果不做加密的话,请求数据很容易被抓包工具获取,从而造成安全隐患。所以我们怎么避免数据被别人通过抓包工具抓取呢?

SSL Pinning不是一个新概念,尽管很多开发者使用SSL来保证数据传输的安全性,但基本上都是未设置受信任证书,所以无法防止中间人攻击,数据还是可以被抓包。

证书锁定(SSL/TLS Pinning)顾名思义,将服务器提供的SSL/TLS证书内置到移动端开发的APP客户端中,当客户端发起请求时,通过比对内置的证书和服务器端证书的内容,以确定这个连接的合法性。

方法一

获取服务器证书摘要

创建一个脚本文件,certs.sh,文件内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
#!/bin/bash
certs=`openssl s_client -servername $1 -host $1 -port 443 -showcerts </dev/null 2>/dev/null | sed -n '/Certificate chain/,/Server certificate/p'`
rest=$certs
while [[ "$rest" =~ '-----BEGIN CERTIFICATE-----' ]]
do
cert="${rest%%-----END CERTIFICATE-----*}-----END CERTIFICATE-----"
rest=${rest#*-----END CERTIFICATE-----}
echo `echo "$cert" | grep 's:' | sed 's/.*s:\(.*\)/\1/'`
echo "$cert" | openssl x509 -pubkey -noout |
openssl rsa -pubin -outform der 2>/dev/null |
openssl dgst -sha256 -binary | openssl enc -base64
done

使用方法如下,可以看到获取的证书摘要信息:

1
2
3
4
5
6
# ./certs.sh www.jllydj.gov.cn

/CN=www.jllydj.gov.cn
2o2MOiraYxPymc3kSEYyLx8IbdlToM0D5Je9PK/lSlk=
/C=US/O=DigiCert Inc/OU=www.digicert.com/CN=Encryption Everywhere DV TLS CA - G1
GI75anSEdkuHj05mreE0Sd9jE6dVqUIzzXRHHlZBVbI=

通过okhttp设置CertificatePinner
1
2
3
4
5
6
7
CertificatePinner certPinner = new CertificatePinner.Builder()
.add("www.jllydj.gov.cn",
"sha256/2o2MOiraYxPymc3kSEYyLx8IbdlToM0D5Je9PK/lSlk=")
.add("www.jllydj.gov.cn",
"sha256/GI75anSEdkuHj05mreE0Sd9jE6dVqUIzzXRHHlZBVbI=")
.build();
builder.certificatePinner(certPinner);

方法二

Android okHttp 如何实现SSL Pinning
  • 加载可信任的Ca证书

    1
    2
    3
    4
    CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");

    InputStream caInput = getResources().openRawResource(R.raw.ca);
    Certificate ca = certificateFactory.generateCertificate(caInput);
  • 通过Ca创建一个包含可信任证书的keystore文件

    1
    2
    3
    4
    String keyStoreType = KeyStore.getDefaultType();
    KeyStore keyStore = KeyStore.getInstance(keyStoreType);
    keyStore.load(null, null);
    keyStore.setCertificateEntry("ca", ca);
  • Create a custom TrustManager from the trusted CAs in the keystore

    1
    2
    3
    String algorithm = TrustManagerFactory.getDefaultAlgorithm();
    TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(algorithm);
    trustManagerFactory.init(keyStore);
  • 设置到okhttp中

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    // Install the all-trusting trust manager
    final SSLContext sslContext = SSLContext.getInstance("SSL");
    sslContext.init(null, trustManagerFactory.getTrustManagers(), null);
    // Create an ssl socket factory with our all-trusting manager
    final SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
    builder.sslSocketFactory(sslSocketFactory, (X509TrustManager) trustManagerFactory.getTrustManagers()[0]);
    builder.hostnameVerifier(new HostnameVerifier() {
    @Override
    public boolean verify(String hostname, SSLSession session) {
    return true;
    }
    });

这样就完成了android客户端的SSL PINning设置。

问题

  • 方法一存在的问题
    服务器SSL证书到期前,测试环境更换证书,在更换配置OK后,发现APP停止服务了。所有的请求全部都失败,也就是SSL-PINNING证书到期后会导致APP拒绝服务。

  • 改进:
    可以将证书的publicKey部分提取到客户端硬编码,每次建立TLS连接时候,匹配公钥。
    服务器在更换续费证书时不要更换私钥就行了。推荐需要实现防用户自己抓包的APP采取本方案。

常用SSLpinning绕过方法以及原理介绍

在前面我们已经介绍了SSL pinning的原理以及具体的实现,那么我们想要绕过SSL pinning的话,只需要将证书校验的过程绕过就可以。那么我们可以通过hook的方式来修改证书校验过程,这样就能成功的绕过SSL pinning了。

本人常用且比较熟悉的hook工具有两种,分别是frida和Xposed,这两种工具中都有hook所有https证书校验方法的模块。

其中我使用的比较多的是基于Xposed框架下的Justtrustme模块。该模块对上述所说的https实现证书校验的过程都添加了hook代码,至于具体代码细节我这里就不详细说明了,感兴趣的话可以自己去查看源码

也可以使用国内大神写的太极框架,太极 阳是不需要root的,太极 magic是需要root的。太极框架兼容Xposed框架。https://www.taichi-app.com/#/index

参考资料