用最简单方法解决api接口安全问题,几乎无法破解

2019年1月01日 11:45

 

 场景描述

 
项目需要为第三方提供api服务接口。接口涉及到核心功能,如何保证接口安全。防止伪造身份、篡改数据?
 

思路

 
保障数据安全最好的方法,当然是加密了。无法解析内容,自然无法伪造,篡改。

可是使用https证书需要收费的。有其它方法么?

有的。

消息哈希认证(hmac)。
 
 

 算法描述

 
*  访问者
 
1. 当访问接口时, 将参数按key值排序,组成key1=value1&key2=value2&....&secret_key=...
2. 然后对上面结果做md5,生成签名sign
3. 将sign放到加入请求的参数
 
* 被访问者
 
密钥是被访问者提供了,它也有访问者的secret_key.
1.根据app_id查到secret_key
2.处理请求参数,按规则组成key1=value1&key2=value2....&secret_key=...
3.对上一步结果做md5,生成sign。比较两个sign,相等则身份验证通过
 

效果

 
1. 密钥只存在双方机器上,不可能被截取
2. 签名无法伪造,同样身份无法伪造、消息无法被篡改
 
使用了hmac认证,接口被破解基本是不可能的
 

python实现

 
import md5

app_id=123
secret_key="xxxxxxxx"

request_param = dict(
    key1="value1",
    key2="value2",
    key3="value3"
)

def sign():
    params = ["%s=%s" % (key, value) for key, value in sorted(request_param.items(), key=lambda item: item[0])]
    params.append("secret_key=%s" % secret_key)
    str_param = "&".join(params)
    print str_param
    md = md5.md5()
    md.update(str_param)
    return md.hexdigest()

if __name__ == '__main__':
    print(sign())
 
 
来源
 

 

Tags: web python
评论(55) 阅读(7639)

没有契合的数据库迁移工具,用pymysql实现一个

2019年1月01日 11:21

版本迭代少不了数据迁移,python有自己的数据库迁移工具migrate。如果有的是其它开发语言,或者没有契合的迁移工具。
 
怎么自己做一个?
 

环境说明

项目开发语言:java
数据库: mysql
迁移脚本: python
python工具包: pymysql
 

实现思路

 
目标是将老版本的数据转为新版本的数据。
 
1. show tables 查询所有的表  ---得到所有表名
2. 注意trancate table_name 保证新表干净  -- 清环境
3. python在执行sql后可以在游标的属性cursor.description中看到表的字段信息  -- 得到所有字段名、类型
4. 查询数据生成insert语句。版本之间有变化的做特殊处理  --数据映射
5. 每生成500条commit一次到新库   -- 批量commit
6. mysqldump一份数据,在开发环境做好测试,没问题就可以用了。
 

前方有坑

 
留意数据精度问题
 

示例

 
import pymysql

FROM_DB = dict(
    ip="xxxxxxxxxx",
    username="xxxx",
    password="xxxx",
    db_name= "db1"
)

TO_DB = dict(
    ip="xxxxxxxxxx",
    username="xxxx",
    password="xxxx",
    db_name="db2"
)


class WebDB:
    def __init__(self, ip, username, password, db_name='xxx'):
        self.ip = ip
        self.username = username
        self.password = password
        self.db_name = db_name
        self.conn = None
        self.cursor = None

    def __enter__(self):
        self.conn = pymysql.connect(self.ip, self.username, self.password, self.db_name)
        self.cursor = self.conn.cursor()
        return self

    def __exit__(self, exctype, excvalue, traceback):
        if self.cursor:
            self.cursor.close()

        if self.conn:
            self.conn.close()



