Генератор ключей PHP API

Кто-нибудь знает какой-либо сценарий/класс генератора ключей API для PHP? Класс должен иметь метод generate, который будет генерировать ключ и метод isValid() для проверки правильности ключа.


person xpepermint    schedule 19.09.2009    source источник
comment
Если вы можете предоставить больше информации о предполагаемом использовании, ответ может быть немного проще... ключи хранятся где-то - например, в БД? есть ли связь клиент/сервер?   -  person J.C. Inacio    schedule 19.09.2009
comment
GUID такой же, как № 1 в моем ответе. Мы попробовали UUID. 34 символа слишком велики, на наш вкус. Он выглядит как 3B90E4C1-C4D4-D204-ECB3-0CDB0F0A2E50. Если вы можете принять это, действуйте.   -  person ZZ Coder    schedule 19.09.2009


Ответы (11)


Существует несколько способов генерации ключей API. Я использовал следующие 3 метода в зависимости от приложений,

  1. Base62 (случайный). Сгенерируйте большое безопасное случайное число и закодируйте его в Base-62. Ключ выглядит как «w5vt2bjzf8ryp63t». Это хорошо для самостоятельной системы. Вам не нужно беспокоиться о коллизиях и неподходящих ключах. Вы должны проверить базу данных, чтобы узнать, действителен ли ключ.

  2. Base62 (MD5-HMAC (ключ, нормализовать (реферер))). Это прекрасно работает, если API разрешено только с одного сайта. Просто проверьте HMAC нормализованного реферера, и вы узнаете, действителен ли ключ, нет доступа к базе данных. Для этого вам нужно держать ключ HMAC в секрете.

  3. Дружественные имена, присвоенные человеком, такие как «example.com». Это отлично работает, если пользователи API должны владеть доменом или они являются вашими корпоративными партнерами.

Пожалуйста, имейте в виду, что в ключах API нет безопасности. Это просто имя, присвоенное вашему приложению API. Все больше и больше людей используют такие термины, как «идентификатор приложения» или «идентификатор разработчика», чтобы показать, что это такое на самом деле. Вам нужно назначить другой секретный ключ, если вы хотите защитить свой протокол, например consumer_key/consumer_secret в OAuth.

person ZZ Coder    schedule 19.09.2009
comment
Любопытство: почему именно base62? Почему не base64 например? - person Chris Harrison; 28.11.2011
comment
Base 62 ограничивает результирующий набор символов (A-Za-z0-9). (26+26+10=62) Это означает, что вы можете сделать определенные удобные предположения в своем приложении о составе ключей. Они также кажутся более последовательными (поскольку они просто буквенно-цифровые), чем Base64. - person rinogo; 12.01.2012
comment
Есть ли безопасность в случайных ключах при использовании в сочетании с SSL? - person MrMedicine; 05.01.2021

Вот мой простой ответ на этот вопрос:

$key = implode('-', str_split(substr(strtolower(md5(microtime().rand(1000, 9999))), 0, 30), 6));
person Gabriel Glauber    schedule 12.05.2016

просто используйте что-то вроде этого (псевдокод) sha1(salt + time + mac-addr + another salt + some other random data) crc32 или md5 также будет работать вместо sha1 и сохранит его в базе данных, а затем isValid () проверяет БД, существует ли ключ?

person thr    schedule 19.09.2009
comment
Это была моя первая мысль :). - person xpepermint; 19.09.2009
comment
Не забывайте, что вы не можете просто взять контрольную сумму SHA1 (или любого другого хэша) и проверить, действительна ли она, если только вы не предоставите все остальные данные... - person J.C. Inacio; 19.09.2009
comment
вы не можете найти MAC-адрес целевого компьютера, если он не находится в вашей локальной сети. не забывай! - person dusoft; 19.09.2009
comment
Зачем добавлять несколько солей? Это добавляет только возможной путаницы, а не делает что-то более «безопасным». - person nilamo; 19.09.2009
comment
@nimalo: Вы правы на 100%, я думаю, что мне нужно ввести другие случайные данные или что-то еще. - person thr; 19.09.2009

Ну, как уже было сказано, все зависит от ситуации. Один метод, который мне нужно было использовать, заключался в аутентификации URL-адреса реферера с помощью специально назначенного ключа API. Таким образом, с ключом API все, что действительно было нужно, это (псевдо) key = md5(referer url + name + salt), для которого вы затем можете получить контрольную сумму. Я знаю, что подобное уже упоминалось ранее, но это именно так. Что касается функции isValid(), все, что вам нужно сделать, это сравнить ее с контрольной суммой и URL-адресом.

Изменить: только что понял возраст исходного вопроса: S

