若水斋 若水斋:Werner的个人博客,关注信息安全 https://blog.werner.wiki zh-cn me@werner.wiki 2018-05-06 编写一个简单的MariaDB认证插件 https://blog.werner.wiki/write-a-simple-mariadb-auth-plugin/ https://blog.werner.wiki/write-a-simple-mariadb-auth-plugin/ Werner 2018-05-06 概述

不知从哪天起,大家都不用Mysql转而使用MariaDB了。

众所周知(其实可能很多人不知道)MariaDB支持插件认证。在MariaDB中新建用户,常见的语句是:

CREATE USER 'username'@'host' IDENTIFIED BY 'password';

这样创建的用户,登录时的认证方式是密码。其实创建用户的语句还可以是:

CREATE USER 'username'@'host' IDENTIFIED VIA 'pluginname' USING 'authstring';

这样创建的用户,登录时的认证方式由插件决定。

本文展示了编写一个简单的MariaDB认证插件的全过程。实现的认证机制是用户输入正确的姓名学号即可登录。显然这一认证机制毫无安全性可言,本文重点在于展示插件编写过程。

本文内容基于MariaDB-10.1.8,操作系统是Ubuntu12.04。假设已经安装好了数据库。

基本原理

一个认证插件分为两部分,服务器侧和客户端侧,两者配合,才能完成整个认证过程。最常见的认证情景是服务器侧提问,客户端侧回答。

MariaDB提供了一个通用的客户端侧“dialog”,该客户端侧的功能是接收服务器侧的问题,将问题显示在终端上,并在终端上读取待登录用户的回答,之后将回答发送给服务器侧。它支持不限个数的问答,还支持普通问题和密码问题两种问题,普通问题在待登录用户输入回答时是有回显的,密码问题在待登录用户输入回答时是没有回显的。由于最后一个问题需要特殊处理,所以实际上有四种类型的问题。问题字符串的第一个字节是问题类型,宏定义如下:

    /* mysql/auth_dialog_client.h */
    #define ORDINARY_QUESTION       "\2"
    #define LAST_QUESTION           "\3"
    #define PASSWORD_QUESTION       "\4"
    #define LAST_PASSWORD           "\5"

由于我们想要编写一个简单的认证插件,所以简单起见,客户端侧就使用“dialog”,完全满足要求。这样,我们便只用编写服务器侧部分。

服务器侧部分要做的事情便是与客户端侧的“dialog”通讯,读取输入的姓名学号进行验证。具体实现见下节。

编写代码

套路部分

认证插件的套路如下:

    #include <string.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <mysql/plugin_auth.h>
    #include <mysql/auth_dialog_client.h>

    static int school_number_auth(MYSQL_PLUGIN_VIO *vio, MYSQL_SERVER_AUTH_INFO *info)
    {
        /* 该函数是实际上进行认证的地方,
           认证通过返回CR_OK,
           认证失败返回CR_ERROR; */
    }

    static struct st_mysql_auth my_auth_plugin=
    {
        MYSQL_AUTHENTICATION_INTERFACE_VERSION, // 插件的接口版本号
        "dialog", // 客户端侧处理函数,我们直接使用了“dialog”,也可以自定义
        school_number_auth // 服务器侧处理函数
    };

    mysql_declare_plugin(dialog)
    {
        MYSQL_AUTHENTICATION_PLUGIN, // 插件类型
        &my_auth_plugin, // 插件结构体指针
        "school_number", // 插件名
        "Werner", // 作者
        "A simple MariaDB auth plugin", // 描述
        PLUGIN_LICENSE_GPL, // 许可证书
        NULL,
        NULL,
        0x0100,
        NULL,
        NULL,
        NULL,
        0,
    }
    mysql_declare_plugin_end;

mysql_declare_plugin声明了一个插件,其中写明了插件名、插件类型、作者、描述和许可证书等信息, 最重要的是插件结构体指针“&my_auth_plugin”。

