Skip to content

Latest commit

 

History

History
479 lines (302 loc) · 20.8 KB

File metadata and controls

479 lines (302 loc) · 20.8 KB

二、与 Web 应用交互

在上一章中,我们了解了 web 应用安全过程以及测试应用安全性的重要性。在本章中,我们将了解以下主题:

  • HTTP 协议基础
  • HTTP 请求的剖析
  • 使用请求库与 web 应用交互
  • 分析 HTTP 响应

HTTP 协议基础

在本节中,我们将了解 HTTP 协议,它是如何工作的,它的安全方面,以及在执行请求时支持哪些方法。

这将为您提供 HTTP 的基本知识,这对于理解如何构建工具和测试 web 应用中的安全问题非常重要。

什么是 HTTP 及其工作原理?

HTTP 被设计为支持客户端和服务器之间的通信。

HTTP 是在应用层运行的基于 TCP/IP 的通信协议。通常,我们使用 web 浏览器与 web 应用交互,但在本次培训中,我们将不使用浏览器,而是使用 Python 与 web 应用进行对话。此协议与媒体无关。

这意味着只要客户机和服务器知道如何处理数据内容,就可以通过 HTTP 发送任何类型的数据。而且它是无状态的,这意味着 HTTP 服务器和客户端仅在请求事务的过程中相互了解。由于此特性,客户端或服务器都不会在请求之间保留信息,这将在以后执行某些攻击时有所帮助。

HTTP 协议有两种不同版本:

  • HTTP/1.0:这将为每个请求/响应事务使用一个新连接
  • HTTP/1.1:这是一个或多个请求-响应事务可以使用连接的地方

HTTP 不是一种安全协议,这意味着所有通信都是明文,容易被拦截和篡改。

通常,HTTP 正在端口80上服务。下面是一个简单事务的示例:

在左边,我们有一个客户端,它向服务器发送一个 HTTPGET请求,请求资源test.html。服务器返回一个 HTTP 响应,其中包含一个200 OK代码、一些头和内容test.html(如果它存在于服务器上)。

如果不存在,则返回404 Not Found响应码。这代表了 web 应用世界中最基本的GET请求。

1994 年,HTTPS 被引入,在 HTTP 之上增加了安全性。HTTPS 本身不是协议,而是在安全套接字层SSL)或传输层安全TLS之上分层 HTTP 的结果。

HTTPS 在不安全的网络上创建安全通道。这确保了对窃听者和中间人攻击的合理保护,前提是使用了足够的密码套件,并且服务证书经过验证和信任。因此,每当应用处理敏感信息时,如银行付款、购物网站、登录页面和个人资料页面,都应该使用 HTTPS。基本上,如果我们处理流程或存储客户数据,它应该使用 HTTPS。

在 HTTP 中,方法表示要对所选资源执行的所需操作,也称为 HTTP 谓词。HTTP/1.0 定义了三种方法:

  • HEAD:只返回表头和状态码,不返回其内容
  • GET:这是用于检索给定 URI 的资源内容的标准方法
  • POST:这是一种向服务器提交内容、表单数据、文件等的方法

然后,HTTP/1.1 引入了以下方法:

  • OPTIONS:提供目标资源的通信选项
  • PUT:请求存储给定 URI 标识的资源
  • DELETE:这将删除给定 URI 标识的目标资源的所有表示
  • TRACE:此方法回显接收到的请求,以便客户端可以看到中间服务器进行了哪些更改或版本
  • CONNECT:这将建立到 HTTPS 使用的给定 URI 标识的服务器的隧道
  • PATCH:此方法对资源应用部分修改

HEADGETOPTIONSTRACE按照惯例被定义为安全的,这意味着它们仅用于信息检索,不应改变服务器的状态。

另一方面,诸如POSTPUTDELETEPATCH等方法旨在用于可能对服务器或外部产生副作用的操作。还有比这些更多的方法。我鼓励你去探索它们。

我们已经看到 HTTP 是一种无状态的客户机-服务器协议。