person Marc Towler    schedule 01.04.2011

В зависимости от того, что вы хотите, вы также можете использовать что-то вроде 3scale для создания ключей и управления доступом к API. Он генерирует ключи, но также отслеживает ограничения скорости, аналитику и т. д. и позволяет разработчикам API создавать новые ключи.

В качестве одного из коннекторов используется библиотека PHP: https://support.3scale.net/reference/libraries

person steve    schedule 02.05.2012

Вы можете просто использовать md5(uniqid()) и разделить его на части или отформатировать другим предпочтительным способом.

person Skiff    schedule 06.04.2016

Это старый вопрос, но вчера я наткнулся на ту же проблему и нашел этот класс, совместимый с RFC4122:

/*-
 * Copyright (c) 2008 Fredrik Lindberg - http://www.shapeshifter.se
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */

/*
 * UUID (RFC4122) Generator
 * http://tools.ietf.org/html/rfc4122
 *
 * Implements version 1, 3, 4 and 5
 */
class GEN_UUID {
    /* UUID versions */
    const UUID_TIME     = 1;    /* Time based UUID */
    const UUID_NAME_MD5     = 3;    /* Name based (MD5) UUID */
    const UUID_RANDOM     = 4;    /* Random UUID */
    const UUID_NAME_SHA1     = 5;    /* Name based (SHA1) UUID */

    /* UUID formats */
    const FMT_FIELD     = 100;
    const FMT_STRING     = 101;
    const FMT_BINARY     = 102;
    const FMT_QWORD     = 1;    /* Quad-word, 128-bit (not impl.) */
    const FMT_DWORD     = 2;    /* Double-word, 64-bit (not impl.) */
    const FMT_WORD         = 4;    /* Word, 32-bit (not impl.) */
    const FMT_SHORT        = 8;    /* Short (not impl.) */
    const FMT_BYTE        = 16;    /* Byte */
    const FMT_DEFAULT     = 16;


    public function __construct()
    {
    }


    /* Field UUID representation */
    static private $m_uuid_field = array(
        'time_low' => 0,        /* 32-bit */
        'time_mid' => 0,        /* 16-bit */
        'time_hi' => 0,            /* 16-bit */
        'clock_seq_hi' => 0,        /*  8-bit */
        'clock_seq_low' => 0,        /*  8-bit */
        'node' => array()        /* 48-bit */
    );

    static private $m_generate = array(
        self::UUID_TIME => "generateTime",
        self::UUID_RANDOM => "generateRandom",
        self::UUID_NAME_MD5 => "generateNameMD5",
        self::UUID_NAME_SHA1 => "generateNameSHA1"
    );

    static private $m_convert = array(
        self::FMT_FIELD => array(
            self::FMT_BYTE => "conv_field2byte",
            self::FMT_STRING => "conv_field2string",
            self::FMT_BINARY => "conv_field2binary"
        ),
        self::FMT_BYTE => array(
            self::FMT_FIELD => "conv_byte2field",
            self::FMT_STRING => "conv_byte2string",
            self::FMT_BINARY => "conv_byte2binary"
        ),
        self::FMT_STRING => array(
            self::FMT_BYTE => "conv_string2byte",
            self::FMT_FIELD => "conv_string2field",
            self::FMT_BINARY => "conv_string2binary"
        ),
    );

    /* Swap byte order of a 32-bit number */
    static private function swap32($x) {
        return (($x & 0x000000ff) << 24) | (($x & 0x0000ff00) << 8) |
            (($x & 0x00ff0000) >> 8) | (($x & 0xff000000) >> 24);
    }

    /* Swap byte order of a 16-bit number */
    static private function swap16($x) {
        return (($x & 0x00ff) << 8) | (($x & 0xff00) >> 8);
    }

    /* Auto-detect UUID format */
    static private function detectFormat($src) {
        if (is_string($src))
            return self::FMT_STRING;
        else if (is_array($src)) {
            $len = count($src);
            if ($len == 1 || ($len % 2) == 0)
                return $len;
            else
                return (-1);
        }
        else
            return self::FMT_BINARY;
    }

    /*
     * Public API, generate a UUID of 'type' in format 'fmt' for
     * the given namespace 'ns' and node 'node'
     */
    static public function generate($type=self::UUID_RANDOM, $fmt = self::FMT_STRING, $node = "", $ns = "") {
        $func = self::$m_generate[$type];
        if (!isset($func))
            return null;
        $conv = self::$m_convert[self::FMT_FIELD][$fmt];

        $uuid = self::$func($ns, $node);
        return self::$conv($uuid);
    }