class MigrateDB(object):
    def start(self):
        self.clean()
        self.import_data()

    def clean(self):
        with WebDB(**TO_DB)as db:
            db.cursor.execute("show tables;")
            for tables in db.cursor.fetchall():
                table_name = tables[0]
                db.cursor.execute("truncate %s;" % table_name)

    def import_data(self):
        with WebDB(**FROM_DB) as from_db:
            with WebDB(**TO_DB) as to_db:
                #所有表
                from_db.cursor.execute("show tables;")
                for from_tables in from_db.cursor.fetchall():
                    from_table_name = from_tables[0]
                    from_db.cursor.execute("select * from %s;" % from_table_name)
                    items = self.to_dict(from_db.cursor.fetchall(), from_db.cursor.description)
                    print("import table: %s" % from_table_name)
                    to_table_name = from_table_name
                    for i, item in enumerate(items):
                        # 需要映射转换的表,生成专门的sql
                        if from_table_name == "xxxx":
                            to_table_name = "xxxxx"
                            item = self.gen_insert_sql_for_xxx(item)

                        sql = self.gen_insert_sql(to_table_name, item)
                        to_db.cursor.execute(sql, args=item)
                        # 每500条commit一次
                        if i != 0 and i % 500 == 0:
                            to_db.conn.commit()
                    to_db.conn.commit()


    def to_dict(self, rows, description):
        """
        记录转为字典
        :param rows:
        :param description:
        :return:
        """
        for row in rows:
            item = dict()
            for i, field in enumerate(description):
                field_name = field[0]
                item[field_name] = row[i]
            yield item

    def gen_insert_sql(self, table_name, item):
        sql = "insert into %s(%s) values(%s)"
        keys = item.keys()
        key_str = ",".join(keys)
        value_str = ",".join(["%%(%s)s" % k for k, v in item.items()])

        return sql % (table_name, key_str, value_str)


if __name__ == "__main__":
    m = MigrateDB()
    m.start()
 
来源
 
 
 

评论(77) 阅读(4061)

nginx运行php的终极方法,可避免许多不必要的坑

2018年12月28日 00:56

 

 说明

 
php有许多开源项目比较好像,在自己的机器上用docker搭建了很多。
 
每个docker都是跑了个apache,每个docker里面都有apache。
 
这样好像有点浪费资源,而平时都是用nginx,所以研究了用nginx运行php
 
中间竟然碰到许多坑,而回过头来看,其实都很简单。
 

nginx运行php的原理

 
nginx不能解析php,要和php-fpm配置使用。nginx负责将php的请求转给php-fpm解析处理,然后返回响应结果
 

nginx运行php会遇到哪些坑

 
错误的提示有很多,最基本有"File not found"、"403"、"404"、"nginx no input file specified"等等
 
总之能试出许多错误。**而归结到底,只有一个根本原因“权限”**
 
而最好的方法是,直接把nginx和php-fpm设置为root用户运行
 

测试环境

 
|对象|版本|
|--|--|
| 操作系统 | fedora27 |
| nginx    | 1.12.1   |
| php-fpm  | php7     |
 
 

配置方法

 
* 安装nginx
 
yum install nginx
 
* 安装php-fpm
 
yum install php-fpm
经过上面两步, php-fpm会自动将nginx配置好,只要将php放到/usr/share/nginx/html目录就可以运行。但是我不想放在这个目录。
 
 
* 设置php-fpm以root用户运行
 
1.打开文件 /usr/lib/systemd/system/php-fpm.service


2.在ExecStart 最后面加上参数-R标识可以用root身份运行(php-fpm默认不能用root运行)
(有可能改了php-fpm.server文件不能运行,请运行systemctl reload重新加载)


[Unit]
Description=The PHP FastCGI Process Manager
After=syslog.target network.target

[Service]
Type=notify
ExecStart=/usr/sbin/php-fpm --nodaemonize -R
ExecReload=/bin/kill -USR2 $MAINPID
PrivateTmp=true

[Install]
WantedBy=multi-user.target


3.打开文件/etc/php-fpm.d/www.conf

4.将修改user和group为root
user=root
group=root
 
* 设置nginx以root身份运行
 
1.打开/etc/nginx/nginx.conf

2.将user改为root
 
* 配置自己的php项目
 