此协议不提供任何安全性,因此创建 HTTPS 是为了在 HTTP 之上添加一个安全层。我们还了解到,有一些不同的方法将指示服务器对所选资源执行不同的操作。

HTTP 请求的剖析

在本节中,我们将了解 URL 的结构、请求和响应头,以及一个使用 Telnet 的GET请求示例,以了解它如何在较低级别上工作。

我打赌你现在已经看到了成千上万的网址。现在是时候停下来思考一下 URL 结构了。让我们看看每个部分的含义:

第一部分是 web 应用中的协议。使用的两个协议是 HTTP 和 HTTPS。使用 HTTP 时使用的端口为80,使用 HTTPS 时使用的端口为443

下一部分是我们要联系的主机。接下来,我们可以看到该服务器中的资源或文件位置。在本例中,目录为content,资源为section。然后,我们有一个问号符号,它指示将出现的是查询字符串。这些参数将被传递到页面的部分以进行处理。

有一些替代方案,例如在主机之前添加用户名和密码进行身份验证,或者在 web 服务器未在标准的80443端口中侦听的情况下显式定义端口。

HTTP 头

现在,让我们谈谈标题。标头是 HTTP 请求和响应的核心部分。

它们描述了客户机和服务器之间的通信方式,并提供了有关事务的信息。我们有客户端头文件,由浏览器发送。一些例子如下:

  • 用户代理:告知服务器用户有什么类型的操作系统、浏览器和插件。
  • 接受编码:定义浏览器支持的编码,通常为 GZip 或 Deflate。这将压缩内容并减少每个事务的带宽时间。
  • Referer:这包含 Referer URL,基本上是您在哪个页面单击该链接的。
  • Cookie:如果我们的浏览器在其站点上有 Cookie,它会将它们添加到 Cookie 头中。我们也有服务器端头,由 web 服务器设置。
  • 缓存控制:定义了链上所有缓存机制必须遵守的指令。
  • 位置:用于重新定向。无论何时有301302响应,服务器都必须发送此头。
  • 设置 Cookie:用于在用户浏览器中设置 Cookie 的表头。
  • WWW-Authenticate:服务器使用此头请求认证。当浏览器看到此标题时,它将打开一个登录窗口,询问用户名和密码。

这是向发出GET请求时响应头的一个示例 https://www.packtpub.com/

我们在这里提到了其中的一些,例如cache-controlcontent-encodingcontent-type。我建议你把它们都熟悉一下。每次你找到一个新的标题,阅读它来了解它的功能。

获取请求

在查看 URL 结构和标题之后,让我们在真正的服务器上尝试一个GET请求。

为此,我将使用终端和telnet命令向服务器发送原始GET请求。这是我们通过键入 Telnet 连接来模拟浏览器的尝试。

执行以下步骤:

  1. 让我们切换到虚拟机,打开终端并键入以下内容:
telnet www.httpbin.org 80

80 is the port we want Telnet to connect to. httpbin.org is a website that provides an HTTP request and response service that is useful to test tools.

点击进入

  1. 连接后,我们将看到以下消息:

这意味着连接已建立。

  1. 接下来,我们输入GET /ip HTTP/1.0并点击回车两次。这是我们告诉服务器我们正在使用GET请求名为/ip的资源。然后,我们指定HTTP/1.0协议,然后按Enter两次。因此,我们从服务器获得第一个响应:

请注意,我们在请求中根本没有使用任何头,但是我们从服务器收到了许多头,以及资源 IP 的内容。

在这种情况下,内容是发出请求的机器的 IP 地址。

现在,让我们再举一个例子,但这次请求一个包含参数的 URL。

打开终端并键入:

telnet www.httpbin.org 80
GET /redirect-to?url=http://www.bing.com HTTP/1.0

同样,我们使用了GET,但这次我们请求资源重定向到,查询字符串中的参数 URL 值为http://www.bing.com