插件结构体指针“&my_auth_plugin”指向插件结构体“my_auth_plugin”,该结构体中写明了客户端侧处理函数和服务器侧处理函数。在我们编写的插件中,客户端侧处理函数直接写字符串"dialog",表示使用MariaDB提供的通用客户端侧“dialog”,服务器侧处理函数school_number_auth是实际上进行认证的地方,认证通过返回CR_OK,认证失败返回CR_ERROR。CR_OK和CR_ERROR宏定义如下:

    /* mysql/plugin_auth_common.h */
    #define CR_ERROR 0
    #define CR_OK -1

我们只需要完善函数school_number_auth即可。

认证部分

在这一小节中,我们将完善函数school_number_auth。

首先看该函数的两个参数“MYSQL_PLUGIN_VIO *vio”和“MYSQL_SERVER_AUTH_INFO *info”。

“MYSQL_PLUGIN_VIO”中的“VIO”的含义是虚拟输入输出,它的定义如下所示:

    /* mysql/plugin_auth.h.pp */
    typedef struct st_plugin_vio
    {
      int (*read_packet)(struct st_plugin_vio *vio,
                         unsigned char **buf);
      int (*write_packet)(struct st_plugin_vio *vio,
                          const unsigned char *packet,
                          int packet_len);
      void (*info)(struct st_plugin_vio *vio, struct st_plugin_vio_info *info);
    } MYSQL_PLUGIN_VIO;

可以看到它是一个结构体,成员都是函数指针。

顾名思义,函数*read_packet是虚拟的读,从vio中读取以“\0”结尾的字符串,返回读取到的字符串长度。这个读操作是阻塞读。

*write_packet是虚拟的写,向vio中写入一个字符串,需要指定写入长度。同样,写操作是阻塞写。

“MYSQL_SERVER_AUTH_INFO”的定义如下:

    /* mysql/plugin_auth.h.pp */
    typedef struct st_mysql_server_auth_info
    {
      char *user_name; // 客户端发送的用户名
      unsigned int user_name_length; // 客户端发送的用户名长度
      const char *auth_string; // 在mysql.user表中记录的相应账户的authentication_string
      unsigned long auth_string_length; // authentication_string长度
      char authenticated_as[512 +1]; // 代理用户名,传入时为user_name,可设置
      char external_user[512 +1]; // 系统变量external_user显示的值,待设置
      int password_used; // 是否使用密码,待设置
      const char *host_or_ip; // 主机或IP
      unsigned int host_or_ip_length; // 主机或IP的长度
    } MYSQL_SERVER_AUTH_INFO;

由上述定义可知在“MYSQL_SERVER_AUTH_INFO”中可以取到“user_name”和“auth_string”这样的关键字符串。

“password_used”的含义是“是否使用密码”,当认证出错时,报错信息的后面有“Password used: Yes/No”,显示“Yes”还是“No”就由“password_used”决定。默认为“No”,若想保存信息中显示“Yes”,可在school_number_auth函数中设置“password_used”,代码片段如下:

info->password_used= PASSWORD_USED_YES;

明白传入参数的含义后很容易就可以写出school_number_auth函数,其内容如下:

    static int school_number_auth(MYSQL_PLUGIN_VIO *vio, MYSQL_SERVER_AUTH_INFO *info)
    {
        int pkt_len;
        unsigned char *pkt;

        if (vio->write_packet(vio, (const unsigned char *) ORDINARY_QUESTION "Please enter your name: ", 26))
            return CR_ERROR;

        if ((pkt_len= vio->read_packet(vio, &pkt)) < 0)
            return CR_ERROR;

        if (strcmp((const char *) pkt, info->user_name))
            return CR_ERROR;

        if (vio->write_packet(vio, (const unsigned char *) LAST_QUESTION "Please enter your school number: ", 35))
            return CR_ERROR;

        if ((pkt_len= vio->read_packet(vio, &pkt)) < 0)
            return CR_ERROR;

        if (strcmp((const char *) pkt, info->auth_string))
            return CR_ERROR;

        return CR_OK;
    }

至此,我们就完成了认证插件的代码编写,将其保存到文件my_auth_plugin.c中,然后进入到下一节。

编译安装

编译

插件的代码写好后按如下命令编译:

gcc $(mysql_config --cflags) -shared -fPIC -DMYSQL_DYNAMIC_PLUGIN -o my_auth_plugin.so my_auth_plugin.c

