0.0.1 2022-03-16 23:00 UTC

This package is not auto-updated.

Last update: 2024-09-13 09:17:18 UTC


README

Hawk 是一种使用消息认证码 (MAC) 算法提供的部分 HTTP 请求加密验证的 HTTP 认证方案。 —— hawk README

安装

通过 Composer 作为 dflydev/hawk 安装。

客户端

构建客户端

Client 有一些必需的依赖。通常使用 ClientBuilder 构建客户端会更简单。可以不设置任何内容来构建一个 Client,以获取合理的默认值。

简单的 ClientBuilder 示例

<?php

// Simple example
$client = Dflydev\Hawk\Client\ClientBuilder::create()
    ->build()

完整的 ClientBuilder 示例

<?php

// A complete example
$client = Dflydev\Hawk\Client\ClientBuilder::create()
    ->setCrypto($crypto)
    ->setTimeProvider($timeProvider)
    ->setNonceProvider($nonceProvider)
    ->setLocaltimeOffset($localtimeOffset)
    ->build()

创建请求

为了使客户端能够签名请求,它需要知道发出请求的用户凭据、URL、方法以及请求的负载和内容类型(可选)。

所有可用选项包括

  • payload:请求体
  • content_type:请求的内容类型
  • nonce:如果应使用特定 nonce 而不是由 nonce 提供程序自动生成的 nonce。
  • ext:此请求特定的 ext 值
  • app:此请求的应用程序(Oz 特定)
  • dlg:此请求的委托-by 值(Oz 特定)

创建请求示例

<?php

$request = $client->createRequest(
    $credentials,
    'http://example.com/foo/bar?whatever',
    'POST',
    array(
        'payload' => 'hello world!',
        'content_type' => 'text/plain',
    )
);

// Assuming a hypothetical $headers object that can be used to add new headers
// to an outbound request, we can add the resulting 'Authorization' header
// for this Hawk request by doing:
$headers->set(
    $request->header()->fieldName(), // 'Authorization'
    $request->header()->fieldValue() // 'Hawk id="12345", mac="ad8c9f', ...'
);

客户端请求对象

Request 代表客户端需要了解有关请求的所有内容,包括头和用于创建请求的工件。

  • header():表示请求的 Header 实例
  • artifacts():包含在创建请求时使用的值的 Artifacts 实例

头是必需的,以便能够获取正确格式的 Hawk 授权头并发送到服务器。工件在服务器响应进行身份验证时很有用。

验证服务器响应

Hawk 提供了客户端验证服务器响应的功能,以确保返回的响应来自预期的目标。

所有可用选项包括

  • payload:响应体
  • content_type:响应的内容类型

验证响应示例

<?php

// Assuming a hypothetical $headers object that can be used to get headers sent
// back as the response of a user agent request, we can get the value for the
// 'Server-Authorization' header.
$header = $headers->get('Server-Authorization');

// We need to use the original credentials, the original request, the value
// for the 'Server-Authorization' header, and optionally the payload and
// content type of the response from the server.
$isAuthenticatedResponse = $client->authenticate(
    $credentials,
    $request,
    $header,
    array(
        'payload' => '{"message": "good day, sir!"}',
        'content_type' => 'application/json',
    )
);

完整的客户端示例

<?php

// Create a set of Hawk credentials
$credentials = new Dflydev\Hawk\Credentials\Credentials(
    'afe89a3x',  // shared key
    'sha256',    // default: sha256
    '12345'      // identifier, default: null
);

// Create a Hawk client
$client = Dflydev\Hawk\Client\ClientBuilder::create()
    ->build();

// Create a Hawk request based on making a POST request to a specific URL
// using a specific user's credentials. Also, we're expecting that we'll
// be sending a payload of 'hello world!' with a content-type of 'text/plain'.
$request = $client->createRequest(
    $credentials,
    'http://example.com/foo/bar?whatever',
    'POST',
    array(
        'payload' => 'hello world!',
        'content_type' => 'text/plain',
    )
);

// Ask a really useful fictional user agent to make a request; note that the
// request we are making here matches the details that we told the Hawk client
// about our request.
$response = Fictional\UserAgent::makeRequest(
    'POST',
    'http://example.com/foo/bar?whatever',
    array(
        'content_type' => 'text/plain',
        $request->header()->fieldName() => $request->header()->fieldValue(),
    ),
    'hello world!'
);