将下面内容放到/etc/nginx/conf.d/目录下
server {
        listen 8080;
        server_name  localhost;

        root  /home/xxxxx;      #改成自己的地址
        index  /_h5ai/public/index.php;   #改成自己的php首页
        include /etc/nginx/default.d/*.conf;    #使用已经配置好的location

        location / {
        }
}
 

排错方法

 
如果在配置过程中,遇到了错误。用什么方法调试呢?
 
* nginx调试方法
 
nginx的任务是把php的路径调对。可以先把php-fpm注释掉,专门调nginx,当能从浏览器下载php文件,说明配置正确。
 
* php-fpm调试方法
 
用命令运行php-fpm会有清晰的输出
 
/usr/sbin/php-fpm --nodaemonize -R
 

总结

 
nginx运行php总的说来很简单,由于权限问题,会导致遇到许多的坑。
 
实际很多情况下,都是给内部用,直接用root就可以。当然这样方法在正式环境不安全。
 
来源
 

 

Tags: php nginx php-fpm
评论(46) 阅读(2937)

frp内网穿透基本用法

2018年12月27日 23:55

 

思考评价

 
由于路由器推荐使用frp进行内网穿透,所以我稍微了解了下。
 
平时我更新喜欢用ssh进行内网穿透。似乎并没有必要使用frp。
 
更方便的方法是,直接用vpn将两台机器连载一起,也比端口穿透好多了。
 
一般说来它比较适合普通人,使用比较简单。
 
不打算深入研究,下面比较水
 

基本用法

 
* 下载
 
 
一般是amd64
 
* 解压
 
tar -zxvf xxx
 
* 启动server
 
./frps -c frps_full.ini
 
* server控制台
 
访问 http://server-ip:7500
 
* 配置说明
 
中文帮助
 
 

示例

 
* server
 
[common]
bind_addr=0.0.0.0
bind_port = 30202
token=密码
 
 
* client
[common]
server_addr = xxx
server_port = xx
token=密码

[web]
type = tcp
local_ip = 192.168.50.1
local_port = 99
remote_port=82
 
来源
 
 

Tags: linux vps
评论(3) 阅读(1941)

rclone报错NewFs: couldn't initialise SFTP: EOF

2018年12月27日 23:27

rclone将路由器挂载到vps,出现下面错误。
 
原因是路由的ssh不支持sftp。
[root@izwzxxxxxxxqre9xrhvz ~]# rclone lsd homedata:
2018/12/24 13:57:24 Failed to create file system for "homedata:": NewFs: couldn't initialise SFTP: EOF

来源

rclone报错NewFs: couldn't initialise SFTP: EOF

 

Tags: rclone
评论(11) 阅读(1445)

entware嵌入到移动硬盘,在路由器重启后不生效

2018年12月27日 23:14

环境说明

 
在路由器r6400上挂载了一个移动硬盘,然后在移动硬盘中安装了entware。
 
如果重启了路由器,entware会失效。如何让它在重启时生效呢?
 

 梅林r6400开机启动原理

 
路由器中的操作系统比较特殊
 
1. /etc/profile: 系统做了限制不能修改,所以不可以加脚本
 
2. /jffs/scripts目录: 里面的脚本名固定不可以随便修改,但是可以加自己的内容。
 
| 脚本名         | 作用                   |
|--|--|
| post-mount     | 在挂载磁盘后调用       |
| nat-start      | 在nat配置后调用        |
| services-start | 在系统服务启动后调用   |
| services-stop  | 在系统服务器停止前调用 |
| wan-start      | 在wan网卡启动后调用    |
 

设置entware开机生效

 
entware生效的方法很简单,只需要把u盘中的entware挂载到opt,它就会生效。
 
现在我们把在加到开机启动脚本中
 
打开/jffs/scripts/post-mount把下面内容加到文件末尾。
 
ln -s /tmp/mnt/sda1/entware  /tmp/opt
 

entware服务的开机启动

 
entware服务在/opt/etc/init.d/目录,将自己的shell加到这个目录就可以
 

entware启动参数说明

https://github.com/RMerl/asuswrt-merlin/wiki/User-scripts
 
来源
 

 

Tags: 路由器 vps
评论(7) 阅读(3768)

让梅林R6400支持sftp协议

2018年12月25日 02:29

说明

 
通常linux中ssh默认支持sftp,梅林路由器中的ssh是精简版,不支持sftp协议。
 
自带的配置不支持,是否有有办法呢?我们可以先装entware-setup.sh,然后用opkg安装sftp。
 
有了ssh和sftp,我们可以做很多事。
 
 

环境(前置条件)

 
  • * r6400路由器
  • * entware-setup.sh已安装好系统,可以使用opkg
  • * 路由器启动了ssh登录
 

 ssh登录到路由器

安装sftp

 
opkg install openssh-sftp-server
 

启动

 
sftp是由ssh触发运行,只需重启路由器,它会自动启动。看到sftp-server表示,它已经运行了
 
 
 

测试

 
使用xshell的xftp进行链接测试
 
 

 总结

 
让梅林支持sftp并不复杂,复杂的地方在于将entware-setup安装调好。
 
来源
 
 

 

Tags: 路由器 梅林 r6400
评论(7) 阅读(3872)

nextcloud搭建私有云盘的几点思考

2018年12月16日 01:29

 

为什么选择nextcloud?

 
国内网盘死的差不多了,剩下也是限制很多。想到搭一个自己的私有云。
 
 开源的网盘有owncloud, nextcloud等等,支持桌面、手机,多终端。试了一下,挺好用,速度很快,没有限制。接下来选择用哪个?经过了解nextcloud是owncloud的作者创建的一个分支。两者功能上基本相同,试用了之后,nextcloud更符合我个人的使用习惯,owncloud用起来挺别扭。
 
nextcloud官网在国内被墙,需要用科学工具访问。
 

搭建方法

 
使用docker-compose一键安装。一定要注意挂载磁盘用绝对路径。这个地方坑很多,折腾了我大半天。
 
 
#docker-compose.yml

version: "3"
services:
    mynextcloud:
        image: nextcloud
        ports:
            - 8080:80
        volumes:
        #一定要用绝对路径,否则会有很多,权限,创建不了目录,各种奇怪问题
          - /root/nextcloud/html:/var/www/html
          - /root/nextcloud/data:/var/www/html/data
        restart: always
 
 
 

基本用法

 
nextcloud功能很全,使用时完成没问题的。达到我的基本要求之后,我更关心的问题是。私有云盘放在哪里?
如何解决流量问题,存储问题?
 
 

第一个问题:放在哪里?

 
 
这个取决于使用者的需求,大部分情况下,我们当然希望是拥有最大的自由度,无论在哪里都可以访问。
 
所以要买个vps是少不掉的。(有vps就有公网ip,可以做很多事了)
 

第二问题:流量,存储怎么解决?

 
* 流量:流量可以买个固定宽带的vps,也可以买国外不限流量的vps,个人用也不会有太大流量,问题不大。
 
 
*  存储:比较麻烦的是存储,vps的存储一般都比较小。而且可能损坏,如果存了很多数据,丢了我也受不了。
 
如果将自己的移动硬盘挂上去就好了。所以解决办法是,在路由器上挂移动硬盘,并用内网穿透,挂到vps上给nextcloud用。容量可以有多大放多大,完全不是问题。
 

第三个问题:数据安全吗?

 
1. 传输安全,可加上https。
 
2. 数据存储安全,可以在存储时加密,nextcloud自带的功能。
 

第四个问题:有了vps还有必要搭云盘?

 
云盘能够将多终端,顺畅的衔接起来。虽然功能上vps都能用工具实现,但是使用易用性上比不上云盘好用。
 

第五个问题:买网盘会员不是更方便?

 
对一般人而言,买网盘会员更省事,又便宜。个人云盘有它没有的优点:
 
  * 个人隐私。网盘中的数据被监视了,这是毫不疑问地,个人云盘没这个问题。
  * 不用担心分享被和谐。
 
 

总结

 
解决方案:vps+路由器+移动硬盘+nextcloud
 
最佳解决方案:将系统嵌入到移动硬盘,然后再将移动硬盘,挂到智能路由器,然后将路由器端口映射到vps,内网和外网都可以访问。并且对vps的消耗最小。
 
 
来源
 

 

Tags: vps nextcloud
评论(2) 阅读(3399)

p2p内网穿透技术-udp打洞

2018年11月25日 16:47

 
 
在公司如何访问家里的电脑?实现方法有很多,其它p2p传输文件最快,能否实现?
 

udp打洞原理

 
假设有两台分别处于各自的私有网络中的主机:A和B;N1和N2是两个网络的NAT设备,分别拥有IP地址P1和P2;S是一个使用了一个众所周知的、从全球任何地方都能访问得到的IP地址的公共服务器

步骤一:A和B分别和S建立UDP连接;NAT设备N1和N2创建UDP转换状态并分配临时的外部端口号

步骤二:S检查UDP包,看A和B的端口是否是正在被使用的(否则的话N1和N2应该是应用了端口随机分配,这会让路由验证变得更麻烦)

步骤三:如果端口不是随机化的,那么A和B各自选择端口X和Y,并告知S。S会让A发送UDP包到P2:Y,让B发送UDP包到P1:X

步骤四:A和B通过转换好的IP地址和端口直接联系到对方的NAT设备;
 
 
 

udp打洞成功前提条件--非对称NAT

 
完全圆锥型NAT: 无条件转发
 
受限圆锥型NAT:  忽略掉其它主机的数据包(要求来源ip一直)
 
端口受限圆锥型NAT: 更进一步地要求源端口(上例中是 2727)必须跟之前发出的数据包的目的端口一致(要求来源ip和端口)
 
对称NAT: 丢弃报文,拒绝转发(每次连接端口会变)
 
对称NAT理论上可以使用端口预测,但是基本上用不了。现在国内几个宽带运行商都是使用对称NAT,所以udp打洞不可行。
 
记得之前上学的时候p2p共享软件很多,后来都销声匿迹,应该是被管控了。
 

socat测试udp打洞

 
下面是socat打洞的例子,试过在公司与家里之间打洞,用不了被现实了
 
https://blog.lilydjwg.me/2012/8/28/udp-hole-punching-experiment.35350.html
 

frp测试udp打洞

 
frp采用xtcp协议,试过同样用不了。stcp可以用,但是不是p2p,要经过中间转发。
 

检查nat类型?

 
#python直接用pynat包
pip install pynat
 

pynat

 
C:\Users\wyq>pynat
Network type: Symmetric NAT   #对称nat
Internal address: 192.168.50.169:54320
External address: xxx.xxx.xxx.xxx:61195
 

 内网穿透可行方法

方法   描述  实现条件 实际是否可行
p2p      udp打洞 非对称nat 不可行。宽带基本都是对称nat。除非在自己公网ip上提供p2p穿透
ddns 路由器使用有公网ip 宽带提供了公网ip 不可行。路由器基本没有公网ip
端口转发 将端口映射到公网ip 需要公网ip 可行
vpn      要求有公网ip节点,运行vpn服务端 需要公网ip 可行

 

参考来源

 

 

Tags: p2p udp vps
评论(77) 阅读(5649)

uwsgi: option is ambiguous: http

2018年11月25日 15:08

 
 
从官网上下载的uwsgi包含所有的插件。其它来源的uwsgi可能被人改过了。例如,为了减小体积,会删除uwsgi不必要的插件。
 

环境

 
alpine中运行uwsgi
 
 

示例:启动uwsgi提供http访问接口,出现错误

 
#报错的原因就是这个uwsgi没有http插件
~ # uwsgi --http :9090  --plugins http,python3
[uWSGI] getting INI configuration from /app/uwsgi.ini
uwsgi: option is ambiguous: http
getopt_long() error
 

看看官网下载的uwsgi

 
 

解决办法

 
#查找
/app # apk search uwsgi |grep http
uwsgi-router_http-2.0.17-r0
uwsgi-http-2.0.17-r0

#安装
apk add uwsgi-http   uwsgi-router_http
 

来源

 
 

Tags: uwsgi docker
评论(1) 阅读(3053)