参数“-DMYSQL_DYNAMIC_PLUGIN”是必不可少的,否则编译的时候不会报错,但在MariaDB中执行“INSTALL PLUGIN”时会报如下错误:

ERROR 1127 (HY000): Can't find symbol '_mysql_plugin_interface_version_' in library

另外一种常见的错误是找不到头文件:

#include <mysql/plugin_auth.h>
#include <mysql/auth_dialog_client.h>

解决方法是安装相关开发包引入需要的头文件,命令是:

sudo rpm -ivh MariaDB-devel-5.2.9-102.el5.x86_64.rpm

sudo apt-get install libmariadbclient-dev

其实不执行上述命令,将MariaDB安装路径下的inculde目录加入到gcc的头文件搜索路径中也可以解决头文件缺失问题。

编译成功后得到my_auth_plugin.so。

复制

编译得到.so文件后需要将.so文件复制到MariaDB的插件目录中。进入MariaDB,用如下语句查询插件目录:

MariaDB [(none)]> SHOW VARIABLES LIKE 'plugin_dir';
+---------------+------------------------------+
| Variable_name | Value                        |
+---------------+------------------------------+
| plugin_dir    | /usr/local/mysql/lib/plugin/ |
+---------------+------------------------------+
1 row in set (0.00 sec)

将my_auth_plugin.so复制到MariaDB的插件目录中:

sudo cp my_auth_plugin.so /usr/local/mysql/lib/plugin/

复制完成后最好修改my_auth_plugin.so的所有者为运行MariaDB的用户,该用户名一般是mysql,命令如下:

sudo chown mysql /usr/local/mysql/lib/plugin/my_auth_plugin.so

安装

只是将.so文件复制到MariaDB的插件目录中还不够,还需要在MariaDB中安装插件,语句如下:

MariaDB [(none)]> INSTALL PLUGIN school_number SONAME 'my_auth_plugin.so';
Query OK, 0 rows affected (0.00 sec)

“school_number”是插件名,定义在mysql_declare_plugin中,my_auth_plugin.so是.so文件名,不要混淆。

有安装就有卸载,如何卸载呢?语句如下:

MariaDB [(none)]> UNINSTALL PLUGIN school_number;
Query OK, 0 rows affected (0.00 sec)

先不要执行卸载语句,或是卸载后重新安装,后面还要用到这个插件。

使用插件

先创建一个使用该插件认证登录的用户,语句如下:

MariaDB [(none)]> CREATE USER 'werner'@'localhost' IDENTIFIED VIA 'school_number' USING 'M201434212';
Query OK, 0 rows affected (0.00 sec)

退出MariaDB后以werner用户登录,可以看到确实使用了插件认证方式,具体过程如下图所示。

插件认证方式登录过程截图

源码下载

可以在这里下载到源码。(其实文中已经出现了全部源码。)

参考文献

  1. MySQL 5.5 Reference Manual
  2. Writing a MariaDB PAM Authentication Plugin
  3. MySQLPlugin之如何编写Auth Plugin
]]>
编程
[译]SVM核函数RBF的参数 https://blog.werner.wiki/rbf-svm-parameters/ https://blog.werner.wiki/rbf-svm-parameters/ Werner 2018-04-17

本文翻译自《RBF SVM parameters》

本例将阐明径向基函数(RBF)做SVM的核函数时参数gamma和C的影响。

直观地,参数gamma定义了单个训练样本的影响大小,值越小影响越大,值越大影响越小。参数gamma可以看作被模型选中作为支持向量的样本的影响半径的倒数。

参数C在误分类样本和分界面简单性之间进行权衡。低的C值使分界面平滑,而高的C值通过增加模型自由度以选择更多支持向量来确保所有样本都被正确分类。

图1是只有两个输入特征和两个可能目标分类(二分类)的简单分类问题在取不同参数值时的决策函数的可视化。注意当有更多特征和目标分类时这种图便画不出来了。

图1

图2是分类器交叉验证的正确率作为C和gamma的函数绘制出的热力图。在这个例子中出于演示目的,我们探索了一个相对较大的参数范围。在实践中,10-3到103的对数范围一般来说是足够的。如果最佳参数位于范围的边界,则可以向该方向扩展范围做进一步的搜索。

图2