// This part is optional but recommended! At this point if we have a successful
// response we could just look at the content and be done with it. However, we
// are given the tools to authenticate the response to ensure that the response
// we were given came from the server we were expecting to be talking to.
$isAuthenticatedResponse = $client->authenticate(
    $credentials,
    $request,
    $response->headers->get('Server-Authorization'),
    array(
        'payload' => $response->getContent(),
        'content_type' => $response->headers->get('content-type'),
    )
);

if (!$isAuthenticatedResponse) {
    die("The server did a very bad thing...");
}

// Huzzah!

Bewit

Hawk 支持使用名为 bewit 的查询参数授予第三方临时访问单个资源的方法。

返回值是一个表示 bewit 的字符串。应将此字符串附加到请求的 URI 的末尾,以便将其添加到请求 URI。如果 URI 已经有查询参数,则 bewit 应具有 &bewit= 附加到其前面。如果 URI 没有查询参数,则 bewit 应具有 ?bewit= 附加到其前面。

客户端 Bewit 示例

<?php

$bewit = $client->createBewit(
    $credentials,
    'https://example.com/posts?foo=bar',
    300 // ttl in seconds
);

服务器

构建服务器

Server 有一些必需的依赖。通常使用 ServerBuilder 构建服务器会更简单。可以不设置任何内容(除了凭据提供程序)以获取合理的默认值。

简单的 ServerBuilder 示例

<?php

$credentialsProvider = function ($id) {
    if ('12345' === $id) {
        return new Dflydev\Hawk\Credentials\Credentials(
            'afe89a3x',  // shared key
            'sha256',    // default: sha256
            '12345'      // identifier, default: null
        );
    }
};

// Simple example
$server = Dflydev\Hawk\Server\ServerBuilder::create($credentialsProvider)
    ->build()

完整的 ServerBuilder 示例

<?php

$credentialsProvider = function ($id) {
    if ('12345' === $id) {
        return new Dflydev\Hawk\Credentials\Credentials(
            'afe89a3x',  // shared key
            'sha256',    // default: sha256
            '12345'      // identifier, default: null
        );
    }
};

// A complete example
$server = Dflydev\Hawk\Server\ServerBuilder::create($credentialsProvider)
    ->setCrypto($crypto)
    ->setTimeProvider($timeProvider)
    ->setNonceValidator($nonceValidator)
    ->setTimestampSkewSec($timestampSkewSec)
    ->setLocaltimeOffsetSec($localtimeOffsetSec)
    ->build()

验证请求

为了服务器能够对请求进行认证,它需要能够构建与客户端相同的MAC。它是通过获取客户端在签名请求时所知道关于请求的相同信息来做到这一点的。

特别是,授权头应该包含ID。该ID用于检索凭据(特别是密钥),以便根据请求的其他信息计算MAC。

认证示例

<?php

// Get the authorization header for the request; it should be in the form
// of 'Hawk id="...", mac="...", [...]'
$authorization = $headers->get('Authorization');

try {
    $response = $server->authenticate(
        'POST',
        'example.com',
        80,
        '/foo/bar?whatever',
        'text/plain',
        'hello world!'
        $authorization
    );
} catch(Dflydev\Hawk\Server\UnauthorizedException $e) {
    // If authorization is incorrect (invalid mac, etc.) we can catch an
    // unauthorized exception.
    throw $e;
}

// The credentials associated with this request. This is where one could access
// the ID for the user that made this request.
$credentials = $response->credentials();

// The artifacts associated with this request. This is where one could access
// things like the 'ext', 'app', and 'dlg' values sent with the request.
$artifacts = $response->artifacts();

服务器响应对象

Response代表了服务器需要知道关于请求的所有信息,包括与请求关联的凭据和工件。

  • credentials()
  • artifacts()

创建响应头

Hawk为服务器提供了对响应进行签名的能力,以便为客户端提供一种验证服务器响应的方法。

所有可用选项包括

  • payload:请求体
  • content_type:请求的内容类型
  • ext:此请求特定的 ext 值

创建响应头示例

<?php

// Using the same credentials and artifacts from the server authenticate
// response, we can create a 'Server-Authorization' header.
$header = $server->createHeader($credentials, $artifacts, array(
    'payload' => '{"message": "good day, sir!"}',
    'content_type' => 'application/json',
));

// Set the header using PHP's header() function.
header(sprintf("%s: %s", $header->fieldName(), $header->fieldValue()));

完整服务器示例

<?php

// Create a simple credentials provider
$credentialsProvider = function ($id) {
    if ('12345' === $id) {
        return new Dflydev\Hawk\Credentials\Credentials(
            'afe89a3x',  // shared key
            'sha256',    // default: sha256
            '12345'      // identifier, default: null
        );
    }
};