    /*
     * Public API, convert a UUID from one format to another
     */
    static public function convert($uuid, $from, $to) {
        $conv = self::$m_convert[$from][$to];
        if (!isset($conv))
            return ($uuid);

        return (self::$conv($uuid));
    }

    /*
     * Generate an UUID version 4 (pseudo random)
     */
    static private function generateRandom($ns, $node) {
        $uuid = self::$m_uuid_field;

        $uuid['time_hi'] = (4 << 12) | (mt_rand(0, 0x1000));
        $uuid['clock_seq_hi'] = (1 << 7) | mt_rand(0, 128);
        $uuid['time_low'] = mt_rand(0, 0xffff) + (mt_rand(0, 0xffff) << 16);
        $uuid['time_mid'] = mt_rand(0, 0xffff);
        $uuid['clock_seq_low'] = mt_rand(0, 255);
        for ($i = 0; $i < 6; $i++)
            $uuid['node'][$i] = mt_rand(0, 255);
        return ($uuid);
    }

    /*
     * Generate UUID version 3 and 5 (name based)
     */
    static private function generateName($ns, $node, $hash, $version) {
        $ns_fmt = self::detectFormat($ns);
        $field = self::convert($ns, $ns_fmt, self::FMT_FIELD);

        /* Swap byte order to keep it in big endian on all platforms */
        $field['time_low'] = self::swap32($field['time_low']);
        $field['time_mid'] = self::swap16($field['time_mid']);
        $field['time_hi'] = self::swap16($field['time_hi']);

        /* Convert the namespace to binary and concatenate node */
        $raw = self::convert($field, self::FMT_FIELD, self::FMT_BINARY);
        $raw .= $node;

        /* Hash the namespace and node and convert to a byte array */
        $val = $hash($raw, true);    
        $tmp = unpack('C16', $val);
        foreach (array_keys($tmp) as $key)
            $byte[$key - 1] = $tmp[$key];

        /* Convert byte array to a field array */
        $field = self::conv_byte2field($byte);

        $field['time_low'] = self::swap32($field['time_low']);
        $field['time_mid'] = self::swap16($field['time_mid']);
        $field['time_hi'] = self::swap16($field['time_hi']);

        /* Apply version and constants */
        $field['clock_seq_hi'] &= 0x3f;
        $field['clock_seq_hi'] |= (1 << 7);
        $field['time_hi'] &= 0x0fff;
        $field['time_hi'] |= ($version << 12);

        return ($field);    
    }
    static private function generateNameMD5($ns, $node) {
        return self::generateName($ns, $node, "md5",
            self::UUID_NAME_MD5);
    }
    static private function generateNameSHA1($ns, $node) {
        return self::generateName($ns, $node, "sha1",
            self::UUID_NAME_SHA1);
    }

    /*
     * Generate UUID version 1 (time based)
     */
    static private function generateTime($ns, $node) {
        $uuid = self::$m_uuid_field;

        /*
         * Get current time in 100 ns intervals. The magic value
         * is the offset between UNIX epoch and the UUID UTC
         * time base October 15, 1582.
         */
        $tp = gettimeofday();
        $time = ($tp['sec'] * 10000000) + ($tp['usec'] * 10) +
            0x01B21DD213814000;

        $uuid['time_low'] = $time & 0xffffffff;
        /* Work around PHP 32-bit bit-operation limits */
        $high = intval($time / 0xffffffff);
        $uuid['time_mid'] = $high & 0xffff;
        $uuid['time_hi'] = (($high >> 16) & 0xfff) | (self::UUID_TIME << 12);

        /*
         * We don't support saved state information and generate
         * a random clock sequence each time.
         */
        $uuid['clock_seq_hi'] = 0x80 | mt_rand(0, 64);
        $uuid['clock_seq_low'] = mt_rand(0, 255);

        /*
         * Node should be set to the 48-bit IEEE node identifier, but
         * we leave it for the user to supply the node.
         */
        for ($i = 0; $i < 6; $i++)
            $uuid['node'][$i] = ord(substr($node, $i, 1));

        return ($uuid);
    }

    /* Assumes correct byte order */
    static private function conv_field2byte($src) {
        $uuid[0] = ($src['time_low'] & 0xff000000) >> 24;
        $uuid[1] = ($src['time_low'] & 0x00ff0000) >> 16;
        $uuid[2] = ($src['time_low'] & 0x0000ff00) >> 8;
        $uuid[3] = ($src['time_low'] & 0x000000ff);
        $uuid[4] = ($src['time_mid'] & 0xff00) >> 8;
        $uuid[5] = ($src['time_mid'] & 0x00ff);
        $uuid[6] = ($src['time_hi'] & 0xff00) >> 8;
        $uuid[7] = ($src['time_hi'] & 0x00ff);
        $uuid[8] = $src['clock_seq_hi'];
        $uuid[9] = $src['clock_seq_low'];

        for ($i = 0; $i < 6; $i++)
            $uuid[10+$i] = $src['node'][$i];

        return ($uuid);
    }