注意到热力图中有一个特殊的彩条,它的中间点的值接近于模型表现最好的得分,这是一眼就可以看到的。

模型的行为对于参数gamma十分敏感。如果参数gamma过大,支持向量的影响半径将小到只能影响到它自己,这时再怎么调整参数C也不能避免过拟合。

当参数gamma非常小时,模型会过于拘束不能捕捉到数据的复杂性或“形状”。任何选中的支持向量的影响区域将包含整个训练集。模型的结果将表现地像是用一组超平面分割两类或多类的高密度中心的线性模型。

至于中间值,我们在图2中可以看到,参数gamma和C的对角线上可以找到好的模型。平滑的模型(更小的gamma值)可以通过选择大量的支持向量(更大的C值)来获得更高的复杂度,于是好的模型便出现在了对角线上。

最后我们也观察到对一些gamma的中间值,当C取非常大的值时依旧可以得到表现良好的模型:没有必要通过限制支持向量的数量来实现正则化。RBF核的半径本身就是一个很好的结构调整器。在实践中仍可能会对通过一个较小的C值来限定支持向量的数目感兴趣,这样就可以使模型使用更少的内存,更快地做出预测。

我们还应该指出随机分割的交叉验证会导致结果得分有细微的不同。通过以计算时间为代价增加CV迭代次数n_splits,可以平滑这种细微的不同。在热力图中增加参数C和gamma的取值步长会降低热力图的分辨率。

]]>
杂项
人生的意义 https://blog.werner.wiki/the-meaning-of-life/ https://blog.werner.wiki/the-meaning-of-life/ Werner 2018-03-27

湛卢剑削铁如泥,杀人自不在话下。只见那人头颅飞起丈余,身子还未倒下,鲜血便从脖颈喷出,形成一道喷泉,腥味顿时四散。几息后无头尸体直挺挺地倒下,血仍从脖颈处一股一股地涌出,汇成一条小溪。

尸体不远处仰面躺着一个人,呵哧呵哧地喘着粗气。此人短衣襟,小打扮,全身上下均被汗水浸湿。衣服破了许多口子,鲜血渗出,染红衣襟,狼狈不堪。约莫过了半个时辰,这人才觉缓过气来,右手摸索几下,抓住湛卢宝剑的剑柄,以剑为杖,撑起身体。正要站起,却觉左腿麻木,全不似自己的腿。仔细查看,大惊失色,大腿面上一道细长的伤口渗出乌黑的血,隐隐发臭。这人心想:“吾命休矣!那老道最善使毒。这荒郊野外,人迹罕至,我上何处寻医问药。”,转念又想,“常听人言,‘凡毒蛇出没之处,七步之内必有解药’。”于是挣扎着想要爬向无头尸体,不想才转过身,便觉头脑发沉,眼前发黑,骨软筋麻,四肢无力,一动也动不了,只能俯面趴在地上。

“也罢也罢。我展熊飞一生行侠仗义,铲恶锄奸,有南侠之称。后又保了包大人,在开封府当差,不知做了多少好事。临死又杀了毒王,为民除害。实不枉此生,不枉此生!”遂大叫一声,不省人事。

已经整整十天了。

“展护卫办事最是稳妥,何曾旬日不归。”包相爷忧心忡忡,黑脸也有些惨白。回想起这几日,开封府乱作一团,皆因展昭失踪。堂堂御前四品带刀护卫,朝廷命官,又是武林中绝顶的高手,怎么就杳无音信,活不见人,死不见尸。包公躺在床上想着这些事,辗转反侧,百思难解。这几日实在过于劳累,不久便昏昏沉沉地睡去。

“咚!——咚!咚!”,飘渺的打更声不知从何处传来。乌云密布,新月无光,正是十月初一日,半夜三更时。

包公迷茫地四下观望,一团漆黑,只见远处点点烛光照出层层叠叠地阴影,阴影中似乎有人影晃动,但不闻人声。近处白雾飘渺,恍若仙境,却又寒气袭人,显得阴森恐怖。

