dflydev/hawk

Hawk

v0.0.0 2013-10-30 17:30 UTC

This package is auto-updated.

Last update: 2024-09-21 23:20:39 UTC


README

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

安装

通过 Composerdflydev/hawk 的形式。

客户端

构建客户端

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

简单的 ClientBuilder 示例

<?php

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

完整的 ClientBuilderExample

<?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: 此请求的委托值 (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 实例

需要 header 才能获取正确格式化的 Hawk 授权头部并发送到服务器。 artifacts 在服务器响应进行身份验证时很有用。

验证服务器响应

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 已经有查询参数,bewit 应在前面附加 &bewit=。如果 URI 没有查询参数,bewit 应在前面附加 ?bewit=

客户端 Bewit 示例

<?php

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

服务器

构建服务器

服务器有一些必需的依赖项。通常,通过使用 ServerBuilder 来构建 Server 更容易。构建一个 Server 可以不设置任何东西,只需提供凭证提供者即可获取合理的默认值。

简单的 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 值字符串的示例。这对于将传入的头部值转换为字符串非常有用。

    抛出

    • 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