Documentation

Secure Method Calls

Important! Calling methods in a secure way is required for commercial applications. Developers have to use it to acquire commercial status for their applications. This technique is as well useful to improve the security of user access to application data.

Usage at a Glance

  1. To call a method securely, an application sends an extra parameter, the state that can take any value;
  2. When a method is called with this parameter, the response includes a special parameter, the signature, whose value is a signature for the method's important data created using JWS (JSON Web Signature);
  3. the application verifies the signature and, if it is valid, obtains data returned by the method.

Signature Algorithm and Format

The system accepts a string serialized to JASON and containing the method data plus the value of the state parameter passed by the application. The string is encoded to base64, and a hash value is calculated on it using hmac-sha256. The hash key is a string which is an md5 of the concatenated Intranet's member_id (passed to the application in authentication parameters) and the application's client_secret value. The signature itself is a concatenation of the base64 data string and the hash value separated with a period ".".

Signature Verification

To verify the signature:

  • the application splits it at the period character ".";
  • calculates a hash value on the left substring using the received member_id and its own client_secret;
  • compares the calculated hash value to the right substring of the signature.

If the hash values match:

  • the application decodes the left substring from base64 to a JSON string;
  • extracts data from it and compares the extracted value of state to that sent when calling a method.

It is obvious that the value for the state parameter needs to be as unpredictable as possible. The best option is a string of random characters.

Signature API's

The Social Network module version 14.0.3 includes a special class  \Bitrix\Socialservices\Bitrix24Signer implementing the signature API's.

The following code shows how to parse a signature in PHP:

function unsign($signedValue, $key)
{
   if (strpos($signedValue, '.') === false)
      return array();

   $pos = strrpos($signedValue, '.');
   $value = substr($signedValue, 0, $pos);
   $signature = base64_decode(substr($signedValue, $pos + 1));
   $hash = hash_hmac('sha256', $value, $key, true);
   if ($hash !== $signature)
      return array();

   return json_decode(base64_decode($value));
}  
 
$member_id = $_REQUEST['MEMBER_ID']; 
$client_secret = 'my_application_secret_key'; 

$signature = 'eyJWRVJTSU9OIjoxLCJTVEFUVVMiOiJGIiwic3RhdGUiOiJzb21lIHN0YXRlIn0=.2oGQ22n1GJfpIbxA9BaLz0bvu7ox0uyd8DbwoJGLkoY='; 

var_dump(unsign($signature, md5($member_id.$client_secret)));
/*
object(stdClass)[46]
  public 'VERSION' => int 1
  public 'STATUS' => string 'F' (length=1)
  public 'state' => string 'some state' (length=10)
*/ 

This code uses Bitrix Framework to verify the signature:

$encodedValue = 'eyJWRVJTSU9OIjoxLCJTVEFUVVMiOiJGIiwic3RhdGUiOiJzb21lIHN0YXRlIn0=.2oGQ22n1GJfpIbxA9BaLz0bvu7ox0uyd8DbwoJGLkoY=';

$member_id = $_REQUEST['MEMBER_ID']; 
$client_secret = 'my_application_secret_key'; 

$key = md5($member_id.$client_secret);

CModule::IncludeModule('socialservices');
$signer = (new Bitrix\Socialservices\Bitrix24Signer)->setKey($key); 
try
{
   var_dump($signer->unsign($encodedValue));
}
catch (Bitrix\Main\Security\Sign\BadSignatureException $e)
{
   echo 'Error!';
}

An example in Python:

import hashlib
import hmac
import json
import base64


def unsign(signed_value, key, sep=b'.'):
    if sep not in signed_value:
        return []

    value, signature = signed_value.rsplit(sep, 1)
    mac = hmac.new(key, msg=value, digestmod=hashlib.sha256)
    signature = base64.b64decode(signature)
    if mac.digest() != signature:
        return []

    return json.loads(base64.b64decode(value).decode())

if __name__ == '__main__':
    member_id = b'03d59e663c1af9ac33a9949d1193505a'
    client_secret = b'100b8cad7cf2a56f6df78f171f97a1ec'
    key = hashlib.md5(member_id + client_secret).hexdigest().encode('UTF-8')
    value = unsign(
        signed_value=b'eyJWRVJTSU9OIjoxLCJzdGF0ZSI6InNvbWUgc3RhdGUiLCJTVEFUVVMiOiJGIn0=.hZMYGHDETn7gz4wX2Lv/879ofMcJJ5bVL3OhR02FWkc=',
        key=key
    )
    print(value) 
    #{'STATUS': 'F', 'state': 'some state', 'VERSION': 1}

Or you might want to use Java:

import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.digest.DigestUtils;

import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
import org.json.simple.parser.ParseException;

import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;


public class Bitrix24UnsignExample {

    public static void main(String[] args) {
        try {
            String memberId = "03d59e663c1af9ac33a9949d1193505a";
            String clientSecret = "100b8cad7cf2a56f6df78f171f97a1ec";
            String key = DigestUtils.md5Hex(memberId + clientSecret);
            String signedValue = "eyJWRVJTSU9OIjoxLCJzdGF0ZSI6InNvbWUgc3RhdGUiLCJTVEFUVVMiOiJGIn0=.hZMYGHDETn7gz4wX2Lv/879ofMcJJ5bVL3OhR02FWkc=";
            JSONObject result = unsign(signedValue, key);
            System.out.println(result.toString());
            // {"VERSION":1,"state":"some state","STATUS":"F"}
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private static JSONObject unsign(final String signedValue, final String key)
            throws NoSuchAlgorithmException, InvalidKeyException, ParseException {
        if (!signedValue.contains("."))
            return null;

        String[] data = signedValue.split("\\.", 2);
        Mac mac = Mac.getInstance("HmacSHA256");
        mac.init(new SecretKeySpec(key.getBytes(), "HmacSHA256"));
        byte[] hash = mac.doFinal(data[0].getBytes());

        if (!Arrays.equals(hash, Base64.decodeBase64(data[1])))
            return null;

        JSONParser parser = new JSONParser();
        return (JSONObject) parser.parse(new String(Base64.decodeBase64(data[0])));
    }
}

Methods Supporting Signing



User Comments

User comments are not part of official documentation. Use information provided by other users in the comments at your own risk.

The User Comments section is not to be used as a feature discussion board. Only registered users can post comments. Your comment will be visible once it has been approved by the moderator.
© «Bitrix Inc.», 2001-2021, «Bitrix Inc.», 2021