包公远远望见一个黑影从远处飘来,几个呼吸间便飘到自己面前,扑通跪地,嘭、嘭、嘭磕了三个响头。相爷连忙扶起那黑影,定睛一看,却是展昭,又惊又喜,便问道:“展护卫,你上哪里去了?何故旬日不归?”展南侠泪如雨下:“启禀大人,卑职已死多日。大人乃文曲星转世,今日冥阴,阴差特许我见大人,禀明前后因果。”。包公闻听展昭已死,不禁垂泪,问:“究竟怎么回事?速速讲与我听。”南侠展昭这才道出,是这么这么这么的。

原来那日展南侠闲来无事,觉得汴梁城人多口杂,闷得慌,便施展陆地飞腾之术,翻山越岭,欣赏沿途风景。暮秋时节,万物肃杀,别有一般趣味。这陆地飞腾之术可了不得,高来高去,越大厦如履平地,上危塔轻而易举,横跳江河竖跳海,万丈高楼脚下踩。心之所向,没有去不着的地方。寻常人见了,只以为他在飞哩。

展南侠这么随意游览,不知跑出多少里去,不知行到何府何地,好不惬意,真个是逍遥游哉!正行间忽听得几声惨叫,响彻云霄。南侠急忙转向,循着声音前行。不久便嗅到浓烈的血腥味,见到溪水中混杂着红色,再不久便看到一个狰狞老道,坐在溪水旁的石头上,拿着一把小刀,悠然自得地剜一个小童子的心,如同在剥莲蓬一般。

南侠见此,怒发冲冠,抖丹田大喝一声:“呔!你是何人?为何行此伤天害理之事?”。

那老道专心剜心,没想到会有人,被吓个踉跄,声色俱变,小刀也掉到了溪水里。老道抬头,见面前站着一人,举剑指着自己,又环顾四周,只有面前一人,便将小童子随手扔在一旁,抖了抖道袍,全不顾血污,行一个揖礼,口中喊道:“福生无量天尊,小道这厢有礼了。敢问这位少侠仙乡何处,尊姓大名?”展南侠将湛卢剑一抖,“呔!你明知自己是三清弟子,为何行此伤天害理之事?我乃展昭展熊飞,你是何人?快快服法,随我去开封府受审。”老道闻听此言,脸色微变,“原来是南侠啊,久仰久仰。小道姓张名巧字道禹,在蜈蚣岭黄花观出的家。采此童子心实为练药,并无行伤天害理之事。”南侠一听,便觉不妙。原来这张巧张道禹并非无名小辈,而是江湖上凶名赫赫的毒王淫道,一生专爱做杀人越货,欺男霸女之事,偏又武艺高强,极善使毒,寻常人根本奈何不了他,故而逍遥法外,成为武林一大害。但今儿个他碰到的可不是寻常人!好南侠毫不怯敌,打起十二分的小心。毒王见状,便知展昭被自己凶名所震,于是指着小童子尸体道:“展大侠有所不知,此小儿是我从他父母手里买来的,与开封府无关。大路朝天咱们......”一句话未说完手中飞镖已发,南侠一剑劈开,霎时间两人战成一团。

展熊飞心想自己不是死了吗,这又是什么地方,难道是有人救了自己。看看伤口,已经痊愈,连疤都没有。衣服也换过了,是件通体纯黑的麻衣,无鞋无袜。摸摸腰间,湛卢剑不在,百宝囊也不在。四下打量,见四面皆是石墙,四方有一丈大小,顶上亦是石顶,南侠心想自己这是被关到了石室里。

忽听得喀嚓一声,石室地板中间裂开一条缝,展昭正惊奇间,地板像两扇门板一样向下打开,展昭猝不及防,跌落下去。头顶的光线瞬间消失,漆黑不见五指,南侠伸手向四处摸索,什么也摸不到,更不知下落多快,只觉身体轻飘飘的。不知过了多久,眼前忽然出现一点萤火大小赤橙色的光亮,很是温暖。光亮一分为二,二分为四,四分为八,不久便成一团,先成幼鱼状,后成婴孩状。忽得一闪,正是一个婴孩。展昭伸直手臂,才发现那婴孩看似在眼前,实则很远,触摸不着,只能耐心观看。那婴孩长得飞快,几个呼吸便长大一圈,蹒跚学步,咿咿学语,皆活灵活现。后又读书识字,从师习武,展昭这才认出这正是年幼时的自己,想来自己已经死了,这是在阴司受审哩。展昭平静地观看自己的一生,有苦难有得意,有磨砺有风光,有儿女柔情,更有行侠仗义。最后看到自己毒发身亡,南侠自己也不禁赞叹,真英雄也!