在这种情况下,服务器基本上将浏览器重定向到提供的 URL,使用位置头并返回一个302重定向代码。在这种情况下,不会发生任何事情,因为 Telnet 不会解释该报头。记住,这是一个规则连接。

使用请求库与 web 应用交互

在本节中,我们将开始编写 Python 代码,以使用请求库执行 HTTP 请求。

请求库

Requests 是用 Python 编写的 Apache2 许可 HTTP 库。创建它是为了减少使用urllib2和目前可用的其他 HTTP 库时所需的复杂性和工作量。

这是使用urllib2库时使用身份验证执行api.github.com请求所需的代码示例:

import urllib2

gh_url = 'https://api.github.com'

req = urllib2.Request(gh_url)

password_manager = urllib2.HTTPPasswordMgrWithDefaultRealm()
password_manager.add_password(None, gh_url, 'user', 'pass')

auth_manager = urllib2.HTTPBasicAuthHandler(password_manager)
opener = urllib2.build_opener(auth_manager)

urllib2.install_opener(opener)

handler = urllib2.urlopen(req)

print handler.getcode()
print handler.headers.getheader('content-type')

这是相同的功能,但使用requests库:

import requests

r = requests.get('https://api.github.com', auth=('user', 'pass'))

print r.status_code
print r.headers['content-type']

简单性是显而易见的。在编写脚本时,它确实方便了我们的工作。

我们的第一个剧本

让我们开始用 Python 编程。在第一个示例中,我们将使用 Python 和requests库执行GET请求:

  1. 让我们在虚拟机中打开 Atom 编辑器,通过导航到 file | new file 创建一个新文件。
  2. 首先,我们将导入requests库。这可以通过键入import requests来完成。
  3. 现在,我们需要创建一个变量 R,在这里我们将用GET方法实例化一个 requests 对象,本例中的目标 URL 是httpbin.org/ip
import requests
r=requests.get('http://httpbin.org/ip')
  1. 最后,我们使用print r.text打印响应的内容。
  2. /Examples/Section-2文件夹中的文件另存为Chapter-3.py
  3. 让我们在终端上运行它。打开终端,使用以下命令将目录更改为/Example/Section-2
cd Desktop/Examples/Section-2/ 
  1. 接下来,我们使用以下命令运行它:
python Chapter-3.py

我们可以看到响应体,在这里我们可以再次看到我的 IP:

记住,/ip在主体中返回调用方 IP。

这是我们使用requests库的第一个脚本。祝贺您,您正在使用 Python 与 web 应用通信!

现在,让我们在GET请求中添加一个查询字符串:

  1. 为了做到这一点,我们将添加一个名为payload的变量和一个字典,其中每个键都是参数名,值将是该参数的值。在这种情况下,参数为 URL,值为http://www.edge-security.com
  2. 然后,我们将资源更改为/redirect-to而不是 IP。此资源希望参数 URL 具有有效 URL,这将重定向我们。
  3. 我们还需要在请求params=payload中添加有效负载作为params的值:
import requests
payload= {'url':'http://www.edge-security.com'}
r=requests.get('http://httpbin.org/redirect-to',params=payload)
print r.text
  1. 然后,我们会保存它。
  2. 现在,如果我们运行脚本,我们将在python Chapter-3.py终端中看到重定向页面的内容。好了。

这里我们有终端中www.edge-security.com的全部内容:

这就是我们向查询字符串添加参数的方式。

如果我们想查看服务器返回的代码,该怎么办?我们需要添加以下代码:

  1. 让我们通过键入print "Status code:"打印一些标题。
  2. 然后,我们可以使用以下命令打印一些格式:
print "t *" + str(r.status_code)

我们可以移除print r.text以获得更清晰的响应。

  1. 我们将保存它,并使用 Python 和脚本名称在终端中运行它。结果我们可以看到状态200,表示请求有效:

现在我们将了解如何访问响应的标题。

  1. 我们将返回虚拟机中的编辑器并打开文件Video-3-headers.py,该文件已准备好保存一些键入内容。此脚本正在再次使用资源/IP。

