Skip to main content

Security considerations

Security remains the top priority when it comes to online financial transactions.

Integration

To ensure that exchanges between the merchant and HUB2 remain confidential, and that only the merchant is at the origin of the transactions (and therefore of the amount and other properties of these transactions), the integration of the API must only take place on the server side. Sensitive data, such as API keys, must never be disclosed, whether on a website or in a mobile application. This approach strengthens security by avoiding direct exposure of API keys and sensitive information on the client side. By performing transactions on the server side, the risk of malicious attacks, such as client-side data manipulation, is considerably reduced.

CORS

To protect against potential client-side integrations, the HUB2 API is set up so that it can never be loaded from the end-client’s browser. In other words, the HTTP headers for cross origin resource sharing (CORS) have been correctly set.

Signature

To enhance the security and integrity of API interactions, an optional payload signature mechanism is available. This feature enables merchants to cryptographically sign the JSON request body sent to the API. By verifying this signature, it can be ensured that the data has not been tampered with in transit and originates from a trusted source. This added layer of security is particularly valuable for sensitive financial transactions. Check out the integration page to understand how to use out.

Platform security mechanisms

To protect the HUB2 platform, several security components are implemented at different levels of the infrastructure. At the perimeter, a Web Application Firewall (WAF) analyzes every single HTTP request before forwarding it to the backend. Its primary role is to detect and block malicious HTTP requests before they can reach the application layer. For rejected requests, clients will face the following behavior:
HUB2-WAF-HTTP-403-Forbidden

WAF behavior

Here, an SQL injection (SQLi attack) is simulated on the HUB2 API.
The web application firewall immediately rejects the request and returns a response with the status HTTP 403 Forbidden.
Note: The will always return a text/html response Body.
Please keep this in mind when integrating the HUB2 API.

Troubleshooting HTTP 403 Forbidden common errors

If a legitimate request faces an HTTP 403 Forbidden, please first follow the below guidelines to ensure your integration complies with our security standards.
If your request is still rejected after applying the below guidelines, feel free to file a ticket with the HUB2 Support team.

1. GET HTTP request with Body content

  • GET requests must NOT include any Body content. All non-compliant requests will be rejected by the HUB2 security gateway.
  • The Content-Length header is not mandatory for a GET request. However, if included, it must be set to 0 and the body must remain completely empty.
For example:

Bad practice

Notice the Content-Type & Content-Length headers sent, along with the Body content for a GET request. Such requests will be rejected.
GET /webhooks/ HTTP/1.1
Host: api.hub2.io
Accept: application/json
User-Agent: Example Merchant backend/1.0
Content-Type: application/json <------------ Here
Content-Length: 184 <----------------------- Here

{"url":"https://my.webhook.target","events":["payment.created","payment_intent.created"],"description":"This is a webhook trigger upon payment & payment_intent creation","metadata":{}}
^----------- Here
See the Good practice below to fix this.
Apply the following recommendations to avoid triggering the security gateway:
  1. Remove the Body content entirely.
  2. Remove the Content-Type & Content-Length headers (Content-Length is generally computed automatically by your HTTP client library, so there is usually nothing extra to configure).
GET /webhooks/ HTTP/1.1
Host: api.hub2.io
Accept: application/json
User-Agent: Example Merchant backend/1.0

2. Non-stringified JSON body

All HTTP POST requests using application/json must be stringified as a single line of text. Multi-line JSON payloads will be rejected by the HUB2 security gateway. Formatting the JSON string with carriage returns (CR/LF) or spaces (except inside the values themselves) can trigger filtering rules. For example:

Bad practice

Notice the multi-line JSON object in the Body content below:
POST /webhooks/ HTTP/1.1
Host: api.hub2.io
Accept: application/json
User-Agent: Example Merchant backend/1.0
Content-Type: application/json
Content-Length: 214

v----------- Here
{
    "url": "https://my.webhook.target",
    "events": [
        "payment.created",
        "payment_intent.created"
    ],
    "description": "This is a webhook trigger upon payment & payment_intent creation",
    "metadata": {}
}
See the Good practice below to fix this.
To avoid triggering the security gateway, transform the multi-line Body content into a single line of text, as follows:
POST /webhooks/ HTTP/1.1
Host: api.hub2.io
Accept: application/json
User-Agent: Example Merchant backend/1.0
Content-Type: application/json
Content-Length: 184

{"url":"https://my.webhook.target","events":["payment.created","payment_intent.created"],"description":"This is a webhook trigger upon payment & payment_intent creation","metadata":{}}

3. Body content differs from the Content-Type header

The HUB2 API currently only accepts JSON payloads. If an HTTP request is sent with valid JSON content, but the Content-Type header is set to a different value (e.g., text/plain, text/xml, or application/x-www-form-urlencoded), the security gateway will parse it incorrectly and block the request. For example:

Bad practice

Notice the incorrect Content-Type header given the JSON payload:
POST /webhooks/ HTTP/1.1
Host: api.hub2.io
Accept: application/json
User-Agent: Example Merchant backend/1.0
Content-Type: application/x-www-form-urlencoded <------------ Here
Content-Length: 184

{"url":"https://my.webhook.target","events":["payment.created","payment_intent.created"],"description":"This is a webhook trigger upon payment & payment_intent creation","metadata":{}}
See the Good practice below to fix this.
Always set the Content-Type header to match the Body content format:
POST /webhooks/ HTTP/1.1
Host: api.hub2.io
Accept: application/json
User-Agent: Example Merchant backend/1.0
Content-Type: application/json <------------ Here
Content-Length: 184

{"url":"https://my.webhook.target","events":["payment.created","payment_intent.created"],"description":"This is a webhook trigger upon payment & payment_intent creation","metadata":{}}