忽听“嘭”地一声。这声音展护卫再熟悉不过,不知听过多少次,乃惊堂木之声。“堂中所立之鬼,可是常州府武进县遇杰村人氏展昭展熊飞?”,南侠发觉自己不知怎么的就站在阴森森黑漆漆的大堂之中,堂上坐一相貌威严的牛头大鬼,大鬼旁站一牛头小鬼,小鬼手执毛笔,不知记些什么。“正是在下”展昭行礼作答。大鬼问到:“方才我们观你在阳间一生所作所为,可有差错?”,展昭答道:“分毫不差。”。大鬼朝小鬼道,“让它签字画押。”,小鬼便拿着一本簿子和笔墨飘到展昭面前,展昭见那簿上密密麻麻,小鬼指着一处,展昭细看,写着“常州府武进县遇杰村展昭熊飞打嗝凡叁仟伍佰肆拾贰投地狱道”,南侠见“地狱”二字大惊,浑浑噩噩地签了字画了押,又瞥见上面一行写着“陕州夏县涑水乡张家村张巧道禹打嗝凡一万玖仟三佰零贰投人道”,再想看时小鬼已收回簿子和笔墨。大鬼指着展昭喊道:“来啊,把它拖下去投地狱道!”

展昭大喊:“大人,大人,我不服。我平生做尽好事,为何要入地狱道!那张道禹无恶不做,为何可入人道!”大鬼冷哼,摇头道:“天道浩渺,岂是凡夫俗子可察!自开天辟地,阴司便以打嗝判六道轮回。汝等凡俗,愚昧无知,不晓天理,不思努力打嗝,白白浪费阳间一世光阴。拖下去!”

相爷闻听此言,大惊失色,呆若木鸡,良久,问:“此话可当真?”展护卫下跪磕头,答道:“千真万确!卑职不敢有半句谎话。”。包公闻之长叹。

]]>
小说
大学军训说 https://blog.werner.wiki/military-training-in-university/ https://blog.werner.wiki/military-training-in-university/ Werner 2018-03-15

最近在找工作,才发现自己大学期间获得的唯一奖项是大一军训时征文比赛的一等奖。依稀记得那是一篇堆砌辞藻的骈体文,写得很一般,还有些烂尾,能获奖大概只是因为文言短篇让评审人觉得新奇。现在翻出来看,虽然依旧觉得写得不好,但却有惊艳之感,不知当时是怎么写出来的,现在无论如何也写不出这样的文章了。原文创作于2014年9月,记录如下。

我炎黄子孙,巨龙之嗣,数千年来,于荆棘之中求生存,于混沌之中祈福祉, 于血火之中立性命。惜匈奴犯我疆境,遗女且退,须臾又至;五胡乱我民族,戮 食几尽,绝灭人性;列强侵我中华,伏尸千万,血染河山——神州自古多磨难! 幸我华夏儿女,不畏艰难险阻,不惧困难重重,以利刃之态,尖兵之姿,每挽狂 澜于即倒,数扶大厦之将倾,凭信念造精魂,洒热血铸铁骨,承社稷之重,护国 之龙骨,夺百世鸿运,于江湖之野血荐轩辕。

五千年盛衰成败,八百岁愁苦哀叹。

时属今朝,放眼寰球,见团团和气,闻升平歌舞,乃其乐融融,携手共进之 盛世华章!然解甲归田,放马南山,刀枪入库,铸剑为犁,能乎不能?破城岂用 放枪,卖国何须割地!外之敌,内之奸,藏于暗,隐于阴。虎豹没齿,豺狼隐爪。 细观惊见,魑魅魍魉乱华夏,山河破碎无人收;追思当年,男儿女儿带吴钩,杀 敌灭虏净九州。今日大学生,当承古人志,仗轩辕剑,执子房计,行诸葛谋,立 鹏举威。虎豹惨啸,豺狼爪折,平魑魅,灭魍魉,佑中华。呜呼!恨不能生为唐 人,为国开疆裂土,马革裹尸。