// Create a Hawk server
$server = Dflydev\Hawk\Server\ServerBuilder::create($credentialsProvider)
    ->build()

// Get the authorization header for the request; it should be in the form
// of 'Hawk id="...", mac="...", [...]'
$authorization = $headers->get('Authorization');

try {
    $response = $server->authenticate(
        'POST',
        'example.com',
        80,
        '/foo/bar?whatever',
        'text/plain',
        'hello world!'
        $authorization
    );
} catch(Dflydev\Hawk\Server\UnauthorizedException $e) {
    // If authorization is incorrect (invalid mac, etc.) we can catch an
    // unauthorized exception.
    throw $e;
}

// Huzzah! Do something at this point with the request as we now know that
// it is an authenticated Hawk request.
//
// ...
//
// Ok we are done doing things! Assume based on what we did we ended up deciding
// the following payload and content type should be used:

$payload = '{"message": "good day, sir!"}';
$contentType = 'application/json';

// Create a Hawk header to sign our response
$header = $server->createHeader($credentials, $artifacts, array(
    'payload' => $payload,
    'content_type' => $contentType,
));

// Send some headers
header(sprintf("%s: %s", 'Content-Type', 'application/json'));
header(sprintf("%s: %s", $header->fieldName(), $header->fieldValue()));

// Output our payload
print $payload;

Bewit

Hawk 支持使用名为 bewit 的查询参数授予第三方临时访问单个资源的方法。

Bewit认证应仅适用于GETHEAD请求。经过认证的bewit的返回值是一个服务器响应对象。

服务器Bewit示例

<?php

$response = $server->authenticateBewit(
    'example.com',
    443,
    '/posts?bewit=ZXhxYlpXdHlrRlpJaDJEN2NYaTlkQVwxMzY4OTk2ODAwXE8wbWhwcmdvWHFGNDhEbHc1RldBV3ZWUUlwZ0dZc3FzWDc2dHBvNkt5cUk9XA'
);

加密

Dflydev\Hawk\Crypto\Crypto

用于计算和比较MAC值的工具。

  • calculatePayloadHash($payload, $algorithm, $contentType)
  • calculateMac($type, CredentialsInterface $credentials, Artifacts $attributes)
  • calculateTsMac($ts, CredentialsInterface $credentials)
  • fixedTimeComparison($a, $b)
    用于确保比较两个字符串时始终需要相同的时间,无论它们是否相同。

Dflydev\Hawk\Crypto\Artifacts

用于存放所有可能用于创建MAC的数据片段的容器。

凭据

Dflydev\Hawk\Credentials\CredentialsInterface

表示一组有效的凭据。

  • key():用于计算MAC
  • algorithm():用于计算散列的算法
  • id():密钥所属的标识符(例如,用户名)

在某些上下文中,可能只知道密钥。

Dflydev\Hawk\Credentials\Credentials

CredentialsInterface的一个简单实现。

<?php

$credentials = new Dflydev\Hawk\Credentials\Credentials(
    $key,        // shared key
    $algorithm,  // default: sha256
    $id          // identifier, default: null
);

Dflydev\Hawk\Header\Header

  • fieldName():头字段的名称
  • fieldValue():头字段的值
  • attributes():用于构建字段值的属性

Dflydev\Hawk\Header\HeaderFactory

  • create($fieldName, array $attributes = null)
    为给定字段名和一组属性创建一个Hawk头。

  • createFromString($fieldName, $fieldValue, array $requiredKeys = null)
    从给定的Hawk值字符串创建一个Hawk头。例如,'Hawk id="foo", mac="1234"' 就是一个Hawk值字符串的例子。这对于将传入的头部值转换为Hawk值非常有用。

    抛出异常

    • Dflydev\Hawk\Header\FieldValueParserException
    • Dflydev\Hawk\Header\NotHawkAuthorizationException

Dflydev\Hawk\Header\HeaderParser

  • parseFieldValue($fieldValue, array $requiredKeys = null)
    将字段值字符串解析为属性关联数组。

    抛出异常

    • Dflydev\Hawk\Header\FieldValueParserException
    • Dflydev\Hawk\Header\NotHawkAuthorizationException

Dflydev\Hawk\Header\FieldValueParserException

表示字符串声称是Hawk字符串,但不能完全解析。这通常是一个头部值损坏或格式不正确的迹象。

Dflydev\Hawk\Header\NotHawkAuthorizationException

表示字符串与Hawk无关。目前意味着字符串不以 'Hawk' 开头。

许可证

MIT,请参阅LICENSE。

社区

如果您有问题或想帮忙,请加入我们 #dflydevirc.freenode.net