为了访问响应头,我们使用请求对象的方法头。 为了逐行打印,我们可以做一个循环,从r.headers:解包键和值。

  1. 让我们试着在终端上运行这个。
  2. 我们将使用 Python 和脚本文件名。您可以看到服务器返回的不同头、响应代码和响应正文内容。

如果我们只想请求报头以节省带宽并加快 reg 响应事务时间,该怎么办?我们回到编辑器,用head方法更改get方法。

我们保存脚本,然后移动到控制台并运行它。我们可以看到状态代码为200,我们正在返回标题,但我们不再有响应正文内容:

这是因为使用的方法是head,我们只从服务器获取头。

设置标题

现在,我们将了解如何设置请求的头。

我们为什么要这么做?因为我们可能需要添加应用预期的自定义头。我们想伪造我们的用户代理,以欺骗服务器,使其认为我们是一个移动设备。我们可能想要更改post头来欺骗服务器或负载平衡,或者我们可能想要强制或篡改头值,看看应用如何处理它。

让我们尝试设置一个标题:

  1. 返回编辑器中的脚本。我们将修改请求,将方法更改回get,并将资源从ip更改为headers。这将使http://bin.org将其在响应正文中接收到的爬升标头发送给我们,以便进行调试:
#!/usr/bin/env
import requests
r = requests.get('http://httpbin.org/ip')
print r.url
print 'Status code:'
print '\t[-]' + str(r.status_code) + '\n'

print 'Server headers'
print '****************************************'
for x in r.headers:
    print '\t' + x + ' : ' + r.headers[x]
print '****************************************\n'

print "Content:\n"
print r.text
  1. 保存它,然后运行它。
  2. 我们可以看到,用户代理,requests库随每个请求发送python-requests
  3. 现在,让我们回到编辑器,将user-agent头设置为随机测试值。我们需要添加一个名为myheaders的字典,其中包含密钥名、用户代理和测试值Iphone 6
myheaders={'user-agent':'Iphone 6'}
  1. 我们还需要添加请求,一个名为 headers 的参数,其值为myheaders
#!/usr/bin/env
import requests
myheaders={'user-agent':'Iphone 6'}
r = requests.post('http://httpbin.org/post',data={'name':'packt'})
print r.url
print 'Status code:'
print '\t[-]' + str(r.status_code) + '\n'

print 'Server headers'
print '****************************************'
for x in r.headers:
    print '\t' + x + ' : ' + r.headers[x]
print '****************************************\n'

print "Content:\n"
print r.text
  1. 让我们在控制台中再次运行它。

我们可以看到服务器收到了我们修改过的用户代理,伪造了一个Iphone 6

现在,您知道如何操作标题了。

现在,我们看到了一个 TytT0 和一个 Ty1 T1 请求,让我们看一个 Outt2 请求,我们将发送表单参数:

  1. 返回 Atom 编辑器,用post替换get方法。
  2. 我们还将更改 URL。这一次,我们将使用 post 资源http://bin.org/post并添加数据字典。

这通常是您在 web 应用中看到的表单数据。在本例中,我们在数据字典中添加一个参数,该参数具有键代码名和值packt。我们保存它,然后在控制台中运行脚本。完美的我们可以在结果中看到,我们已经提交了带有值的字典表单。

祝贺您,您现在知道如何使用 Python 执行不同的 HTTP 请求了!

分析 HTTP 响应

在本节中,我们将了解不同的 HTTP 响应状态代码和不同类别的 HTTP 响应代码。

然后,我们将编写示例以查看成功的响应或错误,最后,我们将看到重定向示例。

HTTP 代码