然文以为智,武以为行。现今青年,智且足而行有缺。跳不能及五尺,走不 能达百里;登高则畏危,临川则惧深;出门怕狗咬,夜行疑鬼来;晨起病霜露, 午间疾日晒;身无提刀之劲,手无缚鸡之力。鹏举威高,轩辕剑重。纵有济世匡 时之志,经天纬地之计,出鬼入神之谋,亦难施行。且或亲美日,喜英韩,忘国 仇,失家恨,敌友不辨,青白不分,认贼作父!呜呼哀哉,精魂已失,铁骨何在!

想我英勇红军,令则行,禁则止;等闲千山万水,驰骋神州大地;横过草地, 纵越雪山,四渡赤水,六破敌围;腾逶迤五岭,走磅礴乌蒙,寒渡河铁索,暖金 沙云崖。经千辛万苦,历万苦千辛,成旷世绝时之举,点燎原星星之火,祛愚昧 黑暗,散迷雾困惑,断灾厄之源,聚天地之精,赋民族以神,得千秋气运。

故知军训强武。可健其体魄,可康其筋骨,可坚其意志,可塑其人格。使之 知军事,晓国防。使之临险而不惧,面危而不乱。补其行之不足,使之可堪大任。

梁任公云:少年强,则中国强。中华大学生,当承强国之志,秉富民之愿, 入军训之列。养吃苦耐劳之精神,成艰苦奋斗之习惯,立精忠报国之赤诚。使中 华大学生,人人有强健体魄,人人有坚韧意志,人人有不屈精神,问其理想,则 必答曰:“披肝沥胆,以身饲魔,为国尽忠,百死无悔!”

]]>
记录
php中md5($str,true)注入 https://blog.werner.wiki/php-md5-true-sqli/ https://blog.werner.wiki/php-md5-true-sqli/ Werner 2018-02-09 有如下的php代码:

    <?php
    $password = $_POST['password'];
    $sql = "SELECT * FROM admin WHERE username = 'admin' and password = '".md5($password,true)."'";
    $result = mysqli_query($link,$sql);
    if(mysqli_num_rows($result)>0){
        echo 'Success';
    }else{
        echo 'Failure';
    }
    ?>

问是否能通过提交特定的POST参数password,让上示代码执行到“echo 'Success';”。

回答是肯定的。这显然是一个注入问题,问题的关键在于md5函数的第二个参数为true。

先看php中的md5函数,它有两个参数string和raw。 第一个参数string是必需的,规定要计算的字符串。 第二个参数raw可选,规定十六进制或二进制输出格式:

  • TRUE - 原始 - 16 字符二进制格式
  • FALSE - 默认 - 32 字符十六进制数

示例如下:

    <?php
    $str = "Shanghai";
    echo "字符串:".$str."<br>";
    echo "TRUE - 原始 16 字符二进制格式:".md5($str, TRUE)."<br>";
    echo "FALSE - 32 字符十六进制格式:".md5($str)."<br>";
    ?>

输出为:

    字符串:Shanghai
    TRUE - 原始 16 字符二进制格式:Tf頦+饲X0蠨鎗�)�
    FALSE - 32 字符十六进制格式:5466ee572bcbc75830d044e66ab429bc

由上例可知,当md5函数的第二个参数为true时,该函数的输出是原始二进制格式,会被作为字符串处理。 理解这一点后,问题就简单了。 只要提交特定字符串,让其md5值以原始二进制格式输出(被当作字符串)时含有能触发SQL注入的特殊字符即可。

如何找到这样的字符串呢?百度一番后我找到了两个:

content: 129581926211651571912466741651878684928
hex: 06da5430449f8f6f23dfc1276f722738
raw: \x06\xdaT0D\x9f\x8fo#\xdf\xc1'or'8
string: T0Do#'or'8

content: ffifdyop
hex: 276f722736c95d99e921722cf9ed621c
raw: 'or'6\xc9]\x99\xe9!r,\xf9\xedb\x1c
string: 'or'6]!r,b

提交 “129581926211651571912466741651878684928” 或 “ffifdyop” 都可以达到本文开头的目的。

]]>
Web安全