    static private function conv_field2string($src) {
        $str = sprintf(
            '%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x',
            ($src['time_low']), ($src['time_mid']), ($src['time_hi']),
            $src['clock_seq_hi'], $src['clock_seq_low'],
            $src['node'][0], $src['node'][1], $src['node'][2],
            $src['node'][3], $src['node'][4], $src['node'][5]);
        return ($str);
    }

    static private function conv_field2binary($src) {
        $byte = self::conv_field2byte($src);
        return self::conv_byte2binary($byte);
    }

    static private function conv_byte2field($uuid) {
        $field = self::$m_uuid_field;
        $field['time_low'] = ($uuid[0] << 24) | ($uuid[1] << 16) |
            ($uuid[2] << 8) | $uuid[3];
        $field['time_mid'] = ($uuid[4] << 8) | $uuid[5];
        $field['time_hi'] = ($uuid[6] << 8) | $uuid[7];
        $field['clock_seq_hi'] = $uuid[8];
        $field['clock_seq_low'] = $uuid[9];

        for ($i = 0; $i < 6; $i++)
            $field['node'][$i] = $uuid[10+$i];
        return ($field);
    }

    static public function conv_byte2string($src) {
        $field = self::conv_byte2field($src);
        return self::conv_field2string($field);
    }

    static private function conv_byte2binary($src) {
        $raw = pack('C16', $src[0], $src[1], $src[2], $src[3],
            $src[4], $src[5], $src[6], $src[7], $src[8], $src[9],
            $src[10], $src[11], $src[12], $src[13], $src[14], $src[15]);
        return ($raw);
    }

    static private function conv_string2field($src) {
        $parts = sscanf($src, '%x-%x-%x-%x-%02x%02x%02x%02x%02x%02x');
        $field = self::$m_uuid_field;
        $field['time_low'] = ($parts[0]);
        $field['time_mid'] = ($parts[1]);
        $field['time_hi'] = ($parts[2]);
        $field['clock_seq_hi'] = ($parts[3] & 0xff00) >> 8;
        $field['clock_seq_low'] = $parts[3] & 0x00ff;
        for ($i = 0; $i < 6; $i++)
            $field['node'][$i] = $parts[4+$i];

        return ($field);
    }

    static private function conv_string2byte($src) {
        $field = self::conv_string2field($src);
        return self::conv_field2byte($field);
    }

    static private function conv_string2binary($src) {
        $byte = self::conv_string2byte($src);
        return self::conv_byte2binary($byte);
    }
}

надеюсь, это поможет

person Eric    schedule 13.08.2015

См. uniqid:

string uniqid ([ string $prefix = "" [, bool $more_entropy = false ]] )
person Martin Thoma    schedule 14.05.2014
comment
Думаю, людей отталкивает этот абзац: Warning: This function does not create random nor unpredictable strings. This function must not be used for security purposes. Иначе: Приятно знать, что он есть. - person Philzen; 30.07.2014
comment
@Philzen: Возможно. password_hash может быть интересной альтернативой. - person Martin Thoma; 30.07.2014

GUID будет работать, но не является криптографически безопасным.

В ответах сервера используются методы хэширования md5 или sha1 для microtime() или mt_rand.

Хеширование uniqid, uuid или метки времени не обязательно приведет к уникальным результатам! На самом деле хеширование увеличивает вероятность коллизий, поэтому я бы настоятельно не советовал этого делать.

person 24HOURSMEDIA    schedule 08.10.2017

Если вам не нужен ключ API, чтобы что-то означать или быть декодируемым, просто чтобы с чем-то совпадать, тогда это работает:

$api = md5(rand(1, 99999));
person Blaze612 YT    schedule 15.03.2021

Вы можете просто сделать что-то простое, например md5(time()) ->, и просто сохранить результат в БД для будущей проверки.

person AHeraldOfTheNewAge    schedule 13.07.2021
comment
Пожалуйста, не делай этого. Если два запроса обрабатываются за одну и ту же миллисекунду (вполне вероятно), два пользователя получат один и тот же ключ API :S - person Fulldump; 14.07.2021
comment
Это имеет смысл, это отличный отзыв! Я думаю, что быстрое исправление объединяет эту функцию time() с некоторым случайным числом, поэтому что-то вроде: md5(time().rand()) будет лучше - person AHeraldOfTheNewAge; 15.07.2021