HTTP 协议定义了五类响应代码来指示请求的状态:

  • 1XX 信息:100 个范围码用于信息目的。它只存在于 HTTP/1.1 中。
  • 2XX 成功:200 个代码范围用于表示客户请求的操作已被接收、理解、接受和处理。最常见的是200 OK
  • 3XX 重定向:300 范围表示必须采取额外操作才能完成请求的客户端。这些代码大部分用于 URL 重定向。这组代码中最常见的是302 Found代码。
  • 4XX 客户端错误:400 范围表示客户端有错误。最常见的是404 Not Found
  • 5XX 服务器端错误:范围 500 用于指示服务器端的错误。最常见的是500 Internal Server Error

我们建议您在这里学习每组的不同代码: https://developer.mozilla.org/en-US/docs/Web/HTTP/Status

让我们编写一些代码。让我们在虚拟机中打开编辑器并创建一个新文件:

  1. 首先,我们输入import requests导入requests库。
  2. 我们将为目标 URL 创建一个变量。我们将再次使用httpbin.org并键入:
url='http://httpbin.org/html'
  1. 然后,我们将打印带有req.status _code的响应代码。为此,我们输入以下内容:
req = requests.get(url) 
  1. 打印req.status_code字符串的响应代码。这可以通过以下方式完成:
print "Response code: " + str(req.status_code) 
  1. 就这样!我们将把/Example/Section-2中的文件保存为Video-4.py并切换到终端运行脚本。
  2. 使用python Video-4.py

您应该在响应中看到一个200状态代码,这意味着我们的请求成功:

干得好,让我们继续。

让我们回到编辑器:

  1. 现在,让我们将目标 URL 更改为不存在的 URL。为了查看错误代码,我们将更改 URL 并写入fail
import requests
url='http://httpbin.org/fail'
req = requests.get(url)
print "Response code: " + str(req.status_code)
  1. 让我们保存它并在终端中再次运行此脚本。

现在,当我们运行服务器时,它将返回一个404状态码,这意味着在服务器上找不到资源:

因此,现在我们知道,我们可以向服务器请求目录和文件的列表,并找到哪些目录和文件存在,哪些不存在。很有趣,对吧?

现在,让我们看看如何处理重定向。我们将使用一个示例页面,该页面将获取一个参数 URL 并将我们重定向到该已定义的 URL:

  1. 让我们回到我们的脚本并修改它,以获得一个名为payload的新目录,其中将包含我们要重定向到的 URL。
  2. 我们将使用payload='url'重定向到www.bing.com。我们可以这样做:
payload={'url':'http://www.bing.com'} 
  1. 现在,我们将使用这个,资源重定向到并添加params参数,并将其设置为payload
  2. 最后,我们将使用print req.text打印内容:
import requests
url='http://httpbin.org/redirect-to'
payload = {'url':'http://www.bing.com'}
req = requests.get(url,params=payload)
print req.text
print "Response code: " + str(req.status_code)
  1. 我们将保存它并再次运行它。

我们现在得到了什么?A200编码及内容 https://www.bing.com/

代码应该是302,对吗?我们需要访问请求的历史记录以查看重定向。

  1. 让我们添加print r.history。历史记录是重定向链中所有响应的列表。我们将通过这个循环将 URL 和每个 URL 的响应代码打印到脚本中。
  2. 对于x in req.history,打印与 URL 连接的状态码:
import requests
url='http://httpbin.org/redirect-to'
payload = {'url':'http://www.bing.com'}
req = requests.get(url,params=payload)
print req.text
print "Response code: " + str(req.status_code)
for x in req.history:
        print str(x.status_code) + ' : ' + x.url
  1. 保存并运行它:

现在我们可以看到,在200之前,有一个302重定向代码将我们的浏览器发送到www.bing.com

总结

在本章中,我们简要介绍了 HTTP,并看到了一个基本的GET请求示例。我们还看到了可用于与 web 应用交互的不同 HTTP 方法。

我们还了解了 HTTP 请求。我们学习了如何使用 Python 和requests库与 web 应用交互。我们进一步了解了 HTTP 请求剖析以及不同的 HTTP 方法和响应代码。

第 3 章中,我们将学习如何编写 Web 爬虫程序,使用 Python 使用 Spider,以及如何使用 Scrasty 库。